From 4a79df860d76f9c55e13ed7a22fd6ac7233268a7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Dec 2020 15:48:42 +0100 Subject: [PATCH 001/419] Refactor NavMesh into NavMeshRuntime and make it internal --- Source/Engine/Navigation/NavLink.cpp | 1 - Source/Engine/Navigation/NavMeshBuilder.cpp | 36 ++++++++--------- .../{NavMesh.cpp => NavMeshRuntime.cpp} | 40 +++++++++---------- .../{NavMesh.h => NavMeshRuntime.h} | 15 ++++--- Source/Engine/Navigation/Navigation.cpp | 22 +++++----- Source/Engine/Navigation/Navigation.h | 9 ----- Source/Engine/Navigation/NavigationScene.cpp | 10 ++--- 7 files changed, 65 insertions(+), 68 deletions(-) rename Source/Engine/Navigation/{NavMesh.cpp => NavMeshRuntime.cpp} (86%) rename Source/Engine/Navigation/{NavMesh.h => NavMeshRuntime.h} (91%) diff --git a/Source/Engine/Navigation/NavLink.cpp b/Source/Engine/Navigation/NavLink.cpp index 2fb6bc368..d23018646 100644 --- a/Source/Engine/Navigation/NavLink.cpp +++ b/Source/Engine/Navigation/NavLink.cpp @@ -14,7 +14,6 @@ NavLink::NavLink(const SpawnParams& params) void NavLink::UpdateBounds() { - // Cache bounds const auto start = _transform.LocalToWorld(Start); const auto end = _transform.LocalToWorld(End); BoundingBox::FromPoints(start, end, _box); diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 1765d78c7..c0ce014ee 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -18,9 +18,9 @@ #include "NavigationScene.h" #include "NavigationSettings.h" #include "NavMeshBoundsVolume.h" -#include "NavMesh.h" #include "NavLink.h" #include "Navigation.h" +#include "NavMeshRuntime.h" #include #include #include @@ -184,9 +184,9 @@ void RasterizeGeometry(const BoundingBox& tileBounds, rcContext* context, rcConf bool GetNavMeshTileBounds(NavigationScene* scene, int32 x, int32 y, float tileSize, BoundingBox& tileBounds) { // Build initial tile bounds (with infinite extent) - tileBounds.Minimum.X = x * tileSize; + tileBounds.Minimum.X = (float)x * tileSize; tileBounds.Minimum.Y = -NAV_MESH_TILE_MAX_EXTENT; - tileBounds.Minimum.Z = y * tileSize; + tileBounds.Minimum.Z = (float)y * tileSize; tileBounds.Maximum.X = tileBounds.Minimum.X + tileSize; tileBounds.Maximum.Y = NAV_MESH_TILE_MAX_EXTENT; tileBounds.Maximum.Z = tileBounds.Minimum.Z + tileSize; @@ -224,7 +224,7 @@ bool GetNavMeshTileBounds(NavigationScene* scene, int32 x, int32 y, float tileSi return foundAnyVolume; } -void RemoveTile(NavMesh* navMesh, NavigationScene* scene, int32 x, int32 y, int32 layer) +void RemoveTile(NavMeshRuntime* navMesh, NavigationScene* scene, int32 x, int32 y, int32 layer) { ScopeLock lock(navMesh->Locker); @@ -250,7 +250,7 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou int32 layer = 0; // Expand tile bounds by a certain margin - const float tileBorderSize = (1 + config.borderSize) * config.cs; + const float tileBorderSize = (1.0f + (float)config.borderSize) * config.cs; tileBounds.Minimum -= tileBorderSize; tileBounds.Maximum += tileBorderSize; @@ -347,7 +347,7 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou rcFreeCompactHeightfield(compactHeightfield); rcFreeContourSet(contourSet); - for (int i = 0; i < polyMesh->npolys; ++i) + for (int i = 0; i < polyMesh->npolys; i++) { polyMesh->flags[i] = polyMesh->areas[i] == RC_WALKABLE_AREA ? 1 : 0; } @@ -355,7 +355,7 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou if (polyMesh->nverts == 0) { // Empty tile - RemoveTile(Navigation::GetNavMesh(), scene, x, y, layer); + RemoveTile(NavMeshRuntime::Get(), scene, x, y, layer); return false; } @@ -373,9 +373,9 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou params.detailVertsCount = detailMesh->nverts; params.detailTris = detailMesh->tris; params.detailTriCount = detailMesh->ntris; - params.walkableHeight = config.walkableHeight * config.ch; - params.walkableRadius = config.walkableRadius * config.cs; - params.walkableClimb = config.walkableClimb * config.ch; + params.walkableHeight = (float)config.walkableHeight * config.ch; + params.walkableRadius = (float)config.walkableRadius * config.cs; + params.walkableClimb = (float)config.walkableClimb * config.ch; params.tileX = x; params.tileY = y; params.tileLayer = layer; @@ -438,7 +438,7 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou { PROFILE_CPU_NAMED("Navigation.CreateTile"); - ScopeLock lock(Navigation::GetNavMesh()->Locker); + ScopeLock lock(NavMeshRuntime::Get()->Locker); // Add tile data scene->IsDataDirty = true; @@ -451,7 +451,7 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou tile.Data.Copy(navData, navDataSize); // Add tile to navmesh - Navigation::GetNavMesh()->AddTile(scene, tile); + NavMeshRuntime::Get()->AddTile(scene, tile); } dtFree(navData); @@ -635,7 +635,7 @@ void BuildTileAsync(NavigationScene* scene, int32 x, int32 y, rcConfig& config, void BuildWholeScene(NavigationScene* scene) { const float tileSize = GetTileSize(); - const auto navMesh = Navigation::GetNavMesh(); + const auto navMesh = NavMeshRuntime::Get(); // Compute total navigation area bounds const BoundingBox worldBounds = scene->GetNavigationBounds(); @@ -674,9 +674,9 @@ void BuildWholeScene(NavigationScene* scene) { PROFILE_CPU_NAMED("StartBuildingTiles"); - for (int32 y = tilesMin.Z; y < tilesMax.Z; y ++) + for (int32 y = tilesMin.Z; y < tilesMax.Z; y++) { - for (int32 x = tilesMin.X; x < tilesMax.X; x ++) + for (int32 x = tilesMin.X; x < tilesMax.X; x++) { BoundingBox tileBounds; if (GetNavMeshTileBounds(scene, x, y, tileSize, tileBounds)) @@ -695,7 +695,7 @@ void BuildWholeScene(NavigationScene* scene) void BuildDirtyBounds(NavigationScene* scene, const BoundingBox& dirtyBounds) { const float tileSize = GetTileSize(); - const auto navMesh = Navigation::GetNavMesh(); + const auto navMesh = NavMeshRuntime::Get(); // Align dirty bounds to tile size BoundingBox dirtyBoundsAligned; @@ -739,9 +739,9 @@ void BuildDirtyBounds(NavigationScene* scene, const BoundingBox& dirtyBounds) { PROFILE_CPU_NAMED("StartBuildingTiles"); - for (int32 y = tilesMin.Z; y < tilesMax.Z; y ++) + for (int32 y = tilesMin.Z; y < tilesMax.Z; y++) { - for (int32 x = tilesMin.X; x < tilesMax.X; x ++) + for (int32 x = tilesMin.X; x < tilesMax.X; x++) { BoundingBox tileBounds; if (GetNavMeshTileBounds(scene, x, y, tileSize, tileBounds)) diff --git a/Source/Engine/Navigation/NavMesh.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp similarity index 86% rename from Source/Engine/Navigation/NavMesh.cpp rename to Source/Engine/Navigation/NavMeshRuntime.cpp index 347480af1..7a1c4d3c4 100644 --- a/Source/Engine/Navigation/NavMesh.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -1,8 +1,8 @@ // Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. -#include "NavMesh.h" -#include "Engine/Core/Log.h" +#include "NavMeshRuntime.h" #include "NavigationScene.h" +#include "Engine/Core/Log.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Threading/Threading.h" #include @@ -12,25 +12,25 @@ #define USE_DATA_LINK 0 #define USE_NAV_MESH_ALLOC 1 -NavMesh::NavMesh() +NavMeshRuntime::NavMeshRuntime() { _navMesh = nullptr; _navMeshQuery = dtAllocNavMeshQuery(); _tileSize = 0; } -NavMesh::~NavMesh() +NavMeshRuntime::~NavMeshRuntime() { dtFreeNavMesh(_navMesh); dtFreeNavMeshQuery(_navMeshQuery); } -int32 NavMesh::GetTilesCapacity() const +int32 NavMeshRuntime::GetTilesCapacity() const { return _navMesh ? _navMesh->getMaxTiles() : 0; } -void NavMesh::SetTileSize(float tileSize) +void NavMeshRuntime::SetTileSize(float tileSize) { ScopeLock lock(Locker); @@ -49,7 +49,7 @@ void NavMesh::SetTileSize(float tileSize) _tileSize = tileSize; } -void NavMesh::EnsureCapacity(int32 tilesToAddCount) +void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) { ScopeLock lock(Locker); @@ -58,7 +58,7 @@ void NavMesh::EnsureCapacity(int32 tilesToAddCount) if (newTilesCount <= capacity) return; - PROFILE_CPU_NAMED("NavMesh.EnsureCapacity"); + PROFILE_CPU_NAMED("NavMeshRuntime.EnsureCapacity"); // Navmesh tiles capacity growing rule int32 newCapacity = 0; @@ -132,7 +132,7 @@ void NavMesh::EnsureCapacity(int32 tilesToAddCount) } } -void NavMesh::AddTiles(NavigationScene* scene) +void NavMeshRuntime::AddTiles(NavigationScene* scene) { // Skip if no data ASSERT(scene); @@ -140,7 +140,7 @@ void NavMesh::AddTiles(NavigationScene* scene) return; auto& data = scene->Data; - PROFILE_CPU_NAMED("NavMesh.AddTiles"); + PROFILE_CPU_NAMED("NavMeshRuntime.AddTiles"); ScopeLock lock(Locker); @@ -168,12 +168,12 @@ void NavMesh::AddTiles(NavigationScene* scene) } } -void NavMesh::AddTile(NavigationScene* scene, NavMeshTileData& tileData) +void NavMeshRuntime::AddTile(NavigationScene* scene, NavMeshTileData& tileData) { ASSERT(scene); auto& data = scene->Data; - PROFILE_CPU_NAMED("NavMesh.AddTile"); + PROFILE_CPU_NAMED("NavMeshRuntime.AddTile"); ScopeLock lock(Locker); @@ -198,17 +198,17 @@ void NavMesh::AddTile(NavigationScene* scene, NavMeshTileData& tileData) AddTileInternal(scene, tileData); } -bool IsTileFromScene(const NavMesh* navMesh, const NavMeshTile& tile, void* customData) +bool IsTileFromScene(const NavMeshRuntime* navMesh, const NavMeshTile& tile, void* customData) { return tile.Scene == (NavigationScene*)customData; } -void NavMesh::RemoveTiles(NavigationScene* scene) +void NavMeshRuntime::RemoveTiles(NavigationScene* scene) { RemoveTiles(IsTileFromScene, scene); } -void NavMesh::RemoveTile(int32 x, int32 y, int32 layer) +void NavMeshRuntime::RemoveTile(int32 x, int32 y, int32 layer) { ScopeLock lock(Locker); @@ -216,7 +216,7 @@ void NavMesh::RemoveTile(int32 x, int32 y, int32 layer) if (!_navMesh) return; - PROFILE_CPU_NAMED("NavMesh.RemoveTile"); + PROFILE_CPU_NAMED("NavMeshRuntime.RemoveTile"); const auto tileRef = _navMesh->getTileRefAt(x, y, layer); if (tileRef == 0) @@ -240,7 +240,7 @@ void NavMesh::RemoveTile(int32 x, int32 y, int32 layer) } } -void NavMesh::RemoveTiles(bool (* prediction)(const NavMesh* navMesh, const NavMeshTile& tile, void* customData), void* userData) +void NavMeshRuntime::RemoveTiles(bool (* prediction)(const NavMeshRuntime* navMesh, const NavMeshTile& tile, void* customData), void* userData) { ScopeLock lock(Locker); @@ -249,7 +249,7 @@ void NavMesh::RemoveTiles(bool (* prediction)(const NavMesh* navMesh, const NavM if (!_navMesh) return; - PROFILE_CPU_NAMED("NavMesh.RemoveTiles"); + PROFILE_CPU_NAMED("NavMeshRuntime.RemoveTiles"); for (int32 i = 0; i < _tiles.Count(); i++) { @@ -274,7 +274,7 @@ void NavMesh::RemoveTiles(bool (* prediction)(const NavMesh* navMesh, const NavM } } -void NavMesh::Dispose() +void NavMeshRuntime::Dispose() { if (_navMesh) { @@ -284,7 +284,7 @@ void NavMesh::Dispose() _tiles.Resize(0); } -void NavMesh::AddTileInternal(NavigationScene* scene, NavMeshTileData& tileData) +void NavMeshRuntime::AddTileInternal(NavigationScene* scene, NavMeshTileData& tileData) { // Check if that tile has been added to navmesh NavMeshTile* tile = nullptr; diff --git a/Source/Engine/Navigation/NavMesh.h b/Source/Engine/Navigation/NavMeshRuntime.h similarity index 91% rename from Source/Engine/Navigation/NavMesh.h rename to Source/Engine/Navigation/NavMeshRuntime.h index f4364331e..0e073cd81 100644 --- a/Source/Engine/Navigation/NavMesh.h +++ b/Source/Engine/Navigation/NavMeshRuntime.h @@ -21,8 +21,12 @@ public: BytesContainer Data; }; -class FLAXENGINE_API NavMesh +class NavMeshRuntime { +public: + + static NavMeshRuntime* Get(); + private: dtNavMesh* _navMesh; @@ -32,14 +36,13 @@ private: public: - NavMesh(); - - ~NavMesh(); + NavMeshRuntime(); + ~NavMeshRuntime(); public: /// - /// The NavMesh object locker. + /// The object locker. /// CriticalSection Locker; @@ -110,7 +113,7 @@ public: /// /// The prediction callback, returns true for tiles to remove and false for tiles to preserve. /// The user data passed to the callback method. - void RemoveTiles(bool (*prediction)(const NavMesh* navMesh, const NavMeshTile& tile, void* customData), void* userData); + void RemoveTiles(bool (*prediction)(const NavMeshRuntime* navMesh, const NavMeshTile& tile, void* customData), void* userData); /// /// Releases the navmesh. diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 5f6ed573f..c7d727557 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -1,11 +1,12 @@ // Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. #include "Navigation.h" +#include "NavMeshRuntime.h" +#include "NavMeshBuilder.h" #include "Engine/Threading/Threading.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Engine/EngineService.h" -#include "NavMeshBuilder.h" -#include "NavMesh.h" +#include "Engine/Profiler/ProfilerCPU.h" #include #include #include @@ -13,7 +14,15 @@ #define DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL 50.0f #define DEFAULT_NAV_QUERY_EXTENT_VERTICAL 250.0f -NavMesh* _navMesh = nullptr; +namespace +{ + NavMeshRuntime* _navMesh; +} + +NavMeshRuntime* NavMeshRuntime::Get() +{ + return ::_navMesh; +} class NavigationService : public EngineService { @@ -36,11 +45,6 @@ public: NavigationService NavigationServiceInstance; -NavMesh* Navigation::GetNavMesh() -{ - return _navMesh; -} - void* dtAllocDefault(size_t size, dtAllocHint) { return Allocator::Allocate(size); @@ -58,7 +62,7 @@ bool NavigationService::Init() rcAllocSetCustom(rcAllocDefault, Allocator::Free); // Create global nav mesh - _navMesh = New(); + _navMesh = New(); return false; } diff --git a/Source/Engine/Navigation/Navigation.h b/Source/Engine/Navigation/Navigation.h index d1fad8083..29d21cffb 100644 --- a/Source/Engine/Navigation/Navigation.h +++ b/Source/Engine/Navigation/Navigation.h @@ -6,7 +6,6 @@ #include "Engine/Core/Math/Vector3.h" class Scene; -class NavMesh; #define NAV_MESH_PATH_MAX_SIZE 200 @@ -39,14 +38,6 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshHit); API_CLASS(Static) class FLAXENGINE_API Navigation { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Navigation); -public: - - /// - /// Gets the navigation mesh (read-only). Use the other API to request data to use safe access to the navigation system. - /// - /// The navigation mesh. - static NavMesh* GetNavMesh(); - public: /// diff --git a/Source/Engine/Navigation/NavigationScene.cpp b/Source/Engine/Navigation/NavigationScene.cpp index 03c87f852..397de0d31 100644 --- a/Source/Engine/Navigation/NavigationScene.cpp +++ b/Source/Engine/Navigation/NavigationScene.cpp @@ -1,8 +1,8 @@ // Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. #include "NavigationScene.h" -#include "NavMesh.h" #include "Navigation.h" +#include "NavMeshRuntime.h" #include "NavMeshBoundsVolume.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Level/Scene/Scene.h" @@ -101,14 +101,14 @@ void NavigationScene::SaveNavMesh() void NavigationScene::OnEnable() { - auto navMesh = Navigation::GetNavMesh(); - if (navMesh) - navMesh->AddTiles(this); + auto navMesh = NavMeshRuntime::Get(); + CHECK(navMesh); + navMesh->AddTiles(this); } void NavigationScene::OnDisable() { - auto navMesh = Navigation::GetNavMesh(); + auto navMesh = NavMeshRuntime::Get(); if (navMesh) navMesh->RemoveTiles(this); } From 4c0c7cc3940e33d71a0beea5fbd1088b23166d6e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Dec 2020 17:15:29 +0100 Subject: [PATCH 002/419] Add NavAgentProperties and NavigationTypes.h --- Source/Engine/Navigation/Navigation.h | 26 +--------- Source/Engine/Navigation/NavigationTypes.h | 57 ++++++++++++++++++++++ 2 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 Source/Engine/Navigation/NavigationTypes.h diff --git a/Source/Engine/Navigation/Navigation.h b/Source/Engine/Navigation/Navigation.h index 29d21cffb..140f32f77 100644 --- a/Source/Engine/Navigation/Navigation.h +++ b/Source/Engine/Navigation/Navigation.h @@ -2,36 +2,12 @@ #pragma once -#include "Engine/Scripting/ScriptingType.h" -#include "Engine/Core/Math/Vector3.h" +#include "NavigationTypes.h" class Scene; #define NAV_MESH_PATH_MAX_SIZE 200 -/// -/// The result information for navigation mesh queries. -/// -API_STRUCT() struct NavMeshHit -{ -DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshHit); - - /// - /// The hit point position. - /// - API_FIELD() Vector3 Position; - - /// - /// The distance to hit point (from the query origin). - /// - API_FIELD() float Distance; - - /// - /// The hit point normal vector. - /// - API_FIELD() Vector3 Normal; -}; - /// /// The navigation service used for path finding and agents navigation system. /// diff --git a/Source/Engine/Navigation/NavigationTypes.h b/Source/Engine/Navigation/NavigationTypes.h new file mode 100644 index 000000000..e06794a90 --- /dev/null +++ b/Source/Engine/Navigation/NavigationTypes.h @@ -0,0 +1,57 @@ +// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Scripting/ScriptingType.h" +#include "Engine/Core/Math/Vector3.h" + +/// +/// The navigation system agent properties container for navmesh building and querying. +/// +API_STRUCT() struct NavAgentProperties +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(NavAgentProperties); + + /// + /// The radius of the agent used for navigation. Agents can't pass through gaps of less than twice the radius. + /// + API_FIELD() float Radius = 34.0f; + + /// + /// The height of the agent used for navigation. Agents can't enter areas with ceilings lower than this value. + /// + API_FIELD() float Height = 144.0f; + + /// + /// The step height used for navigation. Defines the maximum ledge height that is considered to still be traversable by the agent. + /// + API_FIELD() float StepHeight = 35.0f; + + /// + /// The maximum slope (in degrees) that is considered walkable for navigation. Agents can't go up or down slopes higher than this value. + /// + API_FIELD() float MaxSlopeAngle = 60.0f; +}; + +/// +/// The result information for navigation mesh queries. +/// +API_STRUCT() struct NavMeshHit +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshHit); + + /// + /// The hit point position. + /// + API_FIELD() Vector3 Position; + + /// + /// The distance to hit point (from the query origin). + /// + API_FIELD() float Distance; + + /// + /// The hit point normal vector. + /// + API_FIELD() Vector3 Normal; +}; From a36a54adcdf1d2f8fe520c7656011ea4b6cd0927 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Dec 2020 17:43:46 +0100 Subject: [PATCH 003/419] Tweaks --- Source/Engine/Navigation/NavMeshRuntime.h | 1 - Source/Engine/Navigation/NavigationTypes.h | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshRuntime.h b/Source/Engine/Navigation/NavMeshRuntime.h index 0e073cd81..9c5df539c 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.h +++ b/Source/Engine/Navigation/NavMeshRuntime.h @@ -49,7 +49,6 @@ public: /// /// Gets the size of the tile (in world-units). Returns zero if not initialized yet. /// - /// The tile size. FORCE_INLINE float GetTileSize() const { return _tileSize; diff --git a/Source/Engine/Navigation/NavigationTypes.h b/Source/Engine/Navigation/NavigationTypes.h index e06794a90..e786567dd 100644 --- a/Source/Engine/Navigation/NavigationTypes.h +++ b/Source/Engine/Navigation/NavigationTypes.h @@ -4,11 +4,12 @@ #include "Engine/Scripting/ScriptingType.h" #include "Engine/Core/Math/Vector3.h" +#include "FlaxEngine.Gen.h" /// /// The navigation system agent properties container for navmesh building and querying. /// -API_STRUCT() struct NavAgentProperties +API_STRUCT() struct FLAXENGINE_API NavAgentProperties { DECLARE_SCRIPTING_TYPE_MINIMAL(NavAgentProperties); @@ -36,7 +37,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(NavAgentProperties); /// /// The result information for navigation mesh queries. /// -API_STRUCT() struct NavMeshHit +API_STRUCT() struct FLAXENGINE_API NavMeshHit { DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshHit); From 93c35a19f512b55792da89d61e8cbbcb306429ee Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 16 Dec 2020 10:24:18 +0100 Subject: [PATCH 004/419] Add NavMeshProperties --- Source/Engine/Navigation/NavigationTypes.h | 32 +++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Navigation/NavigationTypes.h b/Source/Engine/Navigation/NavigationTypes.h index e786567dd..a5672f342 100644 --- a/Source/Engine/Navigation/NavigationTypes.h +++ b/Source/Engine/Navigation/NavigationTypes.h @@ -3,8 +3,10 @@ #pragma once #include "Engine/Scripting/ScriptingType.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Math/Color.h" #include "Engine/Core/Math/Vector3.h" -#include "FlaxEngine.Gen.h" +#include "Engine/Core/Math/Quaternion.h" /// /// The navigation system agent properties container for navmesh building and querying. @@ -34,6 +36,34 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(NavAgentProperties); API_FIELD() float MaxSlopeAngle = 60.0f; }; +/// +/// The navigation mesh properties container for navmesh building. +/// +API_STRUCT() struct FLAXENGINE_API NavMeshProperties +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshProperties); + + /// + /// The navmesh type name (for debugging). + /// + API_FIELD() String Name; + + /// + /// The navmesh type color (for debugging). + /// + API_FIELD() Color Color; + + /// + /// The navmesh rotation applied to navigation surface. Used during building to the rotate scene geometry and to revert back result during path finding queries. Can be used to generate navmesh on walls. + /// + API_FIELD() Quaternion Rotation; + + /// + /// The properties of the agent used to generate walkable navigation surface. + /// + API_FIELD() NavAgentProperties Agent; +}; + /// /// The result information for navigation mesh queries. /// From 0e6ead938edd5bfc2f491d897066dff631401a7e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Dec 2020 23:09:09 +0100 Subject: [PATCH 005/419] Add support for API class not using ScriptingObject as a base --- .../Bindings/BindingsGenerator.CSharp.cs | 29 +++++++--- .../Bindings/BindingsGenerator.Cpp.cs | 56 +++++++++++-------- 2 files changed, 56 insertions(+), 29 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index d51d7b754..b53c147c2 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -431,6 +431,7 @@ namespace Flax.Build.Bindings private static void GenerateCSharpClass(BuildData buildData, StringBuilder contents, string indent, ClassInfo classInfo) { + var useUnmanaged = classInfo.IsStatic || classInfo.IsScriptingObject; contents.AppendLine(); // Namespace begin @@ -446,7 +447,7 @@ namespace Flax.Build.Bindings GenerateCSharpComment(contents, indent, classInfo.Comment); // Class begin - GenerateCSharpAttributes(buildData, contents, indent, classInfo, true); + GenerateCSharpAttributes(buildData, contents, indent, classInfo, useUnmanaged); contents.Append(indent); if (classInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -482,6 +483,9 @@ namespace Flax.Build.Bindings // Events foreach (var eventInfo in classInfo.Events) { + if (!useUnmanaged) + throw new NotImplementedException("TODO: support events inside non-static and non-scripting API class types."); + contents.AppendLine(); foreach (var comment in eventInfo.Comment) @@ -494,7 +498,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); } - GenerateCSharpAttributes(buildData, contents, indent, eventInfo, true); + GenerateCSharpAttributes(buildData, contents, indent, eventInfo, useUnmanaged); contents.Append(indent); if (eventInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -593,7 +597,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); } - GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, true); + GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, useUnmanaged); contents.Append(indent); if (fieldInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -604,8 +608,13 @@ namespace Flax.Build.Bindings if (fieldInfo.IsStatic) contents.Append("static "); var returnValueType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, classInfo); - contents.Append(returnValueType).Append(' ').AppendLine(fieldInfo.Name); - contents.AppendLine(indent + "{"); + contents.Append(returnValueType).Append(' ').Append(fieldInfo.Name); + if (!useUnmanaged) + { + contents.AppendLine(";"); + continue; + } + contents.AppendLine().AppendLine(indent + "{"); indent += " "; contents.Append(indent).Append("get { "); @@ -615,7 +624,7 @@ namespace Flax.Build.Bindings if (!fieldInfo.IsReadOnly) { contents.Append(indent).Append("set { "); - GenerateCSharpWrapperFunctionCall(buildData, contents, classInfo, fieldInfo.Setter, true); + GenerateCSharpWrapperFunctionCall(buildData, contents, classInfo, fieldInfo.Setter, useUnmanaged); contents.Append(" }").AppendLine(); } @@ -630,6 +639,9 @@ namespace Flax.Build.Bindings // Properties foreach (var propertyInfo in classInfo.Properties) { + if (!useUnmanaged) + throw new NotImplementedException("TODO: support properties inside non-static and non-scripting API class types."); + contents.AppendLine(); foreach (var comment in propertyInfo.Comment) @@ -646,7 +658,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); } - GenerateCSharpAttributes(buildData, contents, indent, propertyInfo, true); + GenerateCSharpAttributes(buildData, contents, indent, propertyInfo, useUnmanaged); contents.Append(indent); if (propertyInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -692,6 +704,9 @@ namespace Flax.Build.Bindings // Functions foreach (var functionInfo in classInfo.Functions) { + if (!useUnmanaged) + throw new Exception($"Not supported function {functionInfo.Name} inside non-static and non-scripting class type {classInfo.Name}."); + if (!functionInfo.NoProxy) { contents.AppendLine(); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 393cb9ea5..f9a695b77 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1044,6 +1044,7 @@ namespace Flax.Build.Bindings var classTypeNameInternal = classInfo.NativeName; if (classInfo.Parent != null && !(classInfo.Parent is FileInfo)) classTypeNameInternal = classInfo.Parent.FullNameNative + '_' + classTypeNameInternal; + var useUnmanaged = classInfo.IsStatic || classInfo.IsScriptingObject; if (classInfo.IsAutoSerialization) GenerateCppAutoSerialization(buildData, contents, moduleInfo, classInfo, classTypeNameNative); @@ -1056,6 +1057,8 @@ namespace Flax.Build.Bindings // Events foreach (var eventInfo in classInfo.Events) { + if (!useUnmanaged) + continue; CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h"); // C# event invoking wrapper (calls C# event from C++ delegate) @@ -1126,6 +1129,8 @@ namespace Flax.Build.Bindings // Fields foreach (var fieldInfo in classInfo.Fields) { + if (!useUnmanaged) + continue; if (fieldInfo.Getter != null) GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Getter, "{0}"); if (fieldInfo.Setter != null) @@ -1135,6 +1140,8 @@ namespace Flax.Build.Bindings // Properties foreach (var propertyInfo in classInfo.Properties) { + if (!useUnmanaged) + continue; if (propertyInfo.Getter != null) GenerateCppWrapperFunction(buildData, contents, classInfo, propertyInfo.Getter); if (propertyInfo.Setter != null) @@ -1144,6 +1151,8 @@ namespace Flax.Build.Bindings // Functions foreach (var functionInfo in classInfo.Functions) { + if (!useUnmanaged) + throw new Exception($"Not supported function {functionInfo.Name} inside non-static and non-scripting class type {classInfo.Name}."); GenerateCppWrapperFunction(buildData, contents, classInfo, functionInfo); } @@ -1253,27 +1262,30 @@ namespace Flax.Build.Bindings // Runtime initialization (internal methods binding) contents.AppendLine(" static void InitRuntime()"); contents.AppendLine(" {"); - foreach (var eventInfo in classInfo.Events) + if (useUnmanaged) { - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);"); - } - foreach (var fieldInfo in classInfo.Fields) - { - if (fieldInfo.Getter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});"); - if (fieldInfo.Setter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});"); - } - foreach (var propertyInfo in classInfo.Properties) - { - if (propertyInfo.Getter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Getter.UniqueName}\", &{propertyInfo.Getter.UniqueName});"); - if (propertyInfo.Setter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Setter.UniqueName}\", &{propertyInfo.Setter.UniqueName});"); - } - foreach (var functionInfo in classInfo.Functions) - { - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});"); + foreach (var eventInfo in classInfo.Events) + { + contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);"); + } + foreach (var fieldInfo in classInfo.Fields) + { + if (fieldInfo.Getter != null) + contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});"); + if (fieldInfo.Setter != null) + contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});"); + } + foreach (var propertyInfo in classInfo.Properties) + { + if (propertyInfo.Getter != null) + contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Getter.UniqueName}\", &{propertyInfo.Getter.UniqueName});"); + if (propertyInfo.Setter != null) + contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Setter.UniqueName}\", &{propertyInfo.Setter.UniqueName});"); + } + foreach (var functionInfo in classInfo.Functions) + { + contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});"); + } } GenerateCppClassInitRuntime?.Invoke(buildData, classInfo, contents); @@ -1288,11 +1300,11 @@ namespace Flax.Build.Bindings contents.Append($"StringAnsiView(\"{classTypeNameManaged}\", {classTypeNameManaged.Length}), "); contents.Append($"sizeof({classTypeNameNative}), "); contents.Append($"&{classTypeNameInternal}Internal::InitRuntime, "); - if (!classInfo.IsStatic && !classInfo.NoSpawn) + if (!classInfo.IsStatic && !classInfo.NoSpawn && useUnmanaged) contents.Append($"(ScriptingType::SpawnHandler)&{classTypeNameNative}::Spawn, "); else contents.Append("&ScriptingType::DefaultSpawn, "); - if (classInfo.BaseType != null) + if (classInfo.BaseType != null && useUnmanaged) contents.Append($"&{classInfo.BaseType}::TypeInitializer, "); else contents.Append("nullptr, "); From 3a314d97eb456624329420f58773938ef11d902e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 21 Dec 2020 13:29:52 +0100 Subject: [PATCH 006/419] Add ScriptingTypeHandle debugger view to flax.natvis --- Source/flax.natvis | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/flax.natvis b/Source/flax.natvis index fb3ef1228..da4ead650 100644 --- a/Source/flax.natvis +++ b/Source/flax.natvis @@ -206,4 +206,13 @@ + + + Null + Type={Module->Types._allocation._data[TypeIndex].Fullname} + + Module->Types._allocation._data[TypeIndex] + + + From 585c752d7296956e5c10ba611998f759c651edbd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 21 Dec 2020 14:03:15 +0100 Subject: [PATCH 007/419] Add more objects types checks and casting utilities to ScriptingObject --- Source/Engine/Scripting/ScriptingObject.cpp | 14 +++++++++++++ Source/Engine/Scripting/ScriptingObject.h | 22 +++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index a065d6a0a..d7e8c3729 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -72,6 +72,12 @@ ScriptingObject* ScriptingObject::ToNative(MonoObject* obj) return ptr; } +bool ScriptingObject::Is(const ScriptingTypeHandle& type) const +{ + CHECK_RETURN(type, false); + return _type == type || CanCast(GetClass(), type.GetType().ManagedClass); +} + void ScriptingObject::ChangeID(const Guid& newId) { ASSERT(newId.IsValid() && newId != _id); @@ -206,6 +212,14 @@ void ScriptingObject::UnregisterObject() Scripting::UnregisterObject(this); } +bool ScriptingObject::CanCast(const ScriptingTypeHandle& from, const ScriptingTypeHandle& to) +{ + if (!from && !to) + return true; + CHECK_RETURN(from && to, false); + return CanCast(from.GetType().ManagedClass, to.GetType().ManagedClass); +} + bool ScriptingObject::CanCast(MClass* from, MClass* to) { if (!from && !to) diff --git a/Source/Engine/Scripting/ScriptingObject.h b/Source/Engine/Scripting/ScriptingObject.h index 0f8b26375..540e0d60b 100644 --- a/Source/Engine/Scripting/ScriptingObject.h +++ b/Source/Engine/Scripting/ScriptingObject.h @@ -131,12 +131,30 @@ public: return obj ? obj->GetOrCreateManagedInstance() : nullptr; } + /// + /// Checks if can cast one scripting object type into another type. + /// + /// The object type for the cast. + /// The destination type to the cast. + /// True if can, otherwise false. + static bool CanCast(const ScriptingTypeHandle& from, const ScriptingTypeHandle& to); + + /// + /// Checks if can cast one scripting object type into another type. + /// + /// The object class for the cast. + /// The destination class to the cast. + /// True if can, otherwise false. + static bool CanCast(MClass* from, MClass* to); + template static T* Cast(ScriptingObject* obj) { return obj && CanCast(obj->GetClass(), T::GetStaticClass()) ? (T*)obj : nullptr; } + bool Is(const ScriptingTypeHandle& type) const; + bool Is(MClass* type) const { return CanCast(GetClass(), type); @@ -185,10 +203,6 @@ public: /// void UnregisterObject(); -private: - - static bool CanCast(MClass* from, MClass* to); - protected: /// From aa7711dc315bbe07366193e1ba914355e7a457c7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 21 Dec 2020 14:03:53 +0100 Subject: [PATCH 008/419] Fix loaded asset verification error for json assets that have the scripting type --- Source/Engine/Content/Content.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index 78d0ff1d6..8d43a7318 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -872,7 +872,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type) if (result) { // Validate type - if (IsAssetTypeIdInvalid(type, result->GetTypeHandle())) + if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type)) { LOG(Warning, "Different loaded asset type! Asset: \'{0}\'. Expected type: {1}", result->ToString(), type.ToString()); return nullptr; @@ -945,15 +945,6 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& return nullptr; } - // Validate type - const StringAsANSI<> typeNameStd(assetInfo.TypeName.Get(), assetInfo.TypeName.Length()); - const auto assetType = Scripting::FindScriptingType(StringAnsiView(typeNameStd.Get(), assetInfo.TypeName.Length())); - if (IsAssetTypeIdInvalid(type, assetType)) - { - LOG(Error, "Different loaded asset type! Asset: '{0}'. Expected type: {1}", assetInfo.ToString(), type.ToString()); - return nullptr; - } - #endif // Find asset factory based in its type @@ -972,6 +963,18 @@ Asset* Content::load(const Guid& id, const ScriptingTypeHandle& type, AssetInfo& return nullptr; } +#if ASSETS_LOADING_EXTRA_VERIFICATION + + // Validate type + if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type)) + { + LOG(Error, "Different loaded asset type! Asset: '{0}'. Expected type: {1}", assetInfo.ToString(), type.ToString()); + result->DeleteObject(); + return nullptr; + } + +#endif + // Register asset { AssetsLocker.Lock(); From cea1a6774910e82d9ae4f26859367d6f351bfba5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 21 Dec 2020 14:53:27 +0100 Subject: [PATCH 009/419] Refactor game settings to support using API bindings --- .gitmodules | 6 ++++++ Source/Engine/Audio/AudioSettings.h | 6 +++--- Source/Engine/Core/Config/BuildSettings.h | 5 ++--- Source/Engine/Core/Config/GameSettings.cpp | 2 +- Source/Engine/Core/Config/GraphicsSettings.h | 5 ++--- .../Engine/Core/Config/LayersTagsSettings.h | 5 ++--- Source/Engine/Core/Config/Settings.h | 20 +++++++++---------- Source/Engine/Core/Config/TimeSettings.h | 5 ++--- Source/Engine/Input/InputSettings.h | 6 +++--- Source/Engine/Navigation/NavigationSettings.h | 2 +- Source/Engine/Physics/PhysicsSettings.h | 6 +++--- .../Android/AndroidPlatformSettings.h | 6 +++--- .../Platform/Linux/LinuxPlatformSettings.h | 6 +++--- .../Engine/Platform/UWP/UWPPlatformSettings.h | 6 +++--- .../Windows/WindowsPlatformSettings.h | 6 +++--- Source/Platforms/PS4 | 1 + Source/Platforms/XboxScarlett | 1 + 17 files changed, 49 insertions(+), 45 deletions(-) create mode 160000 Source/Platforms/PS4 create mode 160000 Source/Platforms/XboxScarlett diff --git a/.gitmodules b/.gitmodules index e69de29bb..36d11c4bd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "Source/Platforms/PS4"] + path = Source/Platforms/PS4 + url = https://gitlab.flaxengine.com/flax/flaxengine-ps4.git +[submodule "Source/Platforms/XboxScarlett"] + path = Source/Platforms/XboxScarlett + url = https://gitlab.flaxengine.com/flax/flaxengine-xboxscarlett.git diff --git a/Source/Engine/Audio/AudioSettings.h b/Source/Engine/Audio/AudioSettings.h index 388e901b5..c8850adda 100644 --- a/Source/Engine/Audio/AudioSettings.h +++ b/Source/Engine/Audio/AudioSettings.h @@ -8,8 +8,8 @@ /// /// Audio settings container. /// -/// -class AudioSettings : public Settings +/// +class AudioSettings : public SettingsBase { public: @@ -30,7 +30,7 @@ public: public: - // [Settings] + // [SettingsBase] void RestoreDefault() final override { DisableAudio = false; diff --git a/Source/Engine/Core/Config/BuildSettings.h b/Source/Engine/Core/Config/BuildSettings.h index 26bf13d61..253ab5c7a 100644 --- a/Source/Engine/Core/Config/BuildSettings.h +++ b/Source/Engine/Core/Config/BuildSettings.h @@ -10,8 +10,7 @@ /// /// The game building rendering settings. /// -/// -class BuildSettings : public Settings +class BuildSettings : public SettingsBase { public: @@ -62,7 +61,7 @@ public: public: - // [Settings] + // [SettingsBase] void RestoreDefault() final override { MaxAssetsPerPackage = 1024; diff --git a/Source/Engine/Core/Config/GameSettings.cpp b/Source/Engine/Core/Config/GameSettings.cpp index b87c380aa..ea4fef45f 100644 --- a/Source/Engine/Core/Config/GameSettings.cpp +++ b/Source/Engine/Core/Config/GameSettings.cpp @@ -28,7 +28,7 @@ bool GameSettings::NoSplashScreen = false; Guid GameSettings::SplashScreen; Dictionary GameSettings::CustomSettings; -Array SettingsBase::Containers(32); +Array Settings::Containers(32); #if USE_EDITOR extern void LoadPlatformSettingsEditor(ISerializable::DeserializeStream& data); diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 2efd08eac..79f0b23c5 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -9,8 +9,7 @@ /// /// Graphics rendering settings. /// -/// -class GraphicsSettings : public Settings +class GraphicsSettings : public SettingsBase { public: @@ -56,7 +55,7 @@ public: public: - // [Settings] + // [SettingsBase] void Apply() override; void RestoreDefault() final override diff --git a/Source/Engine/Core/Config/LayersTagsSettings.h b/Source/Engine/Core/Config/LayersTagsSettings.h index 161302d49..db7292676 100644 --- a/Source/Engine/Core/Config/LayersTagsSettings.h +++ b/Source/Engine/Core/Config/LayersTagsSettings.h @@ -7,8 +7,7 @@ /// /// Layers and objects tags settings. /// -/// -class LayersAndTagsSettings : public Settings +class LayersAndTagsSettings : public SettingsBase { public: @@ -54,7 +53,7 @@ public: public: - // [Settings] + // [SettingsBase] void RestoreDefault() override { Tags.Clear(); diff --git a/Source/Engine/Core/Config/Settings.h b/Source/Engine/Core/Config/Settings.h index 6400a43a7..4d12788ba 100644 --- a/Source/Engine/Core/Config/Settings.h +++ b/Source/Engine/Core/Config/Settings.h @@ -7,16 +7,16 @@ #include "Engine/Serialization/ISerializable.h" /// -/// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to c#. +/// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to engine/game. /// -class SettingsBase +class FLAXENGINE_API Settings { public: /// /// The settings containers. /// - static Array Containers; + static Array Containers; /// /// Restores the default settings for all the registered containers. @@ -30,19 +30,19 @@ public: private: // Disable copy/move - SettingsBase(const SettingsBase&) = delete; - SettingsBase& operator=(const SettingsBase&) = delete; + Settings(const Settings&) = delete; + Settings& operator=(const Settings&) = delete; protected: - SettingsBase() + Settings() { Containers.Add(this); } public: - virtual ~SettingsBase() = default; + virtual ~Settings() = default; public: @@ -69,14 +69,14 @@ public: }; /// -/// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to c#. +/// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to engine/game. /// template -class Settings : public SettingsBase, public Singleton +class SettingsBase : public Settings, public Singleton { protected: - Settings() + SettingsBase() { } }; diff --git a/Source/Engine/Core/Config/TimeSettings.h b/Source/Engine/Core/Config/TimeSettings.h index 385cbad7e..c020bea09 100644 --- a/Source/Engine/Core/Config/TimeSettings.h +++ b/Source/Engine/Core/Config/TimeSettings.h @@ -8,8 +8,7 @@ /// /// Time and game simulation settings container. /// -/// -class TimeSettings : public Settings +class TimeSettings : public SettingsBase { public: @@ -40,7 +39,7 @@ public: public: - // [Settings] + // [SettingsBase] void Apply() override; void RestoreDefault() override diff --git a/Source/Engine/Input/InputSettings.h b/Source/Engine/Input/InputSettings.h index 7fc7d64a0..20b87943d 100644 --- a/Source/Engine/Input/InputSettings.h +++ b/Source/Engine/Input/InputSettings.h @@ -10,8 +10,8 @@ /// /// Input settings container. /// -/// -class InputSettings : public Settings +/// +class InputSettings : public SettingsBase { public: @@ -27,7 +27,7 @@ public: public: - // [Settings] + // [SettingsBase] void Apply() override { Input::ActionMappings = ActionMappings; diff --git a/Source/Engine/Navigation/NavigationSettings.h b/Source/Engine/Navigation/NavigationSettings.h index 86abac470..8ae98266d 100644 --- a/Source/Engine/Navigation/NavigationSettings.h +++ b/Source/Engine/Navigation/NavigationSettings.h @@ -82,7 +82,7 @@ public: public: - // [Settings] + // [SettingsBase] void RestoreDefault() final override { CellHeight = 10.0f; diff --git a/Source/Engine/Physics/PhysicsSettings.h b/Source/Engine/Physics/PhysicsSettings.h index 58f89feab..2ffd2307b 100644 --- a/Source/Engine/Physics/PhysicsSettings.h +++ b/Source/Engine/Physics/PhysicsSettings.h @@ -25,8 +25,8 @@ /// /// Physics simulation settings container. /// -/// -class PhysicsSettings : public Settings +/// +class PhysicsSettings : public SettingsBase { public: @@ -115,7 +115,7 @@ public: public: - // [Settings] + // [SettingsBase] void Apply() override; void RestoreDefault() override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override; diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index 3a1c1f736..061368486 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -9,8 +9,8 @@ /// /// Android platform settings. /// -/// -class AndroidPlatformSettings : public Settings +/// +class AndroidPlatformSettings : public SettingsBase { public: @@ -36,7 +36,7 @@ public: RestoreDefault(); } - // [Settings] + // [SettingsBase] void RestoreDefault() final override { PackageName = TEXT("com.${COMPANY_NAME}.${PROJECT_NAME}"); diff --git a/Source/Engine/Platform/Linux/LinuxPlatformSettings.h b/Source/Engine/Platform/Linux/LinuxPlatformSettings.h index eb8230da7..6086ae9f5 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatformSettings.h +++ b/Source/Engine/Platform/Linux/LinuxPlatformSettings.h @@ -9,8 +9,8 @@ /// /// Linux platform settings. /// -/// -class LinuxPlatformSettings : public Settings +/// +class LinuxPlatformSettings : public SettingsBase { public: @@ -56,7 +56,7 @@ public: public: - // [Settings] + // [SettingsBase] void RestoreDefault() final override { WindowMode = GameWindowMode::Windowed; diff --git a/Source/Engine/Platform/UWP/UWPPlatformSettings.h b/Source/Engine/Platform/UWP/UWPPlatformSettings.h index 0d5baa958..adc1469a4 100644 --- a/Source/Engine/Platform/UWP/UWPPlatformSettings.h +++ b/Source/Engine/Platform/UWP/UWPPlatformSettings.h @@ -9,8 +9,8 @@ /// /// Universal Windows Platform settings. /// -/// -class UWPPlatformSettings : public Settings +/// +class UWPPlatformSettings : public SettingsBase { public: @@ -95,7 +95,7 @@ public: public: - // [Settings] + // [SettingsBase] void RestoreDefault() final override { PreferredLaunchWindowingMode = WindowMode::FullScreen; diff --git a/Source/Engine/Platform/Windows/WindowsPlatformSettings.h b/Source/Engine/Platform/Windows/WindowsPlatformSettings.h index 2c1ca3ce4..3e163066e 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatformSettings.h +++ b/Source/Engine/Platform/Windows/WindowsPlatformSettings.h @@ -9,8 +9,8 @@ /// /// Windows platform settings. /// -/// -class WindowsPlatformSettings : public Settings +/// +class WindowsPlatformSettings : public SettingsBase { public: @@ -71,7 +71,7 @@ public: public: - // [Settings] + // [SettingsBase] void RestoreDefault() final override { WindowMode = GameWindowMode::Windowed; diff --git a/Source/Platforms/PS4 b/Source/Platforms/PS4 new file mode 160000 index 000000000..b9e29ede6 --- /dev/null +++ b/Source/Platforms/PS4 @@ -0,0 +1 @@ +Subproject commit b9e29ede69d31f93cbdd012d6e6c4ad8120dc7c0 diff --git a/Source/Platforms/XboxScarlett b/Source/Platforms/XboxScarlett new file mode 160000 index 000000000..7691bbaf2 --- /dev/null +++ b/Source/Platforms/XboxScarlett @@ -0,0 +1 @@ +Subproject commit 7691bbaf2a7e47459d9fb18fd1a8833cdbd7517a From 4d2d801bf4330c8f779663737e3a7e74e8da500d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 21 Dec 2020 14:53:33 +0100 Subject: [PATCH 010/419] Fix typos --- Source/Engine/Physics/Colliders/CharacterController.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 2abd1e62a..25db38937 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -143,7 +143,7 @@ public: API_PROPERTY() void SetStepOffset(float value); /// - /// Gets the minimum move distance of the character controller. The minimum travelled distance to consider. If travelled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. + /// Gets the minimum move distance of the character controller. The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. /// API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\")") FORCE_INLINE float GetMinMoveDistance() const @@ -152,7 +152,7 @@ public: } /// - /// Sets the minimum move distance of the character controller.The minimum travelled distance to consider. If travelled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. + /// Sets the minimum move distance of the character controller.The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. /// API_PROPERTY() void SetMinMoveDistance(float value); From 41b938b3cd26cf55c0a9e66714765b8ea554a5ab Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 21 Dec 2020 14:53:54 +0100 Subject: [PATCH 011/419] Refactor NavigationSettings to use API bindings --- .../Content/Settings/NavigationSettings.cs | 106 ------------------ Source/Engine/Level/Level.Build.cs | 1 - Source/Engine/Navigation/Navigation.Build.cs | 1 + Source/Engine/Navigation/NavigationSettings.h | 24 +++- 4 files changed, 19 insertions(+), 113 deletions(-) delete mode 100644 Source/Editor/Content/Settings/NavigationSettings.cs diff --git a/Source/Editor/Content/Settings/NavigationSettings.cs b/Source/Editor/Content/Settings/NavigationSettings.cs deleted file mode 100644 index f4e0e3a02..000000000 --- a/Source/Editor/Content/Settings/NavigationSettings.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. - -using System.ComponentModel; -using FlaxEngine; - -namespace FlaxEditor.Content.Settings -{ - /// - /// The navigation system settings container. - /// - public sealed class NavigationSettings : SettingsBase - { - /// - /// The height of a grid cell in the navigation mesh building steps using heightfields. - /// A lower number means higher precision on the vertical axis but longer build times. - /// - [DefaultValue(10.0f), Limit(1, 400)] - [EditorOrder(10), EditorDisplay("Nav Mesh Options"), Tooltip("The height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the vertical axis but longer build times.")] - public float CellHeight = 10.0f; - - /// - /// The width/height of a grid cell in the navigation mesh building steps using heightfields. - /// A lower number means higher precision on the horizontal axes but longer build times. - /// - [DefaultValue(30.0f), Limit(1, 400)] - [EditorOrder(20), EditorDisplay("Nav Mesh Options"), Tooltip("The width/height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the vertical axis but longer build times.")] - public float CellSize = 30.0f; - - /// - /// Tile size used for Navigation mesh tiles, the final size of a tile is CellSize*TileSize. - /// - [DefaultValue(64), Limit(8, 4096)] - [EditorOrder(30), EditorDisplay("Nav Mesh Options"), Tooltip("Tile size used for Navigation mesh tiles, the final size of a tile is CellSize*TileSize.")] - public int TileSize = 64; - - /// - /// The minimum number of cells allowed to form isolated island areas. - /// - [DefaultValue(0), Limit(0, 100)] - [EditorOrder(40), EditorDisplay("Nav Mesh Options"), Tooltip("The minimum number of cells allowed to form isolated island areas.")] - public int MinRegionArea = 0; - - /// - /// Any regions with a span count smaller than this value will, if possible, be merged with larger regions. - /// - [DefaultValue(20), Limit(0, 100)] - [EditorOrder(50), EditorDisplay("Nav Mesh Options"), Tooltip("Any regions with a span count smaller than this value will, if possible, be merged with larger regions.")] - public int MergeRegionArea = 20; - - /// - /// The maximum allowed length for contour edges along the border of the mesh. - /// - [DefaultValue(1200.0f), Limit(100)] - [EditorOrder(60), EditorDisplay("Nav Mesh Options", "Max Edge Length"), Tooltip("The maximum allowed length for contour edges along the border of the mesh.")] - public float MaxEdgeLen = 1200.0f; - - /// - /// The maximum distance a simplified contour's border edges should deviate from the original raw contour. - /// - [DefaultValue(1.3f), Limit(0.1f, 4)] - [EditorOrder(70), EditorDisplay("Nav Mesh Options"), Tooltip("The maximum distance a simplified contour's border edges should deviate from the original raw contour.")] - public float MaxEdgeError = 1.3f; - - /// - /// The sampling distance to use when generating the detail mesh. For height detail only. - /// - [DefaultValue(600.0f), Limit(1)] - [EditorOrder(80), EditorDisplay("Nav Mesh Options", "Detail Sampling Distance"), Tooltip("The sampling distance to use when generating the detail mesh.")] - public float DetailSamplingDist = 600.0f; - - /// - /// The maximum distance the detail mesh surface should deviate from heightfield data. For height detail only. - /// - [DefaultValue(1.0f), Limit(0, 3)] - [EditorOrder(90), EditorDisplay("Nav Mesh Options"), Tooltip("The maximum distance the detail mesh surface should deviate from heightfield data.")] - public float MaxDetailSamplingError = 1.0f; - - /// - /// The radius of the smallest objects to traverse this nav mesh. Objects can't pass through gaps of less than twice the radius. - /// - [DefaultValue(34.0f), Limit(0)] - [EditorOrder(1000), EditorDisplay("Agent Options"), Tooltip("The radius of the smallest objects to traverse this nav mesh. Objects can't pass through gaps of less than twice the radius.")] - public float WalkableRadius = 34.0f; - - /// - /// The height of the smallest objects to traverse this nav mesh. Objects can't enter areas with ceilings lower than this value. - /// - [DefaultValue(144.0f), Limit(0)] - [EditorOrder(1010), EditorDisplay("Agent Options"), Tooltip("The height of the smallest objects to traverse this nav mesh. Objects can't enter areas with ceilings lower than this value.")] - public float WalkableHeight = 144.0f; - - /// - /// The maximum ledge height that is considered to still be traversable. - /// - [DefaultValue(35.0f), Limit(0)] - [EditorOrder(1020), EditorDisplay("Agent Options"), Tooltip("The maximum ledge height that is considered to still be traversable.")] - public float WalkableMaxClimb = 35.0f; - - /// - /// The maximum slope that is considered walkable (in degrees). Objects can't go up or down slopes higher than this value. - /// - [DefaultValue(60.0f), Limit(0, 89.0f)] - [EditorOrder(1030), EditorDisplay("Agent Options"), Tooltip("The maximum slope that is considered walkable (in degrees). Objects can't go up or down slopes higher than this value.")] - public float WalkableMaxSlopeAngle = 60.0f; - } -} diff --git a/Source/Engine/Level/Level.Build.cs b/Source/Engine/Level/Level.Build.cs index e65967025..a2b680aee 100644 --- a/Source/Engine/Level/Level.Build.cs +++ b/Source/Engine/Level/Level.Build.cs @@ -21,7 +21,6 @@ public class Level : EngineModule options.PublicDependencies.Add("Scripting"); options.PublicDependencies.Add("Serialization"); - options.PublicDependencies.Add("Navigation"); if (options.Target.IsEditor) { diff --git a/Source/Engine/Navigation/Navigation.Build.cs b/Source/Engine/Navigation/Navigation.Build.cs index 96d086e44..00edf3dde 100644 --- a/Source/Engine/Navigation/Navigation.Build.cs +++ b/Source/Engine/Navigation/Navigation.Build.cs @@ -15,6 +15,7 @@ public class Navigation : EngineModule options.PublicDefinitions.Add("COMPILE_WITH_NAV_MESH_BUILDER"); + options.PrivateDependencies.Add("Level"); options.PrivateDependencies.Add("recastnavigation"); if (options.Target.IsEditor) diff --git a/Source/Engine/Navigation/NavigationSettings.h b/Source/Engine/Navigation/NavigationSettings.h index 8ae98266d..4e2fde1a2 100644 --- a/Source/Engine/Navigation/NavigationSettings.h +++ b/Source/Engine/Navigation/NavigationSettings.h @@ -2,82 +2,94 @@ #pragma once +#include "NavigationTypes.h" #include "Engine/Core/Config/Settings.h" #include "Engine/Serialization/Serialization.h" /// /// The navigation system settings container. /// -/// -class NavigationSettings : public Settings +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API NavigationSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(NavigationSettings); public: /// - /// The height of a grid cell in the navigation mesh building steps using heightfields. - /// A lower number means higher precision on the vertical axis but longer build times. + /// The height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the vertical axis but longer build times. /// + API_FIELD(Attributes="DefaultValue(10.0f), Limit(1, 400), EditorOrder(10), EditorDisplay(\"Nav Mesh Options\")") float CellHeight = 10.0f; /// - /// The width/height of a grid cell in the navigation mesh building steps using heightfields. - /// A lower number means higher precision on the horizontal axes but longer build times. + /// The width/height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the horizontal axes but longer build times. /// + API_FIELD(Attributes="DefaultValue(30.0f), Limit(1, 400), EditorOrder(20), EditorDisplay(\"Nav Mesh Options\")") float CellSize = 30.0f; /// /// Tile size used for Navigation mesh tiles, the final size of a tile is CellSize*TileSize. /// + API_FIELD(Attributes="DefaultValue(64), Limit(8, 4096), EditorOrder(30), EditorDisplay(\"Nav Mesh Options\")") int32 TileSize = 64; /// /// The minimum number of cells allowed to form isolated island areas. /// + API_FIELD(Attributes="DefaultValue(0), Limit(0, 100), EditorOrder(40), EditorDisplay(\"Nav Mesh Options\")") int32 MinRegionArea = 0; /// /// Any regions with a span count smaller than this value will, if possible, be merged with larger regions. /// + API_FIELD(Attributes="DefaultValue(20), Limit(0, 100), EditorOrder(50), EditorDisplay(\"Nav Mesh Options\")") int32 MergeRegionArea = 20; /// /// The maximum allowed length for contour edges along the border of the mesh. /// + API_FIELD(Attributes="DefaultValue(1200.0f), Limit(100), EditorOrder(60), EditorDisplay(\"Nav Mesh Options\", \"Max Edge Length\")") float MaxEdgeLen = 1200.0f; /// /// The maximum distance a simplified contour's border edges should deviate from the original raw contour. /// + API_FIELD(Attributes="DefaultValue(1.3f), Limit(0.1f, 4), EditorOrder(70), EditorDisplay(\"Nav Mesh Options\")") float MaxEdgeError = 1.3f; /// /// The sampling distance to use when generating the detail mesh. /// + API_FIELD(Attributes="DefaultValue(600.0f), Limit(1), EditorOrder(80), EditorDisplay(\"Nav Mesh Options\", \"Detail Sampling Distance\")") float DetailSamplingDist = 600.0f; /// /// The maximum distance the detail mesh surface should deviate from heightfield data. /// + API_FIELD(Attributes="DefaultValue(1.0f), Limit(0, 3), EditorOrder(90), EditorDisplay(\"Nav Mesh Options\")") float MaxDetailSamplingError = 1.0f; /// /// The radius of the smallest objects to traverse this nav mesh. Objects can't pass through gaps of less than twice the radius. /// + API_FIELD(Attributes="DefaultValue(34.0f), Limit(0), EditorOrder(1000), EditorDisplay(\"Agent Options\")") float WalkableRadius = 34.0f; /// /// The height of the smallest objects to traverse this nav mesh. Objects can't enter areas with ceilings lower than this value. /// + API_FIELD(Attributes="DefaultValue(144.0f), Limit(0), EditorOrder(1010), EditorDisplay(\"Agent Options\")") float WalkableHeight = 144.0f; /// /// The maximum ledge height that is considered to still be traversable. /// + API_FIELD(Attributes="DefaultValue(35.0f), Limit(0), EditorOrder(1020), EditorDisplay(\"Agent Options\")") float WalkableMaxClimb = 35.0f; /// /// The maximum slope that is considered walkable (in degrees). Objects can't go up or down slopes higher than this value. /// + API_FIELD(Attributes="DefaultValue(60.0f), Limit(0, 89.0f), EditorOrder(1030), EditorDisplay(\"Agent Options\")") float WalkableMaxSlopeAngle = 60.0f; public: From 7798a7f6d5699be320ff2c6fe067b8c9bf8eaa73 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 21 Dec 2020 15:39:14 +0100 Subject: [PATCH 012/419] Refactor PhysicalMaterial to use API bindings --- Source/Engine/Content/JsonAsset.cpp | 4 +- Source/Engine/Engine/PhysicalMaterial.cs | 46 ------------------- Source/Engine/Physics/PhysicalMaterial.cpp | 16 ------- Source/Engine/Physics/PhysicalMaterial.h | 37 ++++++--------- .../Bindings/BindingsGenerator.CSharp.cs | 4 +- .../Bindings/BindingsGenerator.Cpp.cs | 2 + Source/Tools/Flax.Build/Bindings/ClassInfo.cs | 4 ++ 7 files changed, 24 insertions(+), 89 deletions(-) delete mode 100644 Source/Engine/Engine/PhysicalMaterial.cs diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index 4e1f90d70..3779181b7 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -190,13 +190,11 @@ ISerializable* Create() return New(); } +// Key: managed class typename, Value: unmanaged instance spawner function Dictionary UnmanagedTypes(32); void InitUnmanagedJsonTypes() { - ASSERT(UnmanagedTypes.IsEmpty()); - - // Key: managed class typename, Value: unmanaged instance spawner function UnmanagedTypes[TEXT("FlaxEngine.PhysicalMaterial")] = &Create; } diff --git a/Source/Engine/Engine/PhysicalMaterial.cs b/Source/Engine/Engine/PhysicalMaterial.cs deleted file mode 100644 index 1ad1c732c..000000000 --- a/Source/Engine/Engine/PhysicalMaterial.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. - -namespace FlaxEngine -{ - /// - /// Physical materials are used to define the response of a physical object when interacting dynamically with the world. - /// - public sealed class PhysicalMaterial - { - /// - /// The friction value of surface, controls how easily things can slide on this surface. - /// - [EditorOrder(0), Limit(0), EditorDisplay("Physical Material"), Tooltip("The friction value of surface, controls how easily things can slide on this surface.")] - public float Friction = 0.7f; - - /// - /// The friction combine mode, controls how friction is computed for multiple materials. - /// - [EditorOrder(1), EditorDisplay("Physical Material"), Tooltip("The friction combine mode, controls how friction is computed for multiple materials.")] - public PhysicsCombineMode FrictionCombineMode = PhysicsCombineMode.Average; - - /// - /// If set we will use the FrictionCombineMode of this material, instead of the FrictionCombineMode found in the Physics settings. - /// - [HideInEditor] - public bool OverrideFrictionCombineMode = false; - - /// - /// The restitution or 'bounciness' of this surface, between 0 (no bounce) and 1 (outgoing velocity is same as incoming). - /// - [EditorOrder(3), Range(0, 1), EditorDisplay("Physical Material"), Tooltip("The restitution or \'bounciness\' of this surface, between 0 (no bounce) and 1 (outgoing velocity is same as incoming).")] - public float Restitution = 0.3f; - - /// - /// The restitution combine mode, controls how restitution is computed for multiple materials. - /// - [EditorOrder(4), EditorDisplay("Physical Material"), Tooltip("The restitution combine mode, controls how restitution is computed for multiple materials.")] - public PhysicsCombineMode RestitutionCombineMode = PhysicsCombineMode.Average; - - /// - /// If set we will use the RestitutionCombineMode of this material, instead of the RestitutionCombineMode found in the Physics settings. - /// - [HideInEditor] - public bool OverrideRestitutionCombineMode = false; - } -} diff --git a/Source/Engine/Physics/PhysicalMaterial.cpp b/Source/Engine/Physics/PhysicalMaterial.cpp index 61ba840be..115e85a15 100644 --- a/Source/Engine/Physics/PhysicalMaterial.cpp +++ b/Source/Engine/Physics/PhysicalMaterial.cpp @@ -2,7 +2,6 @@ #include "PhysicalMaterial.h" #include "PhysicsSettings.h" -#include "Engine/Serialization/JsonTools.h" #include "Physics.h" #include #include @@ -51,18 +50,3 @@ void PhysicalMaterial::UpdatePhysXMaterial() _material->setRestitutionCombineMode(static_cast(useRestitutionCombineMode)); } } - -void PhysicalMaterial::Serialize(SerializeStream& stream, const void* otherObj) -{ - MISSING_CODE("PhysicalMaterial::Serialize is not implemented"); -} - -void PhysicalMaterial::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) -{ - Friction = JsonTools::GetFloat(stream, "Friction", PhysicalMaterial_Friction); - FrictionCombineMode = JsonTools::GetEnum(stream, "FrictionCombineMode", PhysicalMaterial_FrictionCombineMode); - OverrideFrictionCombineMode = JsonTools::GetBool(stream, "OverrideFrictionCombineMode", PhysicalMaterial_OverrideFrictionCombineMode); - Restitution = JsonTools::GetFloat(stream, "Restitution", PhysicalMaterial_Restitution); - RestitutionCombineMode = JsonTools::GetEnum(stream, "RestitutionCombineMode", PhysicalMaterial_RestitutionCombineMode); - OverrideRestitutionCombineMode = JsonTools::GetBool(stream, "OverrideRestitutionCombineMode", PhysicalMaterial_OverrideRestitutionCombineMode); -} diff --git a/Source/Engine/Physics/PhysicalMaterial.h b/Source/Engine/Physics/PhysicalMaterial.h index b2dc3f8e6..8bc0eebf7 100644 --- a/Source/Engine/Physics/PhysicalMaterial.h +++ b/Source/Engine/Physics/PhysicalMaterial.h @@ -5,20 +5,13 @@ #include "Types.h" #include "Engine/Serialization/ISerializable.h" -// Default values for the physical material - -#define PhysicalMaterial_Friction 0.7f -#define PhysicalMaterial_FrictionCombineMode PhysicsCombineMode::Average -#define PhysicalMaterial_OverrideFrictionCombineMode false -#define PhysicalMaterial_Restitution 0.3f -#define PhysicalMaterial_RestitutionCombineMode PhysicsCombineMode::Average -#define PhysicalMaterial_OverrideRestitutionCombineMode false - /// /// Physical materials are used to define the response of a physical object when interacting dynamically with the world. /// -class FLAXENGINE_API PhysicalMaterial : public ISerializable +API_CLASS() class FLAXENGINE_API PhysicalMaterial final : public ISerializable { +API_AUTO_SERIALIZATION(); +DECLARE_SCRIPTING_TYPE_MINIMAL(PhysicalMaterial); private: PxMaterial* _material; @@ -40,32 +33,38 @@ public: /// /// The friction value of surface, controls how easily things can slide on this surface. /// - float Friction = PhysicalMaterial_Friction; + API_FIELD(Attributes="EditorOrder(0), Limit(0), EditorDisplay(\"Physical Material\")") + float Friction = 0.7f; /// /// The friction combine mode, controls how friction is computed for multiple materials. /// - PhysicsCombineMode FrictionCombineMode = PhysicalMaterial_FrictionCombineMode; + API_FIELD(Attributes="EditorOrder(1), EditorDisplay(\"Physical Material\")") + PhysicsCombineMode FrictionCombineMode = PhysicsCombineMode::Average; /// /// If set we will use the FrictionCombineMode of this material, instead of the FrictionCombineMode found in the Physics settings. /// - bool OverrideFrictionCombineMode = PhysicalMaterial_OverrideFrictionCombineMode; + API_FIELD(Attributes="HideInEditor") + bool OverrideFrictionCombineMode = false; /// /// The restitution or 'bounciness' of this surface, between 0 (no bounce) and 1 (outgoing velocity is same as incoming). /// - float Restitution = PhysicalMaterial_Restitution; + API_FIELD(Attributes="EditorOrder(3), Range(0, 1), EditorDisplay(\"Physical Material\")") + float Restitution = 0.3f; /// /// The restitution combine mode, controls how restitution is computed for multiple materials. /// - PhysicsCombineMode RestitutionCombineMode = PhysicalMaterial_RestitutionCombineMode; + API_FIELD(Attributes="EditorOrder(4), EditorDisplay(\"Physical Material\")") + PhysicsCombineMode RestitutionCombineMode = PhysicsCombineMode::Average; /// /// If set we will use the RestitutionCombineMode of this material, instead of the RestitutionCombineMode found in the Physics settings. /// - bool OverrideRestitutionCombineMode = PhysicalMaterial_OverrideRestitutionCombineMode; + API_FIELD(Attributes="HideInEditor") + bool OverrideRestitutionCombineMode = false; public: @@ -79,10 +78,4 @@ public: /// Updates the PhysX material (after any property change). /// void UpdatePhysXMaterial(); - -public: - - // [ISerializable] - void Serialize(SerializeStream& stream, const void* otherObj) override; - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; }; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index b53c147c2..966737e95 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -393,7 +393,7 @@ namespace Flax.Build.Bindings { // Write attributes contents.Append(indent).Append('[').Append(attributes).Append(']').AppendLine(); - writeTooltip = !attributes.Contains("Tooltip("); + writeTooltip = !attributes.Contains("Tooltip(") && !attributes.Contains("HideInEditor"); } if (useUnmanaged) @@ -462,7 +462,7 @@ namespace Flax.Build.Bindings else if (classInfo.IsAbstract) contents.Append("abstract "); contents.Append("unsafe partial class ").Append(classInfo.Name); - if (classInfo.BaseType != null && classInfo.BaseTypeInheritance != AccessLevel.Private) + if (classInfo.BaseType != null && !classInfo.IsBaseTypeHidden) contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, classInfo.BaseType, classInfo)); contents.AppendLine(); contents.Append(indent + "{"); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index f9a695b77..e64240b9e 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -949,6 +949,8 @@ namespace Flax.Build.Bindings var baseType = classInfo?.BaseType ?? structureInfo?.BaseType; if (baseType != null && baseType.Type == "ISerializable") baseType = null; + else if (classInfo != null && classInfo.IsBaseTypeHidden) + baseType = null; CppAutoSerializeFields.Clear(); CppAutoSerializeProperties.Clear(); CppIncludeFiles.Add("Engine/Serialization/Serialization.h"); diff --git a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs index 1a62132b2..3ac5b37fc 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs @@ -25,6 +25,7 @@ namespace Flax.Build.Bindings public AccessLevel Access; public TypeInfo BaseType; public AccessLevel BaseTypeInheritance; + public bool IsBaseTypeHidden; public bool IsStatic; public bool IsSealed; public bool IsAbstract; @@ -50,6 +51,9 @@ namespace Flax.Build.Bindings { base.Init(buildData); + // Internal base types are usually hidden from bindings (used in core-only internally) + IsBaseTypeHidden = BaseTypeInheritance == AccessLevel.Private || BaseType.Type == "ISerializable"; + // Cache if it it Scripting Object type if (InBuildScriptingObjectTypes.Contains(Name)) _isScriptingObject = true; From 5e77925492addb7c226d9db2758288b3918c429d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 22 Dec 2020 11:18:00 +0100 Subject: [PATCH 013/419] Fix crash when using Physical Material with missing instance --- Source/Engine/Physics/Colliders/Collider.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index b00ceb925..9ddc4f1bc 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -207,7 +207,7 @@ void Collider::Attach(RigidBody* rigidBody) // Attach rigidBody->GetPhysXRigidActor()->attachShape(*_shape); - _shape->setLocalPose(PxTransform(C2P((_localTransform.Translation + _localTransform.Orientation * _center) * rigidBody->GetScale()), C2P(_localTransform.Orientation))); + _shape->setLocalPose(PxTransform(C2P((_localTransform.Translation + _localTransform.Orientation * _center) * rigidBody->GetScale()), C2P(_localTransform.Orientation))); if (rigidBody->IsDuringPlay()) rigidBody->UpdateBounds(); } @@ -246,7 +246,7 @@ void Collider::CreateShapeBase(const PxGeometry& geometry) const bool isTrigger = _isTrigger && CanBeTrigger(); const PxShapeFlags shapeFlags = GetShapeFlags(isTrigger, IsActiveInHierarchy()); PxMaterial* material = Physics::GetDefaultMaterial(); - if (Material && !Material->WaitForLoaded()) + if (Material && !Material->WaitForLoaded() && Material->Instance) { material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial(); } @@ -291,12 +291,9 @@ void Collider::OnMaterialChanged() if (_shape) { PxMaterial* material = Physics::GetDefaultMaterial(); - if (Material) + if (Material && !Material->WaitForLoaded() && Material->Instance) { - if (!Material->WaitForLoaded()) - { - material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial(); - } + material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial(); } _shape->setMaterials(&material, 1); } From 4665e8fbdb4806fe9425667b9145ff71fedfecf9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 22 Dec 2020 12:55:57 +0100 Subject: [PATCH 014/419] Add support for basic classes to Scripting Type (without scripting object as a base) --- Source/Engine/Content/Assets/VisualScript.cpp | 46 ++--- Source/Engine/Level/SceneObjectsFactory.cpp | 6 +- Source/Engine/Scripting/BinaryModule.cpp | 172 ++++++++++-------- Source/Engine/Scripting/Script.cpp | 6 +- Source/Engine/Scripting/ScriptingObject.cpp | 6 +- Source/Engine/Scripting/ScriptingType.h | 14 +- .../Bindings/BindingsGenerator.Cpp.cs | 6 +- .../Build/Plugins/VisualScriptingPlugin.cs | 4 +- 8 files changed, 150 insertions(+), 110 deletions(-) diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp index bb27a233e..e59f4ceb1 100644 --- a/Source/Engine/Content/Assets/VisualScript.cpp +++ b/Source/Engine/Content/Assets/VisualScript.cpp @@ -1290,11 +1290,11 @@ Asset::LoadResult VisualScript::load() // Hack vtable similarly to VisualScriptObjectSpawn ScriptingType& visualScriptType = (ScriptingType&)object->GetType(); - if (visualScriptType.Class.ScriptVTable) + if (visualScriptType.Script.ScriptVTable) { // Override object vtable with hacked one that has Visual Script functions calls - ASSERT(visualScriptType.Class.VTable); - *(void**)object = visualScriptType.Class.VTable; + ASSERT(visualScriptType.Script.VTable); + *(void**)object = visualScriptType.Script.VTable; } } const int32 oldCount = _oldParamsLayout.Count(); @@ -1361,10 +1361,10 @@ void VisualScript::unload(bool isReloading) if (_scriptingTypeHandle) { auto& type = VisualScriptingModule.Types[_scriptingTypeHandle.TypeIndex]; - if (type.Class.DefaultInstance) + if (type.Script.DefaultInstance) { - Delete(type.Class.DefaultInstance); - type.Class.DefaultInstance = nullptr; + Delete(type.Script.DefaultInstance); + type.Script.DefaultInstance = nullptr; } VisualScriptingModule.TypeNameToTypeIndex.RemoveValue(_scriptingTypeHandle.TypeIndex); VisualScriptingModule.Scripts[_scriptingTypeHandle.TypeIndex] = nullptr; @@ -1389,7 +1389,7 @@ void VisualScript::CacheScriptingType() { // Find first native base C++ class of this Visual Script class ScriptingTypeHandle nativeType = baseType; - while (nativeType && nativeType.GetType().Class.ScriptVTable) + while (nativeType && nativeType.GetType().Script.ScriptVTable) { nativeType = nativeType.GetType().GetBaseType(); } @@ -1437,14 +1437,14 @@ void VisualScript::CacheScriptingType() for (ScriptingTypeHandle e = nativeType; e;) { const ScriptingType& eType = e.GetType(); - if (eType.Class.SetupScriptVTable) + if (eType.Script.SetupScriptVTable) { ASSERT(eType.ManagedClass); - eType.Class.SetupScriptVTable(eType.ManagedClass, type.Class.ScriptVTable, type.Class.ScriptVTableBase); + eType.Script.SetupScriptVTable(eType.ManagedClass, type.Script.ScriptVTable, type.Script.ScriptVTableBase); } e = eType.GetBaseType(); } - MMethod** scriptVTable = (MMethod**)type.Class.ScriptVTable; + MMethod** scriptVTable = (MMethod**)type.Script.ScriptVTable; while (scriptVTable && *scriptVTable) { const MMethod* referenceMethod = *scriptVTable; @@ -1498,12 +1498,12 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri ScriptingType& visualScriptType = (ScriptingType&)params.Type.GetType(); ScriptingTypeHandle baseTypeHandle = visualScriptType.GetBaseType(); const ScriptingType* baseTypePtr = &baseTypeHandle.GetType(); - while (baseTypePtr->Class.Spawn == &VisualScriptObjectSpawn) + while (baseTypePtr->Script.Spawn == &VisualScriptObjectSpawn) { baseTypeHandle = baseTypePtr->GetBaseType(); baseTypePtr = &baseTypeHandle.GetType(); } - ScriptingObject* object = baseTypePtr->Class.Spawn(params); + ScriptingObject* object = baseTypePtr->Script.Spawn(params); if (!object) { return nullptr; @@ -1514,9 +1514,9 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri // We create a custom vtable for the Visual Script objects that use a native class object with virtual functions overrides. // To make it easy to use in C++ we inject custom wrapper methods into C++ object vtable to execute Visual Script graph from them. // Because virtual member functions calls are C++ ABI and impl-defined this is quite hard. But works. - if (visualScriptType.Class.ScriptVTable) + if (visualScriptType.Script.ScriptVTable) { - if (!visualScriptType.Class.VTable) + if (!visualScriptType.Script.VTable) { // Duplicate vtable void** vtable = *(void***)object; @@ -1525,21 +1525,21 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri while (vtable[entriesCount] && entriesCount < 200) entriesCount++; const int32 size = entriesCount * sizeof(void*); - visualScriptType.Class.VTable = (void**)((byte*)Platform::Allocate(prefixSize + size, 16) + prefixSize); - Platform::MemoryCopy((byte*)visualScriptType.Class.VTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size); + visualScriptType.Script.VTable = (void**)((byte*)Platform::Allocate(prefixSize + size, 16) + prefixSize); + Platform::MemoryCopy((byte*)visualScriptType.Script.VTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size); // Override vtable entries by the class for (ScriptingTypeHandle e = baseTypeHandle; e;) { const ScriptingType& eType = e.GetType(); - if (eType.Class.SetupScriptObjectVTable) - eType.Class.SetupScriptObjectVTable(visualScriptType.Class.ScriptVTable, visualScriptType.Class.ScriptVTableBase, visualScriptType.Class.VTable, entriesCount, 1); + if (eType.Script.SetupScriptObjectVTable) + eType.Script.SetupScriptObjectVTable(visualScriptType.Script.ScriptVTable, visualScriptType.Script.ScriptVTableBase, visualScriptType.Script.VTable, entriesCount, 1); e = eType.GetBaseType(); } } // Override object vtable with hacked one that has Visual Script functions calls - *(void**)object = visualScriptType.Class.VTable; + *(void**)object = visualScriptType.Script.VTable; } // Mark as custom scripting type @@ -1573,10 +1573,10 @@ void VisualScriptingBinaryModule::OnScriptsReloading() if (script->_scriptingTypeHandle) { auto& type = VisualScriptingModule.Types[script->_scriptingTypeHandle.TypeIndex]; - if (type.Class.DefaultInstance) + if (type.Script.DefaultInstance) { - Delete(type.Class.DefaultInstance); - type.Class.DefaultInstance = nullptr; + Delete(type.Script.DefaultInstance); + type.Script.DefaultInstance = nullptr; } VisualScriptingModule.TypeNameToTypeIndex.RemoveValue(script->_scriptingTypeHandle.TypeIndex); script->_scriptingTypeHandleCached = script->_scriptingTypeHandle; @@ -1862,7 +1862,7 @@ ScriptingTypeHandle VisualScript::GetScriptingType() ScriptingObject* VisualScript::CreateInstance() { const auto scriptingTypeHandle = GetScriptingType(); - return scriptingTypeHandle ? scriptingTypeHandle.GetType().Class.Spawn(ScriptingObjectSpawnParams(Guid::New(), scriptingTypeHandle)) : nullptr; + return scriptingTypeHandle ? scriptingTypeHandle.GetType().Script.Spawn(ScriptingObjectSpawnParams(Guid::New(), scriptingTypeHandle)) : nullptr; } Variant VisualScript::GetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance) const diff --git a/Source/Engine/Level/SceneObjectsFactory.cpp b/Source/Engine/Level/SceneObjectsFactory.cpp index 5d124d0a7..a52d63586 100644 --- a/Source/Engine/Level/SceneObjectsFactory.cpp +++ b/Source/Engine/Level/SceneObjectsFactory.cpp @@ -79,7 +79,7 @@ SceneObject* SceneObjectsFactory::Spawn(ISerializable::DeserializeStream& stream if (type) { const ScriptingObjectSpawnParams params(id, type); - obj = (SceneObject*)type.GetType().Class.Spawn(params); + obj = (SceneObject*)type.GetType().Script.Spawn(params); if (obj == nullptr) { LOG(Warning, "Failed to spawn object of type {0}.", type.ToString(true)); @@ -109,7 +109,7 @@ SceneObject* SceneObjectsFactory::Spawn(ISerializable::DeserializeStream& stream if (type) { const ScriptingObjectSpawnParams params(id, type); - obj = (SceneObject*)type.GetType().Class.Spawn(params); + obj = (SceneObject*)type.GetType().Script.Spawn(params); if (obj == nullptr) { LOG(Warning, "Failed to spawn object of type {0}.", type.ToString(true)); @@ -384,7 +384,7 @@ Actor* SceneObjectsFactory::CreateActor(int32 typeId, const Guid& id) if (type) { const ScriptingObjectSpawnParams params(id, type); - const auto result = dynamic_cast(type.GetType().Class.Spawn(params)); + const auto result = dynamic_cast(type.GetType().Script.Spawn(params)); if (result == nullptr) { LOG(Warning, "Failed to spawn object of type {0}.", type.ToString(true)); diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 6d41bbee4..4c287f2cb 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -60,16 +60,16 @@ ScriptingType::ScriptingType() , Module(nullptr) , InitRuntime(nullptr) , Fullname(nullptr, 0) - , Type(ScriptingTypes::Class) + , Type(ScriptingTypes::Script) , BaseTypePtr(nullptr) { - Class.Spawn = nullptr; - Class.VTable = nullptr; - Class.ScriptVTable = nullptr; - Class.ScriptVTableBase = nullptr; - Class.SetupScriptVTable = nullptr; - Class.SetupScriptObjectVTable = nullptr; - Class.DefaultInstance = nullptr; + Script.Spawn = nullptr; + Script.VTable = nullptr; + Script.ScriptVTable = nullptr; + Script.ScriptVTableBase = nullptr; + Script.SetupScriptVTable = nullptr; + Script.SetupScriptObjectVTable = nullptr; + Script.DefaultInstance = nullptr; } ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, const ScriptingTypeHandle& baseType, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable) @@ -77,21 +77,39 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul , Module(module) , InitRuntime(initRuntime) , Fullname(fullname) - , Type(ScriptingTypes::Class) + , Type(ScriptingTypes::Script) , BaseTypeHandle(baseType) , BaseTypePtr(nullptr) , Size(size) { - Class.Spawn = spawn; - Class.VTable = nullptr; - Class.ScriptVTable = nullptr; - Class.ScriptVTableBase = nullptr; - Class.SetupScriptVTable = setupScriptVTable; - Class.SetupScriptObjectVTable = setupScriptObjectVTable; - Class.DefaultInstance = nullptr; + Script.Spawn = spawn; + Script.VTable = nullptr; + Script.ScriptVTable = nullptr; + Script.ScriptVTableBase = nullptr; + Script.SetupScriptVTable = setupScriptVTable; + Script.SetupScriptObjectVTable = setupScriptObjectVTable; + Script.DefaultInstance = nullptr; } ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, ScriptingTypeInitializer* baseType, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable) + : ManagedClass(nullptr) + , Module(module) + , InitRuntime(initRuntime) + , Fullname(fullname) + , Type(ScriptingTypes::Script) + , BaseTypePtr(baseType) + , Size(size) +{ + Script.Spawn = spawn; + Script.VTable = nullptr; + Script.ScriptVTable = nullptr; + Script.ScriptVTableBase = nullptr; + Script.SetupScriptVTable = setupScriptVTable; + Script.SetupScriptObjectVTable = setupScriptObjectVTable; + Script.DefaultInstance = nullptr; +} + +ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType) : ManagedClass(nullptr) , Module(module) , InitRuntime(initRuntime) @@ -100,13 +118,8 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul , BaseTypePtr(baseType) , Size(size) { - Class.Spawn = spawn; - Class.VTable = nullptr; - Class.ScriptVTable = nullptr; - Class.ScriptVTableBase = nullptr; - Class.SetupScriptVTable = setupScriptVTable; - Class.SetupScriptObjectVTable = setupScriptObjectVTable; - Class.DefaultInstance = nullptr; + Class.Ctor = ctor; + Class.Dtor = dtor; } ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType) @@ -139,14 +152,14 @@ ScriptingType::ScriptingType(const ScriptingType& other) { switch (other.Type) { - case ScriptingTypes::Class: - Class.Spawn = other.Class.Spawn; - Class.VTable = nullptr; - Class.ScriptVTable = nullptr; - Class.ScriptVTableBase = nullptr; - Class.SetupScriptVTable = other.Class.SetupScriptVTable; - Class.SetupScriptObjectVTable = other.Class.SetupScriptObjectVTable; - Class.DefaultInstance = nullptr; + case ScriptingTypes::Script: + Script.Spawn = other.Script.Spawn; + Script.VTable = nullptr; + Script.ScriptVTable = nullptr; + Script.ScriptVTableBase = nullptr; + Script.SetupScriptVTable = other.Script.SetupScriptVTable; + Script.SetupScriptObjectVTable = other.Script.SetupScriptObjectVTable; + Script.DefaultInstance = nullptr; break; case ScriptingTypes::Structure: Struct.Ctor = other.Struct.Ctor; @@ -175,18 +188,18 @@ ScriptingType::ScriptingType(ScriptingType&& other) { switch (other.Type) { - case ScriptingTypes::Class: - Class.Spawn = other.Class.Spawn; - Class.VTable = other.Class.VTable; - other.Class.VTable = nullptr; - Class.ScriptVTable = other.Class.ScriptVTable; - other.Class.ScriptVTable = nullptr; - Class.ScriptVTableBase = other.Class.ScriptVTableBase; - other.Class.ScriptVTableBase = nullptr; - Class.SetupScriptVTable = other.Class.SetupScriptVTable; - Class.SetupScriptObjectVTable = other.Class.SetupScriptObjectVTable; - Class.DefaultInstance = other.Class.DefaultInstance; - other.Class.DefaultInstance = nullptr; + case ScriptingTypes::Script: + Script.Spawn = other.Script.Spawn; + Script.VTable = other.Script.VTable; + other.Script.VTable = nullptr; + Script.ScriptVTable = other.Script.ScriptVTable; + other.Script.ScriptVTable = nullptr; + Script.ScriptVTableBase = other.Script.ScriptVTableBase; + other.Script.ScriptVTableBase = nullptr; + Script.SetupScriptVTable = other.Script.SetupScriptVTable; + Script.SetupScriptObjectVTable = other.Script.SetupScriptObjectVTable; + Script.DefaultInstance = other.Script.DefaultInstance; + other.Script.DefaultInstance = nullptr; break; case ScriptingTypes::Structure: Struct.Ctor = other.Struct.Ctor; @@ -207,13 +220,13 @@ ScriptingType::~ScriptingType() { switch (Type) { - case ScriptingTypes::Class: - if (Class.DefaultInstance) - Delete(Class.DefaultInstance); - if (Class.VTable) - Platform::Free((byte*)Class.VTable - GetVTablePrefix()); - Platform::Free(Class.ScriptVTable); - Platform::Free(Class.ScriptVTableBase); + case ScriptingTypes::Script: + if (Script.DefaultInstance) + Delete(Script.DefaultInstance); + if (Script.VTable) + Platform::Free((byte*)Script.VTable - GetVTablePrefix()); + Platform::Free(Script.ScriptVTable); + Platform::Free(Script.ScriptVTableBase); break; case ScriptingTypes::Structure: break; @@ -235,17 +248,17 @@ ScriptingTypeHandle ScriptingType::GetHandle() const ScriptingObject* ScriptingType::GetDefaultInstance() const { - ASSERT(Type == ScriptingTypes::Class); - if (!Class.DefaultInstance) + ASSERT(Type == ScriptingTypes::Script); + if (!Script.DefaultInstance) { const ScriptingObjectSpawnParams params(Guid::New(), GetHandle()); - Class.DefaultInstance = Class.Spawn(params); - if (!Class.DefaultInstance) + Script.DefaultInstance = Script.Spawn(params); + if (!Script.DefaultInstance) { LOG(Error, "Failed to create default instance of type {0}", ToString()); } } - return Class.DefaultInstance; + return Script.DefaultInstance; } String ScriptingType::ToString() const @@ -268,6 +281,21 @@ ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const S module->TypeNameToTypeIndex[typeName] = TypeIndex; } +ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType) + : ScriptingTypeHandle(module, module->Types.Count()) +{ + module->Types.AddUninitialized(); + new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, baseType); + const MString typeName(fullname.Get(), fullname.Length()); +#if BUILD_DEBUG + if (module->TypeNameToTypeIndex.ContainsKey(typeName)) + { + LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName())); + } +#endif + module->TypeNameToTypeIndex[typeName] = TypeIndex; +} + ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType) : ScriptingTypeHandle(module, module->Types.Count()) { @@ -368,7 +396,7 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp // Create native object ScriptingTypeHandle managedTypeHandle = params.Type; const ScriptingType* managedTypePtr = &managedTypeHandle.GetType(); - while (managedTypePtr->Class.Spawn != &ManagedObjectSpawn) + while (managedTypePtr->Script.Spawn != &ManagedObjectSpawn) { managedTypeHandle = managedTypePtr->GetBaseType(); managedTypePtr = &managedTypeHandle.GetType(); @@ -376,12 +404,12 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp ScriptingType& managedType = (ScriptingType&)*managedTypePtr; ScriptingTypeHandle nativeTypeHandle = managedType.GetBaseType(); const ScriptingType* nativeTypePtr = &nativeTypeHandle.GetType(); - while (nativeTypePtr->Class.Spawn == &ManagedObjectSpawn) + while (nativeTypePtr->Script.Spawn == &ManagedObjectSpawn) { nativeTypeHandle = nativeTypePtr->GetBaseType(); nativeTypePtr = &nativeTypeHandle.GetType(); } - ScriptingObject* object = nativeTypePtr->Class.Spawn(params); + ScriptingObject* object = nativeTypePtr->Script.Spawn(params); if (!object) { return nullptr; @@ -392,9 +420,9 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp // We create a custom vtable for the C# objects that use a native class object with virtual functions overrides. // To make it easy to use in C++ we inject custom wrapper methods into C++ object vtable to call C# code from them. // Because virtual member functions calls are C++ ABI and impl-defined this is quite hard. But works. - if (managedType.Class.ScriptVTable) + if (managedType.Script.ScriptVTable) { - if (!managedType.Class.VTable) + if (!managedType.Script.VTable) { // Duplicate vtable void** vtable = *(void***)object; @@ -403,23 +431,23 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp while (vtable[entriesCount] && entriesCount < 200) entriesCount++; const int32 size = entriesCount * sizeof(void*); - managedType.Class.VTable = (void**)((byte*)Platform::Allocate(prefixSize + size, 16) + prefixSize); - Platform::MemoryCopy((byte*)managedType.Class.VTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size); + managedType.Script.VTable = (void**)((byte*)Platform::Allocate(prefixSize + size, 16) + prefixSize); + Platform::MemoryCopy((byte*)managedType.Script.VTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size); // Override vtable entries by the class for (ScriptingTypeHandle e = nativeTypeHandle; e;) { const ScriptingType& eType = e.GetType(); - if (eType.Class.SetupScriptObjectVTable) + if (eType.Script.SetupScriptObjectVTable) { - eType.Class.SetupScriptObjectVTable(managedType.Class.ScriptVTable, managedType.Class.ScriptVTableBase, managedType.Class.VTable, entriesCount, 0); + eType.Script.SetupScriptObjectVTable(managedType.Script.ScriptVTable, managedType.Script.ScriptVTableBase, managedType.Script.VTable, entriesCount, 0); } e = eType.GetBaseType(); } } // Override object vtable with hacked one that has C# functions calls - *(void**)object = managedType.Class.VTable; + *(void**)object = managedType.Script.VTable; } // Mark as managed type @@ -557,7 +585,7 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly) if (baseClassModule->TypeNameToTypeIndex.TryGet(baseClass->GetFullName(), typeIndex)) { nativeType = ScriptingTypeHandle(baseClassModule, typeIndex); - if (nativeType.GetType().Class.Spawn != &ManagedObjectSpawn) + if (nativeType.GetType().Script.Spawn != &ManagedObjectSpawn) break; } baseClass = baseClass->GetBaseClass(); @@ -592,14 +620,14 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly) for (ScriptingTypeHandle e = nativeType; e;) { const ScriptingType& eType = e.GetType(); - if (eType.Class.SetupScriptVTable) + if (eType.Script.SetupScriptVTable) { ASSERT(eType.ManagedClass); - eType.Class.SetupScriptVTable(eType.ManagedClass, type.Class.ScriptVTable, type.Class.ScriptVTableBase); + eType.Script.SetupScriptVTable(eType.ManagedClass, type.Script.ScriptVTable, type.Script.ScriptVTableBase); } e = eType.GetBaseType(); } - MMethod** scriptVTable = (MMethod**)type.Class.ScriptVTable; + MMethod** scriptVTable = (MMethod**)type.Script.ScriptVTable; while (scriptVTable && *scriptVTable) { const MMethod* referenceMethod = *scriptVTable; @@ -657,10 +685,10 @@ void ManagedBinaryModule::OnUnloading(MAssembly* assembly) for (ScriptingType& type : Types) { type.ManagedClass = nullptr; - if (type.Type == ScriptingTypes::Class && type.Class.ScriptVTable) + if (type.Type == ScriptingTypes::Script && type.Script.ScriptVTable) { - Platform::Free(type.Class.ScriptVTable); - type.Class.ScriptVTable = nullptr; + Platform::Free(type.Script.ScriptVTable); + type.Script.ScriptVTable = nullptr; } } ClassToTypeIndex.Clear(); diff --git a/Source/Engine/Scripting/Script.cpp b/Source/Engine/Scripting/Script.cpp index 6b98ba7d9..7b1de3234 100644 --- a/Source/Engine/Scripting/Script.cpp +++ b/Source/Engine/Scripting/Script.cpp @@ -186,9 +186,9 @@ void Script::SetupType() while (typeHandle != Script::TypeInitializer) { auto& type = typeHandle.GetType(); - _tickUpdate |= type.Class.ScriptVTable[8] != nullptr; - _tickLateUpdate |= type.Class.ScriptVTable[9] != nullptr; - _tickFixedUpdate |= type.Class.ScriptVTable[10] != nullptr; + _tickUpdate |= type.Script.ScriptVTable[8] != nullptr; + _tickLateUpdate |= type.Script.ScriptVTable[9] != nullptr; + _tickFixedUpdate |= type.Script.ScriptVTable[10] != nullptr; typeHandle = type.GetBaseType(); } } diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index d7e8c3729..cc3972bd0 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -371,7 +371,7 @@ public: // Create unmanaged object const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex)); - ScriptingObject* obj = scriptingType.Class.Spawn(params); + ScriptingObject* obj = scriptingType.Script.Spawn(params); if (obj == nullptr) { LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass))); @@ -414,7 +414,7 @@ public: // Create unmanaged object const ScriptingObjectSpawnParams params(Guid::New(), type); - ScriptingObject* obj = type.GetType().Class.Spawn(params); + ScriptingObject* obj = type.GetType().Script.Spawn(params); if (obj == nullptr) { LOG(Error, "Failed to spawn object of type \'{0}\'.", String(typeName)); @@ -468,7 +468,7 @@ public: // Create unmanaged object const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex)); - ScriptingObject* obj = scriptingType.Class.Spawn(params); + ScriptingObject* obj = scriptingType.Script.Spawn(params); if (obj == nullptr) { LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass))); diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index 815bb99d1..acde5612f 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -93,9 +93,10 @@ inline uint32 GetHash(const ScriptingTypeHandle& key) /// enum class ScriptingTypes { - Class = 0, + Script = 0, Structure = 1, Enum = 2, + Class = 3, }; /// @@ -193,6 +194,15 @@ struct FLAXENGINE_API ScriptingType /// The default instance of the scripting type. Used by serialization system for comparision to save only modified properties of the object. /// mutable ScriptingObject* DefaultInstance; + } Script; + + struct + { + // Class constructor method pointer + Ctor Ctor; + + // Class destructor method pointer + Dtor Dtor; } Class; struct @@ -223,6 +233,7 @@ struct FLAXENGINE_API ScriptingType ScriptingType(); ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, const ScriptingTypeHandle& baseType, SetupScriptVTableHandler setupScriptVTable = nullptr, SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr); ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime = DefaultInitRuntime, SpawnHandler spawn = DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, SetupScriptVTableHandler setupScriptVTable = nullptr, SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr); + ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType); ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType); ScriptingType(const ScriptingType& other); ScriptingType(ScriptingType&& other); @@ -268,6 +279,7 @@ struct FLAXENGINE_API ScriptingType struct FLAXENGINE_API ScriptingTypeInitializer : ScriptingTypeHandle { ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime = ScriptingType::DefaultInitRuntime, ScriptingType::SpawnHandler spawn = ScriptingType::DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, ScriptingType::SetupScriptVTableHandler setupScriptVTable = nullptr, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr); + ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType = nullptr); ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType = nullptr); }; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index e64240b9e..026ffdbbb 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -818,7 +818,7 @@ namespace Flax.Build.Bindings contents.AppendLine(" ScriptingTypeHandle managedTypeHandle = object->GetTypeHandle();"); contents.AppendLine(" const ScriptingType* managedTypePtr = &managedTypeHandle.GetType();"); - contents.AppendLine(" while (managedTypePtr->Class.Spawn != &ManagedBinaryModule::ManagedObjectSpawn)"); + contents.AppendLine(" while (managedTypePtr->Script.Spawn != &ManagedBinaryModule::ManagedObjectSpawn)"); contents.AppendLine(" {"); contents.AppendLine(" managedTypeHandle = managedTypePtr->GetBaseType();"); contents.AppendLine(" managedTypePtr = &managedTypeHandle.GetType();"); @@ -827,7 +827,7 @@ namespace Flax.Build.Bindings contents.AppendLine(" if (IsDuringWrapperCall)"); contents.AppendLine(" {"); contents.AppendLine(" // Prevent stack overflow by calling native base method"); - contents.AppendLine(" const auto scriptVTableBase = managedTypePtr->Class.ScriptVTableBase;"); + contents.AppendLine(" const auto scriptVTableBase = managedTypePtr->Script.ScriptVTableBase;"); contents.Append($" return (object->**({functionInfo.UniqueName}_Signature*)&scriptVTableBase[{scriptVTableIndex} + 2])("); separator = false; for (var i = 0; i < functionInfo.Parameters.Count; i++) @@ -840,7 +840,7 @@ namespace Flax.Build.Bindings } contents.AppendLine(");"); contents.AppendLine(" }"); - contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Class.ScriptVTable;"); + contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;"); contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableIndex}]);"); contents.AppendLine($" auto method = scriptVTable[{scriptVTableIndex}];"); contents.AppendLine(" MonoObject* exception = nullptr;"); diff --git a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs index 4d485011b..803f40142 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs @@ -48,7 +48,7 @@ namespace Flax.Build.Plugins contents.AppendLine(" if (IsDuringWrapperCall)"); contents.AppendLine(" {"); contents.AppendLine(" // Prevent stack overflow by calling base method"); - contents.AppendLine(" const auto scriptVTableBase = object->GetType().Class.ScriptVTableBase;"); + contents.AppendLine(" const auto scriptVTableBase = object->GetType().Script.ScriptVTableBase;"); contents.Append($" return (object->**({functionInfo.UniqueName}_Signature*)&scriptVTableBase[{scriptVTableIndex} + 2])("); separator = false; for (var i = 0; i < functionInfo.Parameters.Count; i++) @@ -61,7 +61,7 @@ namespace Flax.Build.Plugins } contents.AppendLine(");"); contents.AppendLine(" }"); - contents.AppendLine(" auto scriptVTable = (VisualScript::Method**)object->GetType().Class.ScriptVTable;"); + contents.AppendLine(" auto scriptVTable = (VisualScript::Method**)object->GetType().Script.ScriptVTable;"); contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableIndex}]);"); if (functionInfo.Parameters.Count != 0) From 16cd75d1792e5b148fd7841312906ae9cecb515c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 22 Dec 2020 12:57:00 +0100 Subject: [PATCH 015/419] Fix code style --- Source/Engine/Core/Config/GameSettings.cpp | 10 ++---- .../GraphicsDevice/Vulkan/CmdBufferVulkan.cpp | 4 +-- Source/Engine/GraphicsDevice/Vulkan/Config.h | 11 ------ .../Vulkan/GPUAdapterVulkan.cpp | 13 ------- .../GraphicsDevice/Vulkan/GPUAdapterVulkan.h | 12 ------- .../Vulkan/GPUDeviceVulkan.Layers.cpp | 7 ---- .../Vulkan/GPUSwapChainVulkan.cpp | 24 ------------- .../Vulkan/GPUSwapChainVulkan.h | 9 ----- .../Vulkan/Linux/LinuxVulkanPlatform.h | 1 - .../GraphicsDevice/Vulkan/RenderToolsVulkan.h | 34 +++++++------------ 10 files changed, 17 insertions(+), 108 deletions(-) diff --git a/Source/Engine/Core/Config/GameSettings.cpp b/Source/Engine/Core/Config/GameSettings.cpp index ea4fef45f..fc629e001 100644 --- a/Source/Engine/Core/Config/GameSettings.cpp +++ b/Source/Engine/Core/Config/GameSettings.cpp @@ -78,13 +78,10 @@ bool GameSettings::Load() if (id.IsValid()) \ { \ AssetReference subAsset = Content::LoadAsync(id); \ - if (subAsset) \ + if (subAsset && !subAsset->WaitForLoaded()) \ { \ - if (!subAsset->WaitForLoaded()) \ - { \ - settingsType::Instance()->Deserialize(*subAsset->Data, nullptr); \ - settingsType::Instance()->Apply(); \ - } \ + settingsType::Instance()->Deserialize(*subAsset->Data, nullptr); \ + settingsType::Instance()->Apply(); \ } \ else \ { LOG(Warning, "Cannot load " nodeName " settings"); } \ @@ -143,7 +140,6 @@ bool GameSettings::Load() LOAD_SETTINGS("Navigation", NavigationSettings); // Load platform settings - // TODO: refactor platform settings impl to be more modular (better multi-platform handling) #if PLATFORM_WINDOWS LOAD_SETTINGS("WindowsPlatform", WindowsPlatformSettings); #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp index a2dbb7733..a143f7dd8 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp @@ -24,14 +24,12 @@ void CmdBufferVulkan::Begin() RenderToolsVulkan::ZeroStruct(beginInfo, VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; VALIDATE_VULKAN_RESULT(vkBeginCommandBuffer(_commandBufferHandle, &beginInfo)); - -#if VULKAN_USE_DESCRIPTOR_POOL_MANAGER + // Acquire a descriptor pool set on if (_descriptorPoolSetContainer == nullptr) { AcquirePoolSet(); } -#endif _state = State::IsInsideBegin; diff --git a/Source/Engine/GraphicsDevice/Vulkan/Config.h b/Source/Engine/GraphicsDevice/Vulkan/Config.h index 28ba0bc7b..a3bb1662c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Config.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Config.h @@ -23,29 +23,18 @@ /// #define VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT 10 -// Enables the VK_LAYER_LUNARG_api_dump layer and the report VK_DEBUG_REPORT_INFORMATION_BIT_EXT flag #define VULKAN_ENABLE_API_DUMP 0 - #define VULKAN_RESET_QUERY_POOLS 0 - #define VULKAN_HASH_POOLS_WITH_TYPES_USAGE_ID 1 #ifndef VULKAN_USE_DEBUG_LAYER #define VULKAN_USE_DEBUG_LAYER GPU_ENABLE_DIAGNOSTICS #endif -#ifndef VULKAN_USE_DESCRIPTOR_POOL_MANAGER -#define VULKAN_USE_DESCRIPTOR_POOL_MANAGER 1 -#endif - #ifndef VULKAN_HAS_PHYSICAL_DEVICE_PROPERTIES2 #define VULKAN_HAS_PHYSICAL_DEVICE_PROPERTIES2 0 #endif -#ifndef VULKAN_ENABLE_DESKTOP_HMD_SUPPORT -#define VULKAN_ENABLE_DESKTOP_HMD_SUPPORT 0 -#endif - #ifdef VK_KHR_maintenance1 #define VULKAN_SUPPORTS_MAINTENANCE_LAYER1 1 #else diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.cpp index 982878ce0..4d451fa52 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.cpp @@ -4,24 +4,11 @@ #include "GPUAdapterVulkan.h" #include "GPUDeviceVulkan.h" -#include "RenderToolsVulkan.h" GPUAdapterVulkan::GPUAdapterVulkan(VkPhysicalDevice gpu) : Gpu(gpu) { - // Query device information vkGetPhysicalDeviceProperties(gpu, &GpuProps); -#if VULKAN_ENABLE_DESKTOP_HMD_SUPPORT - if (GPUDeviceVulkan::OptionalDeviceExtensions.HasKHRGetPhysicalDeviceProperties2) - { - VkPhysicalDeviceProperties2KHR GpuProps2; - RenderToolsVulkan::ZeroStruct(GpuProps2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR); - GpuProps2.pNext = &GpuProps2; - RenderToolsVulkan::ZeroStruct(GpuIdProps, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR); - vkGetPhysicalDeviceProperties2KHR(Gpu, &GpuProps2); - } -#endif - Description = GpuProps.deviceName; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h index 559eea7ce..17a9212e7 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUAdapterVulkan.h @@ -33,9 +33,6 @@ public: { Gpu = other.Gpu; GpuProps = other.GpuProps; -#if VULKAN_ENABLE_DESKTOP_HMD_SUPPORT - GpuIdProps = other.GpuIdProps; -#endif Description = other.Description; return *this; } @@ -58,15 +55,6 @@ public: /// VkPhysicalDeviceProperties GpuProps; -#if VULKAN_ENABLE_DESKTOP_HMD_SUPPORT - - /// - /// The GPU device extended properties. - /// - VkPhysicalDeviceIDPropertiesKHR GpuIdProps; - -#endif - /// /// The GPU description. /// diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp index 65451ca81..88c30d660 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp @@ -34,10 +34,6 @@ static const char* GValidationLayers[] = static const char* GInstanceExtensions[] = { -#if VULKAN_ENABLE_DESKTOP_HMD_SUPPORT - VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, - VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, -#endif #if VULKAN_SUPPORTS_VALIDATION_CACHE VK_EXT_VALIDATION_CACHE_EXTENSION_NAME, #endif @@ -50,9 +46,6 @@ static const char* GDeviceExtensions[] = #if VULKAN_SUPPORTS_MAINTENANCE_LAYER1 VK_KHR_MAINTENANCE1_EXTENSION_NAME, #endif -#if VULKAN_SUPPORTS_MAINTENANCE_LAYER2 - VK_KHR_MAINTENANCE2_EXTENSION_NAME, -#endif #if VULKAN_SUPPORTS_VALIDATION_CACHE VK_EXT_VALIDATION_CACHE_EXTENSION_NAME, #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index ca8af3873..6354e9956 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -17,9 +17,6 @@ void BackBufferVulkan::Setup(GPUSwapChainVulkan* window, VkImage backbuffer, Pix Device = window->GetDevice(); Handle.Init(window->GetDevice(), this, backbuffer, 1, format, MSAALevel::None, extent, VK_IMAGE_VIEW_TYPE_2D); -#if VULKAN_USE_IMAGE_ACQUIRE_FENCES - ImageAcquiredFence = Device->FenceManager.AllocateFence(true); -#endif RenderingDoneSemaphore = New(Device); ImageAcquiredSemaphore = New(Device); } @@ -27,9 +24,6 @@ void BackBufferVulkan::Setup(GPUSwapChainVulkan* window, VkImage backbuffer, Pix void BackBufferVulkan::Release() { Handle.Release(); -#if VULKAN_USE_IMAGE_ACQUIRE_FENCES - Device->FenceManager.ReleaseFence(ImageAcquiredFence); -#endif Delete(RenderingDoneSemaphore); Delete(ImageAcquiredSemaphore); } @@ -509,19 +503,9 @@ int32 GPUSwapChainVulkan::AcquireImageIndex(SemaphoreVulkan** outSemaphore) { ASSERT(_swapChain && _backBuffers.HasItems()); - // Get the index of the next swap chain image to render to. - // Wait with an "infinite" timeout, the function will block until an image is ready. - // The semaphore will get signaled when the image is ready (upon function return). - uint32 imageIndex = 0; const int32 prevSemaphoreIndex = _semaphoreIndex; _semaphoreIndex = (_semaphoreIndex + 1) % _backBuffers.Count(); - -#if VULKAN_USE_IMAGE_ACQUIRE_FENCES - const auto fence = _backBuffers[_semaphoreIndex].ImageAcquiredFence; - _device->FenceManager.ResetFence(fence); -#endif - const auto semaphore = _backBuffers[_semaphoreIndex].ImageAcquiredSemaphore; const VkResult result = vkAcquireNextImageKHR( @@ -529,11 +513,7 @@ int32 GPUSwapChainVulkan::AcquireImageIndex(SemaphoreVulkan** outSemaphore) _swapChain, UINT64_MAX, semaphore->GetHandle(), -#if VULKAN_USE_IMAGE_ACQUIRE_FENCES - fence->GetHandle(), -#else VK_NULL_HANDLE, -#endif &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) @@ -562,10 +542,6 @@ int32 GPUSwapChainVulkan::AcquireImageIndex(SemaphoreVulkan** outSemaphore) } _currentImageIndex = (int32)imageIndex; -#if VULKAN_USE_IMAGE_ACQUIRE_FENCES - ASSERT(_device->FenceManager.WaitForFence(fence, UINT64_MAX)); -#endif - return _currentImageIndex; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h index 93ff126d9..36204581c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h @@ -30,15 +30,6 @@ public: /// SemaphoreVulkan* RenderingDoneSemaphore; -#if VULKAN_USE_IMAGE_ACQUIRE_FENCES - - /// - /// The image acquired fence handle. - /// - FenceVulkan* ImageAcquiredFence; - -#endif - /// /// The render target surface handle. /// diff --git a/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h b/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h index 66e5a608d..070d3a9c9 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h +++ b/Source/Engine/GraphicsDevice/Vulkan/Linux/LinuxVulkanPlatform.h @@ -8,7 +8,6 @@ #define VULKAN_USE_DEBUG_LAYER GPU_ENABLE_DIAGNOSTICS #define VULKAN_HAS_PHYSICAL_DEVICE_PROPERTIES2 1 -#define VULKAN_ENABLE_DESKTOP_HMD_SUPPORT 0 /// /// The implementation for the Vulkan API support for Linux platform. diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h index 02dd2c68d..c0c45b636 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h @@ -78,40 +78,32 @@ public: case 0: stageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; break; - case VK_ACCESS_TRANSFER_WRITE_BIT: stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT; break; - case VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT: stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; break; - case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT: stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; - + case VK_ACCESS_TRANSFER_READ_BIT: + stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + case VK_ACCESS_SHADER_READ_BIT: + case VK_ACCESS_SHADER_WRITE_BIT: + stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; #if VULKAN_SUPPORTS_MAINTENANCE_LAYER2 case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT: case VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT: stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; #endif - - case VK_ACCESS_TRANSFER_READ_BIT: - stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT; - break; - - case VK_ACCESS_SHADER_READ_BIT: - case VK_ACCESS_SHADER_WRITE_BIT: - stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - default: CRASH; break; } - return stageFlags; } @@ -136,12 +128,6 @@ public: accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; break; -#if VULKAN_SUPPORTS_MAINTENANCE_LAYER2 - case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR: - accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - break; -#endif case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: accessFlags = VK_ACCESS_TRANSFER_READ_BIT; stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT; @@ -158,6 +144,12 @@ public: accessFlags = VK_ACCESS_SHADER_READ_BIT; stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; +#if VULKAN_SUPPORTS_MAINTENANCE_LAYER2 + case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR: + accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; +#endif case VK_IMAGE_LAYOUT_GENERAL: accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; From ce6c360d29ee44494193e3e03e93bd94852b02c8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 23 Dec 2020 12:55:53 +0100 Subject: [PATCH 016/419] Add proper typeInfo generation for non-scripting API classes --- .../Bindings/BindingsGenerator.Cpp.cs | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 026ffdbbb..d7711a13a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1046,7 +1046,7 @@ namespace Flax.Build.Bindings var classTypeNameInternal = classInfo.NativeName; if (classInfo.Parent != null && !(classInfo.Parent is FileInfo)) classTypeNameInternal = classInfo.Parent.FullNameNative + '_' + classTypeNameInternal; - var useUnmanaged = classInfo.IsStatic || classInfo.IsScriptingObject; + var useScripting = classInfo.IsStatic || classInfo.IsScriptingObject; if (classInfo.IsAutoSerialization) GenerateCppAutoSerialization(buildData, contents, moduleInfo, classInfo, classTypeNameNative); @@ -1059,7 +1059,7 @@ namespace Flax.Build.Bindings // Events foreach (var eventInfo in classInfo.Events) { - if (!useUnmanaged) + if (!useScripting) continue; CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h"); @@ -1131,7 +1131,7 @@ namespace Flax.Build.Bindings // Fields foreach (var fieldInfo in classInfo.Fields) { - if (!useUnmanaged) + if (!useScripting) continue; if (fieldInfo.Getter != null) GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Getter, "{0}"); @@ -1142,7 +1142,7 @@ namespace Flax.Build.Bindings // Properties foreach (var propertyInfo in classInfo.Properties) { - if (!useUnmanaged) + if (!useScripting) continue; if (propertyInfo.Getter != null) GenerateCppWrapperFunction(buildData, contents, classInfo, propertyInfo.Getter); @@ -1153,7 +1153,7 @@ namespace Flax.Build.Bindings // Functions foreach (var functionInfo in classInfo.Functions) { - if (!useUnmanaged) + if (!useScripting) throw new Exception($"Not supported function {functionInfo.Name} inside non-static and non-scripting class type {classInfo.Name}."); GenerateCppWrapperFunction(buildData, contents, classInfo, functionInfo); } @@ -1264,7 +1264,7 @@ namespace Flax.Build.Bindings // Runtime initialization (internal methods binding) contents.AppendLine(" static void InitRuntime()"); contents.AppendLine(" {"); - if (useUnmanaged) + if (useScripting) { foreach (var eventInfo in classInfo.Events) { @@ -1291,10 +1291,24 @@ namespace Flax.Build.Bindings } GenerateCppClassInitRuntime?.Invoke(buildData, classInfo, contents); - contents.AppendLine(" }"); - contents.Append('}'); - contents.Append(';'); - contents.AppendLine(); + contents.AppendLine(" }").AppendLine(); + + if (!useScripting) + { + // Constructor + contents.AppendLine(" static void Ctor(void* ptr)"); + contents.AppendLine(" {"); + contents.AppendLine($" new(ptr){classTypeNameNative}();"); + contents.AppendLine(" }").AppendLine(); + + // Destructor + contents.AppendLine(" static void Dtor(void* ptr)"); + contents.AppendLine(" {"); + contents.AppendLine($" (({classTypeNameNative}*)ptr)->~{classInfo.NativeName}();"); + contents.AppendLine(" }").AppendLine(); + } + + contents.Append('}').Append(';').AppendLine(); contents.AppendLine(); // Type initializer @@ -1302,15 +1316,22 @@ namespace Flax.Build.Bindings contents.Append($"StringAnsiView(\"{classTypeNameManaged}\", {classTypeNameManaged.Length}), "); contents.Append($"sizeof({classTypeNameNative}), "); contents.Append($"&{classTypeNameInternal}Internal::InitRuntime, "); - if (!classInfo.IsStatic && !classInfo.NoSpawn && useUnmanaged) - contents.Append($"(ScriptingType::SpawnHandler)&{classTypeNameNative}::Spawn, "); + if (useScripting) + { + if (classInfo.IsStatic || classInfo.NoSpawn) + contents.Append("&ScriptingType::DefaultSpawn, "); + else + contents.Append($"(ScriptingType::SpawnHandler)&{classTypeNameNative}::Spawn, "); + if (classInfo.BaseType != null && useScripting) + contents.Append($"&{classInfo.BaseType}::TypeInitializer, "); + else + contents.Append("nullptr, "); + contents.Append(setupScriptVTable); + } else - contents.Append("&ScriptingType::DefaultSpawn, "); - if (classInfo.BaseType != null && useUnmanaged) - contents.Append($"&{classInfo.BaseType}::TypeInitializer, "); - else - contents.Append("nullptr, "); - contents.Append(setupScriptVTable); + { + contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor"); + } contents.Append(");"); contents.AppendLine(); From e242dbf89fb48135a0a991d428da17a58cadcce0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 23 Dec 2020 12:58:31 +0100 Subject: [PATCH 017/419] Add missing code for ScriptingType copy/move --- Source/Engine/Scripting/BinaryModule.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 4c287f2cb..862b95347 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -170,6 +170,10 @@ ScriptingType::ScriptingType(const ScriptingType& other) Struct.GetField = other.Struct.GetField; Struct.SetField = other.Struct.SetField; break; + case ScriptingTypes::Class: + Class.Ctor = other.Class.Ctor; + Class.Dtor = other.Class.Dtor; + break; case ScriptingTypes::Enum: break; default: ; @@ -210,6 +214,10 @@ ScriptingType::ScriptingType(ScriptingType&& other) Struct.GetField = other.Struct.GetField; Struct.SetField = other.Struct.SetField; break; + case ScriptingTypes::Class: + Class.Ctor = other.Class.Ctor; + Class.Dtor = other.Class.Dtor; + break; case ScriptingTypes::Enum: break; default: ; From 0f2e579674f7de206596d5698be13cb537a1c2c0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 2 Jan 2021 12:51:32 +0100 Subject: [PATCH 018/419] Create BatteryInfo struct. --- Source/Engine/Platform/BatteryInfo.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Source/Engine/Platform/BatteryInfo.h diff --git a/Source/Engine/Platform/BatteryInfo.h b/Source/Engine/Platform/BatteryInfo.h new file mode 100644 index 000000000..c46bde59e --- /dev/null +++ b/Source/Engine/Platform/BatteryInfo.h @@ -0,0 +1,16 @@ +// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" + +API_STRUCT() struct BatteryInfo +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(BatteryInfo); + + API_FIELD() byte ACLineStatus; + + API_FIELD() byte BatteryLifePercent; + + API_FIELD() uint32 BatteryLifeTime; +}; From a4607385fd604e328c332e925d4ba0124743f4c5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 2 Jan 2021 12:53:20 +0100 Subject: [PATCH 019/419] Adding GetBatteryInfo() to PlatformBase. --- Source/Engine/Platform/Base/PlatformBase.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h index f0a2baf43..86e760b3c 100644 --- a/Source/Engine/Platform/Base/PlatformBase.h +++ b/Source/Engine/Platform/Base/PlatformBase.h @@ -11,6 +11,7 @@ struct CPUInfo; struct MemoryStats; struct ProcessMemoryStats; struct CreateWindowSettings; +struct BatteryInfo; // ReSharper disable CppFunctionIsNotImplemented @@ -548,6 +549,11 @@ public: /// static void SetHighDpiAwarenessEnabled(bool enable); + /// + /// Gets the battery information. + /// + API_PROPERTY() static BatteryInfo GetBatteryInfo(); + /// /// Gets the screen DPI setting. /// From 624ab4b8dd9e527c06a0c6d455015d8bfc795065 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 2 Jan 2021 12:54:16 +0100 Subject: [PATCH 020/419] Implementing GetBatteryInfo() to Win32Platform. --- Source/Engine/Platform/Win32/Win32Platform.cpp | 13 +++++++++++++ Source/Engine/Platform/Win32/Win32Platform.h | 1 + 2 files changed, 14 insertions(+) diff --git a/Source/Engine/Platform/Win32/Win32Platform.cpp b/Source/Engine/Platform/Win32/Win32Platform.cpp index 5bf724de2..317b6f232 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.cpp +++ b/Source/Engine/Platform/Win32/Win32Platform.cpp @@ -5,6 +5,7 @@ #include "Engine/Platform/Platform.h" #include "Engine/Platform/MemoryStats.h" #include "Engine/Platform/CPUInfo.h" +#include "Engine/Platform/BatteryInfo.h" #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Math/Math.h" @@ -15,6 +16,7 @@ #include #include #include +#include #pragma comment(lib, "Iphlpapi.lib") namespace @@ -308,6 +310,17 @@ bool Win32Platform::Is64BitPlatform() #endif } +BatteryInfo Win32Platform::GetBatteryInfo() +{ + BatteryInfo info; + SYSTEM_POWER_STATUS status; + GetSystemPowerStatus(&status); + info.ACLineStatus = status.ACLineStatus; + info.BatteryLifePercent = status.BatteryLifePercent; + info.BatteryLifeTime = status.BatteryLifeTime; + return info; +} + CPUInfo Win32Platform::GetCPUInfo() { return CpuInfo; diff --git a/Source/Engine/Platform/Win32/Win32Platform.h b/Source/Engine/Platform/Win32/Win32Platform.h index dd584cea9..c162c53e6 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.h +++ b/Source/Engine/Platform/Win32/Win32Platform.h @@ -43,6 +43,7 @@ public: _aligned_free(ptr); } static bool Is64BitPlatform(); + static BatteryInfo GetBatteryInfo(); static CPUInfo GetCPUInfo(); static int32 GetCacheLineSize(); static MemoryStats GetMemoryStats(); From e9f72dbbbffbc6d12569a31bd9268ee81f15e10d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 2 Jan 2021 13:15:15 +0100 Subject: [PATCH 021/419] Adding ACLineStatus enum. --- Source/Engine/Platform/BatteryInfo.h | 33 ++++++++++++++++++- .../Engine/Platform/Win32/Win32Platform.cpp | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/BatteryInfo.h b/Source/Engine/Platform/BatteryInfo.h index c46bde59e..ba05d7992 100644 --- a/Source/Engine/Platform/BatteryInfo.h +++ b/Source/Engine/Platform/BatteryInfo.h @@ -4,13 +4,44 @@ #include "Engine/Core/Types/BaseTypes.h" +/// +/// Power supply status. +/// +API_ENUM() enum class ACLineStatus : byte +{ + /// + /// Power supply is not connected. + /// + Offline = 0, + /// + /// Power supply is connected. + /// + Online = 1, + /// + /// Unknown status. + /// + Unknown = 255 +}; + +/// +/// Contains information about power supply (Battery). +/// API_STRUCT() struct BatteryInfo { DECLARE_SCRIPTING_TYPE_MINIMAL(BatteryInfo); - API_FIELD() byte ACLineStatus; + /// + /// Power supply status. + /// + API_FIELD() ACLineStatus ACLineStatus; + /// + /// Battery percentage left. + /// API_FIELD() byte BatteryLifePercent; + /// + /// Remaining battery life time in second. + /// API_FIELD() uint32 BatteryLifeTime; }; diff --git a/Source/Engine/Platform/Win32/Win32Platform.cpp b/Source/Engine/Platform/Win32/Win32Platform.cpp index 317b6f232..ee73ffac2 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.cpp +++ b/Source/Engine/Platform/Win32/Win32Platform.cpp @@ -315,7 +315,7 @@ BatteryInfo Win32Platform::GetBatteryInfo() BatteryInfo info; SYSTEM_POWER_STATUS status; GetSystemPowerStatus(&status); - info.ACLineStatus = status.ACLineStatus; + info.ACLineStatus = (ACLineStatus)status.ACLineStatus; info.BatteryLifePercent = status.BatteryLifePercent; info.BatteryLifeTime = status.BatteryLifeTime; return info; From 3ed2e010fa1754bfeb8426d489384a6ed0d16f30 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 2 Jan 2021 14:55:30 +0100 Subject: [PATCH 022/419] Bumping new file copyright to 2021. --- Source/Engine/Platform/BatteryInfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/BatteryInfo.h b/Source/Engine/Platform/BatteryInfo.h index ba05d7992..146f3f562 100644 --- a/Source/Engine/Platform/BatteryInfo.h +++ b/Source/Engine/Platform/BatteryInfo.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #pragma once From 21ec0f103eb2ce3fa4e7620a71c45e50786cd141 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 2 Jan 2021 18:41:00 +0100 Subject: [PATCH 023/419] Implementing GetBatteryInfo() in PlatformBase. --- Source/Engine/Platform/Base/PlatformBase.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index 2a0e23aea..d53ff8acc 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -19,6 +19,7 @@ #include "Engine/Engine/CommandLine.h" #include "Engine/Engine/Engine.h" #include "Engine/Utilities/StringConverter.h" +#include "Engine/Platform/BatteryInfo.h" #include // Check types sizes @@ -381,6 +382,11 @@ void PlatformBase::SetHighDpiAwarenessEnabled(bool enable) { } +BatteryInfo PlatformBase::GetBatteryInfo() +{ + return BatteryInfo(); +} + int32 PlatformBase::GetDpi() { return 96; From 10cdbc8fbc1fab67b0dbcac39afd1c88b02a0af9 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Mon, 4 Jan 2021 12:33:29 +0100 Subject: [PATCH 024/419] Add dragging existing connection from input box --- Source/Editor/Surface/Elements/Box.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 886ab1e37..9be0dfd97 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -565,7 +565,18 @@ namespace FlaxEditor.Surface.Elements { _isMouseDown = false; if (Surface.CanEdit) - Surface.ConnectingStart(this); + { + if (!IsOutput && HasSingleConnection) + { + var inputBox = Connections[0]; + BreakConnection(inputBox); + Surface.ConnectingStart(inputBox); + } + else + { + Surface.ConnectingStart(this); + } + } } base.OnMouseLeave(); } From 8dc5b11f5172dafc4cfb09fec769dd7e240ad0b3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 4 Jan 2021 14:18:59 +0100 Subject: [PATCH 025/419] Add `API_INTERFACE` to scripting API bindings for implementing interfaces --- Source/Engine/Core/Config.h | 2 + Source/Engine/Core/Types/BaseTypes.h | 6 +- Source/Engine/Scripting/BinaryModule.cpp | 87 +++++- Source/Engine/Scripting/ScriptingType.h | 54 ++-- .../Tools/Flax.Build/Bindings/ApiTypeInfo.cs | 1 + .../Bindings/BindingsGenerator.Api.cs | 2 + .../Bindings/BindingsGenerator.Cpp.cs | 67 ++++- .../Bindings/BindingsGenerator.Parsing.cs | 262 +++++++++++------- .../Flax.Build/Bindings/BindingsGenerator.cs | 10 + Source/Tools/Flax.Build/Bindings/ClassInfo.cs | 9 +- .../Flax.Build/Bindings/ClassStructInfo.cs | 40 +++ .../Flax.Build/Bindings/InterfaceInfo.cs | 24 ++ .../Flax.Build/Bindings/StructureInfo.cs | 6 +- Source/Tools/Flax.Build/Flax.Build.csproj | 2 + 14 files changed, 421 insertions(+), 151 deletions(-) create mode 100644 Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs create mode 100644 Source/Tools/Flax.Build/Bindings/InterfaceInfo.cs diff --git a/Source/Engine/Core/Config.h b/Source/Engine/Core/Config.h index 152a170b2..55fae1cd4 100644 --- a/Source/Engine/Core/Config.h +++ b/Source/Engine/Core/Config.h @@ -44,6 +44,7 @@ // Scripting API defines (see C++ scripting documentation for more info) #define API_ENUM(...) #define API_CLASS(...) +#define API_INTERFACE(...) #define API_STRUCT(...) #define API_FUNCTION(...) #define API_PROPERTY(...) @@ -52,3 +53,4 @@ #define API_PARAM(...) #define API_INJECT_CPP_CODE(...) #define API_AUTO_SERIALIZATION(...) public: void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; +#define DECLARE_SCRIPTING_TYPE_MINIMAL(type) public: friend class type##Internal; static struct ScriptingTypeInitializer TypeInitializer; diff --git a/Source/Engine/Core/Types/BaseTypes.h b/Source/Engine/Core/Types/BaseTypes.h index 63638e304..580728b2c 100644 --- a/Source/Engine/Core/Types/BaseTypes.h +++ b/Source/Engine/Core/Types/BaseTypes.h @@ -108,7 +108,5 @@ class Dictionary; inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); } \ inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); } -#define DECLARE_SCRIPTING_TYPE_MINIMAL(type) \ - public: \ - friend class type##Internal; \ - static struct ScriptingTypeInitializer TypeInitializer; +// Returns byte offset from the object pointer in vtable to the begin of the given inherited type implementation +#define VTABLE_OFFSET(type, baseType) (((intptr)static_cast((type*)1))-1) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 862b95347..e3688949b 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -62,6 +62,7 @@ ScriptingType::ScriptingType() , Fullname(nullptr, 0) , Type(ScriptingTypes::Script) , BaseTypePtr(nullptr) + , Interfaces(nullptr) { Script.Spawn = nullptr; Script.VTable = nullptr; @@ -72,7 +73,7 @@ ScriptingType::ScriptingType() Script.DefaultInstance = nullptr; } -ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, const ScriptingTypeHandle& baseType, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable) +ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, const ScriptingTypeHandle& baseType, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable, const InterfaceImplementation* interfaces) : ManagedClass(nullptr) , Module(module) , InitRuntime(initRuntime) @@ -80,6 +81,7 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul , Type(ScriptingTypes::Script) , BaseTypeHandle(baseType) , BaseTypePtr(nullptr) + , Interfaces(interfaces) , Size(size) { Script.Spawn = spawn; @@ -91,13 +93,14 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul Script.DefaultInstance = nullptr; } -ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, ScriptingTypeInitializer* baseType, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable) +ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, ScriptingTypeInitializer* baseType, SetupScriptVTableHandler setupScriptVTable, SetupScriptObjectVTableHandler setupScriptObjectVTable, const InterfaceImplementation* interfaces) : ManagedClass(nullptr) , Module(module) , InitRuntime(initRuntime) , Fullname(fullname) , Type(ScriptingTypes::Script) , BaseTypePtr(baseType) + , Interfaces(interfaces) , Size(size) { Script.Spawn = spawn; @@ -109,26 +112,28 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul Script.DefaultInstance = nullptr; } -ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType) +ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces) : ManagedClass(nullptr) , Module(module) , InitRuntime(initRuntime) , Fullname(fullname) , Type(ScriptingTypes::Class) , BaseTypePtr(baseType) + , Interfaces(interfaces) , Size(size) { Class.Ctor = ctor; Class.Dtor = dtor; } -ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType) +ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces) : ManagedClass(nullptr) , Module(module) , InitRuntime(initRuntime) , Fullname(fullname) , Type(ScriptingTypes::Structure) , BaseTypePtr(baseType) + , Interfaces(interfaces) , Size(size) { Struct.Ctor = ctor; @@ -140,6 +145,18 @@ ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* modul Struct.SetField = setField; } +ScriptingType::ScriptingType(const StringAnsiView& fullname, BinaryModule* module, InitRuntimeHandler initRuntime, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces) + : ManagedClass(nullptr) + , Module(module) + , InitRuntime(initRuntime) + , Fullname(fullname) + , Type(ScriptingTypes::Interface) + , BaseTypePtr(baseType) + , Interfaces(interfaces) + , Size(0) +{ +} + ScriptingType::ScriptingType(const ScriptingType& other) : ManagedClass(other.ManagedClass) , Module(other.Module) @@ -148,6 +165,7 @@ ScriptingType::ScriptingType(const ScriptingType& other) , Type(other.Type) , BaseTypeHandle(other.BaseTypeHandle) , BaseTypePtr(other.BaseTypePtr) + , Interfaces(other.Interfaces) , Size(other.Size) { switch (other.Type) @@ -176,6 +194,8 @@ ScriptingType::ScriptingType(const ScriptingType& other) break; case ScriptingTypes::Enum: break; + case ScriptingTypes::Interface: + break; default: ; } } @@ -188,6 +208,7 @@ ScriptingType::ScriptingType(ScriptingType&& other) , Type(other.Type) , BaseTypeHandle(other.BaseTypeHandle) , BaseTypePtr(other.BaseTypePtr) + , Interfaces(other.Interfaces) , Size(other.Size) { switch (other.Type) @@ -220,6 +241,8 @@ ScriptingType::ScriptingType(ScriptingType&& other) break; case ScriptingTypes::Enum: break; + case ScriptingTypes::Interface: + break; default: ; } } @@ -240,6 +263,8 @@ ScriptingType::~ScriptingType() break; case ScriptingTypes::Enum: break; + case ScriptingTypes::Interface: + break; default: ; } } @@ -269,16 +294,40 @@ ScriptingObject* ScriptingType::GetDefaultInstance() const return Script.DefaultInstance; } +const ScriptingType::InterfaceImplementation* ScriptingType::GetInterface(const ScriptingTypeInitializer* interfaceType) const +{ + const InterfaceImplementation* interfaces = Interfaces; + if (interfaces) + { + while (interfaces->InterfaceType) + { + if (interfaces->InterfaceType == interfaceType) + return interfaces; + interfaces++; + } + } + if (BaseTypeHandle) + { + return BaseTypeHandle.GetType().GetInterface(interfaceType); + } + if (BaseTypePtr) + { + return BaseTypePtr->GetType().GetInterface(interfaceType); + } + return nullptr; +} + String ScriptingType::ToString() const { return String(Fullname.Get(), Fullname.Length()); } -ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SpawnHandler spawn, ScriptingTypeInitializer* baseType, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable) +ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::SpawnHandler spawn, ScriptingTypeInitializer* baseType, ScriptingType::SetupScriptVTableHandler setupScriptVTable, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable, const ScriptingType::InterfaceImplementation* interfaces) : ScriptingTypeHandle(module, module->Types.Count()) { + // Script module->Types.AddUninitialized(); - new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, spawn, baseType, setupScriptVTable, setupScriptObjectVTable); + new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, spawn, baseType, setupScriptVTable, setupScriptObjectVTable, interfaces); const MString typeName(fullname.Get(), fullname.Length()); #if BUILD_DEBUG if (module->TypeNameToTypeIndex.ContainsKey(typeName)) @@ -289,11 +338,12 @@ ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const S module->TypeNameToTypeIndex[typeName] = TypeIndex; } -ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType) +ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces) : ScriptingTypeHandle(module, module->Types.Count()) { + // Class module->Types.AddUninitialized(); - new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, baseType); + new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, baseType, interfaces); const MString typeName(fullname.Get(), fullname.Length()); #if BUILD_DEBUG if (module->TypeNameToTypeIndex.ContainsKey(typeName)) @@ -304,11 +354,28 @@ ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const S module->TypeNameToTypeIndex[typeName] = TypeIndex; } -ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType) +ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces) : ScriptingTypeHandle(module, module->Types.Count()) { + // Structure module->Types.AddUninitialized(); - new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, copy, box, unbox, getField, setField, baseType); + new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, size, initRuntime, ctor, dtor, copy, box, unbox, getField, setField, baseType, interfaces); + const MString typeName(fullname.Get(), fullname.Length()); +#if BUILD_DEBUG + if (module->TypeNameToTypeIndex.ContainsKey(typeName)) + { + LOG(Error, "Duplicated native typename {0} from module {1}.", String(fullname), String(module->GetName())); + } +#endif + module->TypeNameToTypeIndex[typeName] = TypeIndex; +} + +ScriptingTypeInitializer::ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, ScriptingType::InitRuntimeHandler initRuntime, ScriptingTypeInitializer* baseType, const ScriptingType::InterfaceImplementation* interfaces) + : ScriptingTypeHandle(module, module->Types.Count()) +{ + // Interface + module->Types.AddUninitialized(); + new(module->Types.Get() + TypeIndex)ScriptingType(fullname, module, initRuntime, baseType, interfaces); const MString typeName(fullname.Get(), fullname.Length()); #if BUILD_DEBUG if (module->TypeNameToTypeIndex.ContainsKey(typeName)) diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index acde5612f..4c41a68a8 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -97,6 +97,7 @@ enum class ScriptingTypes Structure = 1, Enum = 2, Class = 3, + Interface = 4, }; /// @@ -116,6 +117,15 @@ struct FLAXENGINE_API ScriptingType typedef void (*GetField)(void* ptr, const String& name, Variant& value); typedef void (*SetField)(void* ptr, const String& name, const Variant& value); + struct InterfaceImplementation + { + // Pointer to the type of the implemented interface. + const ScriptingTypeInitializer* InterfaceType; + + // The offset (in bytes) from the object pointer to the interface implementation. Used for casting object to the interface. + int16 VTableOffset; + }; + /// /// The managed class (cached, can be null if missing). /// @@ -151,6 +161,11 @@ struct FLAXENGINE_API ScriptingType /// const ScriptingTypeInitializer* BaseTypePtr; + /// + /// The list of interfaces implemented by this type (null if unused, list ends with null entry). + /// + const InterfaceImplementation* Interfaces; + /// /// The native size of the type value (in bytes). /// @@ -196,15 +211,6 @@ struct FLAXENGINE_API ScriptingType mutable ScriptingObject* DefaultInstance; } Script; - struct - { - // Class constructor method pointer - Ctor Ctor; - - // Class destructor method pointer - Dtor Dtor; - } Class; - struct { // Structure constructor method pointer @@ -228,13 +234,23 @@ struct FLAXENGINE_API ScriptingType // Structure field value setter SetField SetField; } Struct; + + struct + { + // Class constructor method pointer + Ctor Ctor; + + // Class destructor method pointer + Dtor Dtor; + } Class; }; ScriptingType(); - ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, const ScriptingTypeHandle& baseType, SetupScriptVTableHandler setupScriptVTable = nullptr, SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr); - ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime = DefaultInitRuntime, SpawnHandler spawn = DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, SetupScriptVTableHandler setupScriptVTable = nullptr, SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr); - ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType); - ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType); + ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, SpawnHandler spawn, const ScriptingTypeHandle& baseType, SetupScriptVTableHandler setupScriptVTable = nullptr, SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr, const InterfaceImplementation* interfaces = nullptr); + ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime = DefaultInitRuntime, SpawnHandler spawn = DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, SetupScriptVTableHandler setupScriptVTable = nullptr, SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr, const InterfaceImplementation* interfaces = nullptr); + ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces = nullptr); + ScriptingType(const StringAnsiView& fullname, BinaryModule* module, int32 size, InitRuntimeHandler initRuntime, Ctor ctor, Dtor dtor, Copy copy, Box box, Unbox unbox, GetField getField, SetField setField, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces = nullptr); + ScriptingType(const StringAnsiView& fullname, BinaryModule* module, InitRuntimeHandler initRuntime, ScriptingTypeInitializer* baseType, const InterfaceImplementation* interfaces = nullptr); ScriptingType(const ScriptingType& other); ScriptingType(ScriptingType&& other); ScriptingType& operator=(ScriptingType&& other) = delete; @@ -270,6 +286,11 @@ struct FLAXENGINE_API ScriptingType /// ScriptingObject* GetDefaultInstance() const; + /// + /// Gets the pointer to the implementation of the given interface type for this scripting type (including base types). Returns null if given interface is not implemented. + /// + const InterfaceImplementation* GetInterface(const ScriptingTypeInitializer* interfaceType) const; + String ToString() const; }; @@ -278,9 +299,10 @@ struct FLAXENGINE_API ScriptingType /// struct FLAXENGINE_API ScriptingTypeInitializer : ScriptingTypeHandle { - ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime = ScriptingType::DefaultInitRuntime, ScriptingType::SpawnHandler spawn = ScriptingType::DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, ScriptingType::SetupScriptVTableHandler setupScriptVTable = nullptr, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr); - ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType = nullptr); - ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType = nullptr); + ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime = ScriptingType::DefaultInitRuntime, ScriptingType::SpawnHandler spawn = ScriptingType::DefaultSpawn, ScriptingTypeInitializer* baseType = nullptr, ScriptingType::SetupScriptVTableHandler setupScriptVTable = nullptr, ScriptingType::SetupScriptObjectVTableHandler setupScriptObjectVTable = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr); + ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingTypeInitializer* baseType = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr); + ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, int32 size, ScriptingType::InitRuntimeHandler initRuntime, ScriptingType::Ctor ctor, ScriptingType::Dtor dtor, ScriptingType::Copy copy, ScriptingType::Box box, ScriptingType::Unbox unbox, ScriptingType::GetField getField, ScriptingType::SetField setField, ScriptingTypeInitializer* baseType = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr); + ScriptingTypeInitializer(BinaryModule* module, const StringAnsiView& fullname, ScriptingType::InitRuntimeHandler initRuntime, ScriptingTypeInitializer* baseType = nullptr, const ScriptingType::InterfaceImplementation* interfaces = nullptr); }; /// diff --git a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs index 60074883c..06cb55257 100644 --- a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs @@ -22,6 +22,7 @@ namespace Flax.Build.Bindings public virtual bool IsClass => false; public virtual bool IsStruct => false; public virtual bool IsEnum => false; + public virtual bool IsInterface => false; public virtual bool IsValueType => false; public virtual bool IsScriptingObject => false; public virtual bool IsPod => false; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs index b70cc8e63..7de3f96fe 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs @@ -13,6 +13,7 @@ namespace Flax.Build.Bindings { public static readonly string Enum = "API_ENUM"; public static readonly string Class = "API_CLASS"; + public static readonly string Interface = "API_INTERFACE"; public static readonly string Struct = "API_STRUCT"; public static readonly string Function = "API_FUNCTION"; public static readonly string Property = "API_PROPERTY"; @@ -27,6 +28,7 @@ namespace Flax.Build.Bindings Enum, Class, Struct, + Interface, }; } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index d7711a13a..b9096b3bb 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -947,9 +947,7 @@ namespace Flax.Build.Bindings var classInfo = typeInfo as ClassInfo; var structureInfo = typeInfo as StructureInfo; var baseType = classInfo?.BaseType ?? structureInfo?.BaseType; - if (baseType != null && baseType.Type == "ISerializable") - baseType = null; - else if (classInfo != null && classInfo.IsBaseTypeHidden) + if (classInfo != null && classInfo.IsBaseTypeHidden) baseType = null; CppAutoSerializeFields.Clear(); CppAutoSerializeProperties.Clear(); @@ -1038,6 +1036,25 @@ namespace Flax.Build.Bindings contents.Append('}').AppendLine(); } + private static string GenerateCppInterfaceInheritanceTable(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, ClassStructInfo typeInfo, string typeNameNative) + { + var interfacesPtr = "nullptr"; + var interfaces = typeInfo.Interfaces; + if (interfaces != null) + { + interfacesPtr = typeNameNative + "_Interfaces"; + contents.Append("static const ScriptingType::InterfaceImplementation ").Append(interfacesPtr).AppendLine("[] = {"); + for (int i = 0; i < interfaces.Count; i++) + { + var interfaceInfo = interfaces[i]; + contents.Append(" { &").Append(interfaceInfo.NativeName).Append("::TypeInitializer, (int16)VTABLE_OFFSET(").Append(typeInfo.NativeName).Append(", ").Append(interfaceInfo.NativeName).AppendLine(") },"); + } + contents.AppendLine(" { nullptr, 0 },"); + contents.AppendLine("};"); + } + return interfacesPtr; + } + private static void GenerateCppClass(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, ClassInfo classInfo) { var classTypeNameNative = classInfo.FullNameNative; @@ -1311,6 +1328,9 @@ namespace Flax.Build.Bindings contents.Append('}').Append(';').AppendLine(); contents.AppendLine(); + // Interfaces + var interfacesTable = GenerateCppInterfaceInheritanceTable(buildData, contents, moduleInfo, classInfo, classTypeNameNative); + // Type initializer contents.Append($"ScriptingTypeInitializer {classTypeNameNative}::TypeInitializer((BinaryModule*)GetBinaryModule{moduleInfo.Name}(), "); contents.Append($"StringAnsiView(\"{classTypeNameManaged}\", {classTypeNameManaged.Length}), "); @@ -1330,8 +1350,9 @@ namespace Flax.Build.Bindings } else { - contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor"); + contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor, nullptr"); } + contents.Append(", ").Append(interfacesTable); contents.Append(");"); contents.AppendLine(); @@ -1526,6 +1547,42 @@ namespace Flax.Build.Bindings } } + private static void GenerateCppInterface(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, InterfaceInfo interfaceInfo) + { + var interfaceTypeNameNative = interfaceInfo.FullNameNative; + var interfaceTypeNameManaged = interfaceInfo.FullNameManaged; + var interfaceTypeNameManagedInternalCall = interfaceTypeNameManaged.Replace('+', '/'); + var interfaceTypeNameInternal = interfaceInfo.NativeName; + if (interfaceInfo.Parent != null && !(interfaceInfo.Parent is FileInfo)) + interfaceTypeNameInternal = interfaceInfo.Parent.FullNameNative + '_' + interfaceTypeNameInternal; + + contents.AppendLine(); + contents.AppendFormat("class {0}Internal", interfaceTypeNameInternal).AppendLine(); + contents.Append('{').AppendLine(); + contents.AppendLine("public:"); + + // Runtime initialization (internal methods binding) + contents.AppendLine(" static void InitRuntime()"); + contents.AppendLine(" {"); + contents.AppendLine(" }").AppendLine(); + + contents.Append('}').Append(';').AppendLine(); + contents.AppendLine(); + + // Type initializer + contents.Append($"ScriptingTypeInitializer {interfaceTypeNameNative}::TypeInitializer((BinaryModule*)GetBinaryModule{moduleInfo.Name}(), "); + contents.Append($"StringAnsiView(\"{interfaceTypeNameManaged}\", {interfaceTypeNameManaged.Length}), "); + contents.Append($"&{interfaceTypeNameInternal}Internal::InitRuntime"); + contents.Append(");"); + contents.AppendLine(); + + // Nested types + foreach (var apiTypeInfo in interfaceInfo.Children) + { + GenerateCppType(buildData, contents, moduleInfo, apiTypeInfo); + } + } + private static bool GenerateCppType(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, object type) { if (type is ApiTypeInfo apiTypeInfo && apiTypeInfo.IsInBuild) @@ -1537,6 +1594,8 @@ namespace Flax.Build.Bindings GenerateCppClass(buildData, contents, moduleInfo, classInfo); else if (type is StructureInfo structureInfo) GenerateCppStruct(buildData, contents, moduleInfo, structureInfo); + else if (type is InterfaceInfo interfaceInfo) + GenerateCppInterface(buildData, contents, moduleInfo, interfaceInfo); else if (type is InjectCppCodeInfo injectCppCodeInfo) contents.AppendLine(injectCppCodeInfo.Code); else diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index f164d2744..b21a4a135 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -383,6 +383,95 @@ namespace Flax.Build.Bindings return name; } + private static void ParseInheritance(ref ParsingContext context, ClassStructInfo desc, out bool isFinal) + { + desc.BaseType = null; + desc.BaseTypeInheritance = AccessLevel.Private; + + var token = context.Tokenizer.NextToken(); + isFinal = token.Value == "final"; + if (isFinal) + token = context.Tokenizer.NextToken(); + + if (token.Type == TokenType.Colon) + { + while (token.Type != TokenType.LeftCurlyBrace) + { + var accessToken = context.Tokenizer.ExpectToken(TokenType.Identifier); + switch (accessToken.Value) + { + case "public": + desc.BaseTypeInheritance = AccessLevel.Public; + token = context.Tokenizer.ExpectToken(TokenType.Identifier); + break; + case "protected": + desc.BaseTypeInheritance = AccessLevel.Protected; + token = context.Tokenizer.ExpectToken(TokenType.Identifier); + break; + case "private": + token = context.Tokenizer.ExpectToken(TokenType.Identifier); + break; + default: + token = accessToken; + break; + } + + var baseTypeInfo = new TypeInfo + { + Type = token.Value, + }; + if (token.Value.Length > 2 && token.Value[0] == 'I' && char.IsUpper(token.Value[1])) + { + // Interface + if (desc.InterfaceNames == null) + desc.InterfaceNames = new List(); + desc.InterfaceNames.Add(baseTypeInfo); + token = context.Tokenizer.NextToken(); + continue; + } + + if (desc.BaseType != null) + { + // Allow for multiple base classes, just the first one needs to be a valid base type + break; + throw new Exception($"Invalid '{desc.Name}' inheritance (only single base class is allowed for scripting types, excluding interfaces)."); + } + desc.BaseType = baseTypeInfo; + token = context.Tokenizer.NextToken(); + if (token.Type == TokenType.LeftCurlyBrace) + { + break; + } + if (token.Type == TokenType.LeftAngleBracket) + { + var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier); + token = context.Tokenizer.ExpectToken(TokenType.RightAngleBracket); + desc.BaseType.GenericArgs = new List + { + new TypeInfo + { + Type = genericType.Value, + } + }; + + // TODO: find better way to resolve this (custom base type attribute?) + if (desc.BaseType.Type == "ShaderAssetTypeBase") + { + desc.BaseType = desc.BaseType.GenericArgs[0]; + } + + token = context.Tokenizer.NextToken(); + } + } + token = context.Tokenizer.PreviousToken(); + } + else + { + // No base type + token = context.Tokenizer.PreviousToken(); + } + } + private static ClassInfo ParseClass(ref ParsingContext context) { var desc = new ClassInfo @@ -410,64 +499,8 @@ namespace Flax.Build.Bindings // Read name desc.Name = desc.NativeName = ParseName(ref context); - // Read class inheritance - token = context.Tokenizer.NextToken(); - var isFinal = token.Value == "final"; - if (isFinal) - token = context.Tokenizer.NextToken(); - if (token.Type == TokenType.Colon) - { - // Current class does have inheritance defined - var accessToken = context.Tokenizer.ExpectToken(TokenType.Identifier); - switch (accessToken.Value) - { - case "public": - desc.BaseTypeInheritance = AccessLevel.Public; - token = context.Tokenizer.ExpectToken(TokenType.Identifier); - break; - case "protected": - desc.BaseTypeInheritance = AccessLevel.Protected; - token = context.Tokenizer.ExpectToken(TokenType.Identifier); - break; - case "private": - token = context.Tokenizer.ExpectToken(TokenType.Identifier); - break; - } - - desc.BaseType = new TypeInfo - { - Type = token.Value, - }; - token = context.Tokenizer.NextToken(); - if (token.Type == TokenType.LeftAngleBracket) - { - var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier); - context.Tokenizer.ExpectToken(TokenType.RightAngleBracket); - desc.BaseType.GenericArgs = new List - { - new TypeInfo - { - Type = genericType.Value, - } - }; - - // TODO: find better way to resolve this (custom base type attribute?) - if (desc.BaseType.Type == "ShaderAssetTypeBase") - { - desc.BaseType = desc.BaseType.GenericArgs[0]; - } - } - else - { - token = context.Tokenizer.PreviousToken(); - } - } - else - { - // No base type - token = context.Tokenizer.PreviousToken(); - desc.BaseType = null; - } + // Read inheritance + ParseInheritance(ref context, desc, out var isFinal); // Process tag parameters foreach (var tag in tagParams) @@ -523,6 +556,68 @@ namespace Flax.Build.Bindings return desc; } + private static InterfaceInfo ParseInterface(ref ParsingContext context) + { + var desc = new InterfaceInfo + { + Children = new List(), + Access = context.CurrentAccessLevel, + }; + + // Read the documentation comment + desc.Comment = ParseComment(ref context); + + // Read parameters from the tag + var tagParams = ParseTagParameters(ref context); + + // Read 'class' keyword + var token = context.Tokenizer.NextToken(); + if (token.Value != "class") + throw new Exception($"Invalid API_INTERFACE usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + + // Read name + desc.Name = desc.NativeName = ParseName(ref context); + if (desc.Name.Length < 2 || desc.Name[0] != 'I' || !char.IsUpper(desc.Name[1])) + throw new Exception($"Invalid API_INTERFACE name '{desc.Name}' (it must start with 'I' character followed by the uppercase character)."); + + // Read inheritance + ParseInheritance(ref context, desc, out _); + + // Process tag parameters + foreach (var tag in tagParams) + { + switch (tag.Tag.ToLower()) + { + case "public": + desc.Access = AccessLevel.Public; + break; + case "protected": + desc.Access = AccessLevel.Protected; + break; + case "private": + desc.Access = AccessLevel.Private; + break; + case "inbuild": + desc.IsInBuild = true; + break; + case "attributes": + desc.Attributes = tag.Value; + break; + case "name": + desc.Name = tag.Value; + break; + case "namespace": + desc.Namespace = tag.Value; + break; + default: + Log.Warning($"Unknown or not supported tag parameter {tag} used on interface {desc.Name} at line {context.Tokenizer.CurrentLine}"); + break; + } + } + + return desc; + } + private static FunctionInfo ParseFunction(ref ParsingContext context) { var desc = new FunctionInfo @@ -875,55 +970,8 @@ namespace Flax.Build.Bindings // Read name desc.Name = desc.NativeName = ParseName(ref context); - // Read structure inheritance - token = context.Tokenizer.NextToken(); - if (token.Type == TokenType.Colon) - { - // Current class does have inheritance defined - var accessToken = context.Tokenizer.ExpectToken(TokenType.Identifier); - switch (accessToken.Value) - { - case "public": - token = context.Tokenizer.ExpectToken(TokenType.Identifier); - break; - case "protected": - token = context.Tokenizer.ExpectToken(TokenType.Identifier); - break; - case "private": - token = context.Tokenizer.ExpectToken(TokenType.Identifier); - break; - default: - token = accessToken; - break; - } - - desc.BaseType = new TypeInfo - { - Type = token.Value, - }; - token = context.Tokenizer.NextToken(); - if (token.Type == TokenType.LeftAngleBracket) - { - var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier); - context.Tokenizer.ExpectToken(TokenType.RightAngleBracket); - desc.BaseType.GenericArgs = new List - { - new TypeInfo - { - Type = genericType.Value, - } - }; - } - else - { - token = context.Tokenizer.PreviousToken(); - } - } - else - { - // No base type - token = context.Tokenizer.PreviousToken(); - } + // Read inheritance + ParseInheritance(ref context, desc, out _); // Process tag parameters foreach (var tag in tagParams) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index 5b58a8d8f..00cf0c7c3 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -254,6 +254,16 @@ namespace Flax.Build.Bindings var injectCppCodeInfo = ParseInjectCppCode(ref context); fileInfo.AddChild(injectCppCodeInfo); } + else if (string.Equals(token.Value, ApiTokens.Interface, StringComparison.Ordinal)) + { + if (!(context.ScopeInfo is FileInfo)) + throw new NotImplementedException("TODO: add support for nested interfaces in scripting API"); + + var interfaceInfo = ParseInterface(ref context); + scopeType = interfaceInfo; + context.ScopeInfo.AddChild(scopeType); + context.CurrentAccessLevel = AccessLevel.Public; + } else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal)) { if (context.ScopeInfo is ClassInfo classInfo) diff --git a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs index 3ac5b37fc..a83ac2b55 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System.Collections.Generic; using System.Linq; @@ -8,7 +8,7 @@ namespace Flax.Build.Bindings /// /// The native class information for bindings generator. /// - public class ClassInfo : ApiTypeInfo + public class ClassInfo : ClassStructInfo { private static readonly HashSet InBuildScriptingObjectTypes = new HashSet { @@ -22,9 +22,6 @@ namespace Flax.Build.Bindings "Actor", }; - public AccessLevel Access; - public TypeInfo BaseType; - public AccessLevel BaseTypeInheritance; public bool IsBaseTypeHidden; public bool IsStatic; public bool IsSealed; @@ -52,7 +49,7 @@ namespace Flax.Build.Bindings base.Init(buildData); // Internal base types are usually hidden from bindings (used in core-only internally) - IsBaseTypeHidden = BaseTypeInheritance == AccessLevel.Private || BaseType.Type == "ISerializable"; + IsBaseTypeHidden = BaseTypeInheritance == AccessLevel.Private || BaseType == null; // Cache if it it Scripting Object type if (InBuildScriptingObjectTypes.Contains(Name)) diff --git a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs new file mode 100644 index 000000000..dcb2d2e9e --- /dev/null +++ b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Generic; + +namespace Flax.Build.Bindings +{ + /// + /// The native class/structure information for bindings generator. + /// + public abstract class ClassStructInfo : ApiTypeInfo + { + public AccessLevel Access; + public AccessLevel BaseTypeInheritance; + public TypeInfo BaseType; + public List Interfaces; // Optional + public List InterfaceNames; // Optional + + public override void Init(Builder.BuildData buildData) + { + base.Init(buildData); + + if (Interfaces == null && InterfaceNames != null && InterfaceNames.Count != 0) + { + Interfaces = new List(); + for (var i = 0; i < InterfaceNames.Count; i++) + { + var interfaceName = InterfaceNames[i]; + var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, interfaceName, this); + if (apiTypeInfo is InterfaceInfo interfaceInfo) + { + Interfaces.Add(interfaceInfo); + } + } + if (Interfaces.Count == 0) + Interfaces = null; + } + } + } +} diff --git a/Source/Tools/Flax.Build/Bindings/InterfaceInfo.cs b/Source/Tools/Flax.Build/Bindings/InterfaceInfo.cs new file mode 100644 index 000000000..e0225f288 --- /dev/null +++ b/Source/Tools/Flax.Build/Bindings/InterfaceInfo.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +namespace Flax.Build.Bindings +{ + /// + /// The native class/structure interface information for bindings generator. + /// + public class InterfaceInfo : ClassStructInfo + { + public override bool IsInterface => true; + + public override void AddChild(ApiTypeInfo apiTypeInfo) + { + apiTypeInfo.Namespace = null; + + base.AddChild(apiTypeInfo); + } + + public override string ToString() + { + return "interface " + Name; + } + } +} diff --git a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs index ca0a02247..baf80a6fe 100644 --- a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs @@ -8,10 +8,8 @@ namespace Flax.Build.Bindings /// /// The native structure information for bindings generator. /// - public class StructureInfo : ApiTypeInfo + public class StructureInfo : ClassStructInfo { - public AccessLevel Access; - public TypeInfo BaseType; public List Fields; public List Functions; public bool IsAutoSerialization; @@ -27,7 +25,7 @@ namespace Flax.Build.Bindings { base.Init(buildData); - if (ForceNoPod) + if (ForceNoPod || (InterfaceNames != null && InterfaceNames.Count != 0)) { _isPod = false; return; diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index a8221455f..9d415beed 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -69,12 +69,14 @@ + + From c5568c8eae5291fe959908d1dfb78d47bef0f5d2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 4 Jan 2021 14:19:51 +0100 Subject: [PATCH 026/419] Add support for loading JsonAsset instance objects if they implement ISerializable interface --- Source/Engine/Content/JsonAsset.cpp | 78 ++++++++++++--------- Source/Engine/Content/JsonAsset.h | 4 +- Source/Engine/Core/ISerializable.h | 60 ++++++++++++++++ Source/Engine/Serialization/ISerializable.h | 56 +-------------- 4 files changed, 110 insertions(+), 88 deletions(-) create mode 100644 Source/Engine/Core/ISerializable.h diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index 3779181b7..4c50d94bb 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -10,7 +10,10 @@ #include "Engine/Core/Log.h" #include "Engine/Serialization/JsonTools.h" #include "Engine/Content/Factories/JsonAssetFactory.h" +#include "Engine/Core/Cache.h" #include "Engine/Debug/Exceptions/JsonParseException.h" +#include "Engine/Scripting/Scripting.h" +#include "Engine/Utilities/StringConverter.h" JsonAssetBase::JsonAssetBase(const SpawnParams& params, const AssetInfo* info) : Asset(params, info) @@ -175,51 +178,54 @@ void JsonAssetBase::onRename(const StringView& newPath) REGISTER_JSON_ASSET(JsonAsset, "FlaxEngine.JsonAsset"); -//////////////////////////////////////////////////////////////////////////////////// - -#include "Engine/Physics/PhysicalMaterial.h" - -// Unmanaged json asset types that are serialized to JsonAsset and should be created by auto by asset. -// This allows to reuse JsonAsset without creating dedicated asset types. It has been designed for lightweight resources. - -typedef ISerializable* (*UnmanagedJsonInstanceCreator)(); - -template -ISerializable* Create() -{ - return New(); -} - -// Key: managed class typename, Value: unmanaged instance spawner function -Dictionary UnmanagedTypes(32); - -void InitUnmanagedJsonTypes() -{ - UnmanagedTypes[TEXT("FlaxEngine.PhysicalMaterial")] = &Create; -} - -//////////////////////////////////////////////////////////////////////////////////// - JsonAsset::JsonAsset(const SpawnParams& params, const AssetInfo* info) : JsonAssetBase(params, info) , Instance(nullptr) { - if (UnmanagedTypes.IsEmpty()) - InitUnmanagedJsonTypes(); } Asset::LoadResult JsonAsset::loadAsset() { // Base auto result = JsonAssetBase::loadAsset(); - if (result != LoadResult::Ok) + if (result != LoadResult::Ok || IsInternalType()) return result; - UnmanagedJsonInstanceCreator instanceSpawner = nullptr; - if (UnmanagedTypes.TryGet(DataTypeName, instanceSpawner)) + // Try to scripting type for this data + const StringAsANSI<> dataTypeNameAnsi(DataTypeName.Get(), DataTypeName.Length()); + const auto typeHandle = Scripting::FindScriptingType(StringAnsiView(dataTypeNameAnsi.Get(), DataTypeName.Length())); + if (typeHandle) { - Instance = instanceSpawner(); - Instance->Deserialize(*Data, nullptr); + auto& type = typeHandle.GetType(); + switch (type.Type) + { + case ScriptingTypes::Class: + { + // Ensure that object can deserialized + const ScriptingType::InterfaceImplementation* interfaces = type.GetInterface(&ISerializable::TypeInitializer); + if (!interfaces) + { + LOG(Warning, "Cannot deserialize {0} from Json Asset because it doesn't implement ISerializable interface.", type.ToString()); + break; + } + + // Allocate object + const auto instance = Allocator::Allocate(type.Size); + if (!instance) + return LoadResult::Failed; + Instance = instance; + _dtor = type.Class.Dtor; + type.Class.Ctor(instance); + + // Deserialize object + auto modifier = Cache::ISerializeModifier.Get(); + modifier->EngineBuild = DataEngineBuild; + ((ISerializable*)((byte*)instance + interfaces->VTableOffset))->Deserialize(*Data, modifier.Value); + // TODO: delete object when containing BinaryModule gets unloaded + break; + } + default: ; + } } return result; @@ -230,5 +236,11 @@ void JsonAsset::unload(bool isReloading) // Base JsonAssetBase::unload(isReloading); - SAFE_DELETE(Instance); + if (Instance) + { + _dtor(Instance); + Allocator::Free(Instance); + Instance = nullptr; + _dtor = nullptr; + } } diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index d5f785418..a6d452ff0 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -78,13 +78,15 @@ protected: API_CLASS(NoSpawn) class JsonAsset : public JsonAssetBase { DECLARE_ASSET_HEADER(JsonAsset); +private: + ScriptingType::Dtor _dtor; public: /// /// The deserialized unmanaged object instance (e.g. PhysicalMaterial). /// - ISerializable* Instance; + void* Instance; protected: diff --git a/Source/Engine/Core/ISerializable.h b/Source/Engine/Core/ISerializable.h new file mode 100644 index 000000000..cfc5e1976 --- /dev/null +++ b/Source/Engine/Core/ISerializable.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Serialization/JsonFwd.h" +#include "Engine/Core/Compiler.h" +#include "Engine/Core/Config.h" + +class JsonWriter; +class ISerializeModifier; + +/// +/// Interface for objects that can be serialized/deserialized to/from JSON format. +/// +API_INTERFACE() class FLAXENGINE_API ISerializable +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(ISerializable); +public: + + typedef rapidjson_flax::Document SerializeDocument; + + /// + /// Serialization output stream + /// + typedef rapidjson_flax::Value DeserializeStream; + + /// + /// Serialization input stream + /// + typedef JsonWriter SerializeStream; + +public: + + /// + /// Finalizes an instance of the class. + /// + virtual ~ISerializable() = default; + + /// + /// Serialize object to the output stream compared to the values of the other object instance (eg. default class object). If other object is null then serialize all properties. + /// + /// The output stream. + /// The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties. + virtual void Serialize(SerializeStream& stream, const void* otherObj) = 0; + + /// + /// Deserialize object from the input stream + /// + /// The input stream. + /// The deserialization modifier object. Always valid. + virtual void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) = 0; + + /// + /// Deserialize object from the input stream child member. Won't deserialize it if member is missing. + /// + /// The input stream. + /// The input stream member to lookup. + /// The deserialization modifier object. Always valid. + void DeserializeIfExists(DeserializeStream& stream, const char* memberName, ISerializeModifier* modifier); +}; diff --git a/Source/Engine/Serialization/ISerializable.h b/Source/Engine/Serialization/ISerializable.h index cc7ebd0d7..537f64ee9 100644 --- a/Source/Engine/Serialization/ISerializable.h +++ b/Source/Engine/Serialization/ISerializable.h @@ -2,57 +2,5 @@ #pragma once -#include "JsonFwd.h" -#include "Engine/Core/Compiler.h" - -class JsonWriter; -class ISerializeModifier; - -/// -/// Interface for objects that can be serialized/deserialized to/from JSON format. -/// -class FLAXENGINE_API ISerializable -{ -public: - - typedef rapidjson_flax::Document SerializeDocument; - - /// - /// Serialization output stream - /// - typedef rapidjson_flax::Value DeserializeStream; - - /// - /// Serialization input stream - /// - typedef JsonWriter SerializeStream; - -public: - - /// - /// Finalizes an instance of the class. - /// - virtual ~ISerializable() = default; - - /// - /// Serialize object to the output stream compared to the values of the other object instance (eg. default class object). If other object is null then serialize all properties. - /// - /// The output stream. - /// The instance of the object to compare with and serialize only the modified properties. If null, then serialize all properties. - virtual void Serialize(SerializeStream& stream, const void* otherObj) = 0; - - /// - /// Deserialize object from the input stream - /// - /// The input stream. - /// The deserialization modifier object. Always valid. - virtual void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) = 0; - - /// - /// Deserialize object from the input stream child member. Won't deserialize it if member is missing. - /// - /// The input stream. - /// The input stream member to lookup. - /// The deserialization modifier object. Always valid. - void DeserializeIfExists(DeserializeStream& stream, const char* memberName, ISerializeModifier* modifier); -}; +// ISerializable moved to Core module +#include "Engine/Core/ISerializable.h" From cd8a27ca2630ac283a035c5b42d0b16802eb4647 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 4 Jan 2021 14:20:00 +0100 Subject: [PATCH 027/419] Fix typo --- Source/Shaders/Lighting.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Shaders/Lighting.hlsl b/Source/Shaders/Lighting.hlsl index f626afa0b..62b1e7aa4 100644 --- a/Source/Shaders/Lighting.hlsl +++ b/Source/Shaders/Lighting.hlsl @@ -4,7 +4,7 @@ #define __LIGHTING__ #if !defined(USE_GBUFFER_CUSTOM_DATA) -#error "Canot calculate lighting without custom data in GBuffer. Define USE_GBUFFER_CUSTOM_DATA." +#error "Cannot calculate lighting without custom data in GBuffer. Define USE_GBUFFER_CUSTOM_DATA." #endif #include "./Flax/LightingCommon.hlsl" From 953f8da84db61a81ab88d1ff8dd794fd8b1f7cef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 4 Jan 2021 14:25:21 +0100 Subject: [PATCH 028/419] Update copyright year --- Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/EnumInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/EventInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/FieldInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/FileInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/FunctionInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/InheritanceInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/LangType.cs | 2 +- Source/Tools/Flax.Build/Bindings/MemberInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/ModuleInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/PropertyInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/StructureInfo.cs | 2 +- Source/Tools/Flax.Build/Bindings/TypeInfo.cs | 2 +- Source/Tools/Flax.Build/Build/Sdk.cs | 2 +- Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs | 2 +- Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs | 2 +- Source/Tools/Flax.Build/Utilities/Tokenizer.cs | 2 +- Source/Tools/Flax.Build/Utilities/TwoWayEnumerator.cs | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs index 06cb55257..814983e9b 100644 --- a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System.Collections.Generic; diff --git a/Source/Tools/Flax.Build/Bindings/EnumInfo.cs b/Source/Tools/Flax.Build/Bindings/EnumInfo.cs index eeb750255..bf9fc8132 100644 --- a/Source/Tools/Flax.Build/Bindings/EnumInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/EnumInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; diff --git a/Source/Tools/Flax.Build/Bindings/EventInfo.cs b/Source/Tools/Flax.Build/Bindings/EventInfo.cs index 7d98979b4..de93332ac 100644 --- a/Source/Tools/Flax.Build/Bindings/EventInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/EventInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. namespace Flax.Build.Bindings { diff --git a/Source/Tools/Flax.Build/Bindings/FieldInfo.cs b/Source/Tools/Flax.Build/Bindings/FieldInfo.cs index 29c8b0617..509c1d6e6 100644 --- a/Source/Tools/Flax.Build/Bindings/FieldInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FieldInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. namespace Flax.Build.Bindings { diff --git a/Source/Tools/Flax.Build/Bindings/FileInfo.cs b/Source/Tools/Flax.Build/Bindings/FileInfo.cs index 38f2e491f..25e1e1944 100644 --- a/Source/Tools/Flax.Build/Bindings/FileInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FileInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; diff --git a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs index 81ea02796..953fd7ed4 100644 --- a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System.Collections.Generic; diff --git a/Source/Tools/Flax.Build/Bindings/InheritanceInfo.cs b/Source/Tools/Flax.Build/Bindings/InheritanceInfo.cs index 01ab2e292..4a8673660 100644 --- a/Source/Tools/Flax.Build/Bindings/InheritanceInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/InheritanceInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. namespace Flax.Build.Bindings { diff --git a/Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs b/Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs index 7deee98c6..dfdb551f6 100644 --- a/Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. namespace Flax.Build.Bindings { diff --git a/Source/Tools/Flax.Build/Bindings/LangType.cs b/Source/Tools/Flax.Build/Bindings/LangType.cs index d74560bc6..7f5f2da03 100644 --- a/Source/Tools/Flax.Build/Bindings/LangType.cs +++ b/Source/Tools/Flax.Build/Bindings/LangType.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; diff --git a/Source/Tools/Flax.Build/Bindings/MemberInfo.cs b/Source/Tools/Flax.Build/Bindings/MemberInfo.cs index 63034f57a..3e09b9f0b 100644 --- a/Source/Tools/Flax.Build/Bindings/MemberInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/MemberInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. namespace Flax.Build.Bindings { diff --git a/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs b/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs index 554d46c01..6626145a9 100644 --- a/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. namespace Flax.Build.Bindings { diff --git a/Source/Tools/Flax.Build/Bindings/PropertyInfo.cs b/Source/Tools/Flax.Build/Bindings/PropertyInfo.cs index b36821511..f53578b4e 100644 --- a/Source/Tools/Flax.Build/Bindings/PropertyInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/PropertyInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. namespace Flax.Build.Bindings { diff --git a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs index baf80a6fe..be24bb1f0 100644 --- a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index 8af061200..c1ac79ae7 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; diff --git a/Source/Tools/Flax.Build/Build/Sdk.cs b/Source/Tools/Flax.Build/Build/Sdk.cs index 816404679..450d8f43f 100644 --- a/Source/Tools/Flax.Build/Build/Sdk.cs +++ b/Source/Tools/Flax.Build/Build/Sdk.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; diff --git a/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs b/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs index f6a7816cf..d02f440ae 100644 --- a/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs +++ b/Source/Tools/Flax.Build/Platforms/Android/AndroidNdk.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.IO; diff --git a/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs b/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs index d2d0278bf..ec83fb90f 100644 --- a/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs +++ b/Source/Tools/Flax.Build/Platforms/Android/AndroidSdk.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.IO; diff --git a/Source/Tools/Flax.Build/Utilities/Tokenizer.cs b/Source/Tools/Flax.Build/Utilities/Tokenizer.cs index 70d0d7fc1..6913ec31a 100644 --- a/Source/Tools/Flax.Build/Utilities/Tokenizer.cs +++ b/Source/Tools/Flax.Build/Utilities/Tokenizer.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; diff --git a/Source/Tools/Flax.Build/Utilities/TwoWayEnumerator.cs b/Source/Tools/Flax.Build/Utilities/TwoWayEnumerator.cs index 0347a88a8..ec9f71091 100644 --- a/Source/Tools/Flax.Build/Utilities/TwoWayEnumerator.cs +++ b/Source/Tools/Flax.Build/Utilities/TwoWayEnumerator.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; From b38ccb0fbd93df57f481c9cf73d6a43d0230dea1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 4 Jan 2021 14:30:33 +0100 Subject: [PATCH 029/419] Remove submodules --- .gitmodules | 6 ------ Source/Platforms/PS4 | 1 - Source/Platforms/XboxScarlett | 1 - 3 files changed, 8 deletions(-) delete mode 160000 Source/Platforms/PS4 delete mode 160000 Source/Platforms/XboxScarlett diff --git a/.gitmodules b/.gitmodules index 36d11c4bd..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +0,0 @@ -[submodule "Source/Platforms/PS4"] - path = Source/Platforms/PS4 - url = https://gitlab.flaxengine.com/flax/flaxengine-ps4.git -[submodule "Source/Platforms/XboxScarlett"] - path = Source/Platforms/XboxScarlett - url = https://gitlab.flaxengine.com/flax/flaxengine-xboxscarlett.git diff --git a/Source/Platforms/PS4 b/Source/Platforms/PS4 deleted file mode 160000 index b9e29ede6..000000000 --- a/Source/Platforms/PS4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b9e29ede69d31f93cbdd012d6e6c4ad8120dc7c0 diff --git a/Source/Platforms/XboxScarlett b/Source/Platforms/XboxScarlett deleted file mode 160000 index 7691bbaf2..000000000 --- a/Source/Platforms/XboxScarlett +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7691bbaf2a7e47459d9fb18fd1a8833cdbd7517a From ed92489a6077f7e812375b3e1ed907e11990f5fb Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 4 Jan 2021 21:59:57 +0100 Subject: [PATCH 030/419] Fixing open file dialog. --- Source/Engine/Platform/Windows/WindowsFileSystem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp index 0c0d9788c..6b6cd6bf0 100644 --- a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp +++ b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp @@ -231,7 +231,9 @@ bool WindowsFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringVie while (*ptr) { filenames.Add(directory / ptr); - ptr += (lstrlenW(ptr) + 1); + ptr += lstrlenW(ptr); + if (multiSelect) + ptr++; } result = false; From f5ac18915a72cc7f08c8924188851eee07328a4b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 4 Jan 2021 22:44:40 +0100 Subject: [PATCH 031/419] Fixing open save dialog. --- Source/Engine/Platform/Windows/WindowsFileSystem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp index 6b6cd6bf0..7c6b04fef 100644 --- a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp +++ b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp @@ -278,7 +278,9 @@ bool WindowsFileSystem::ShowSaveFileDialog(Window* parentWindow, const StringVie while (*ptr) { filenames.Add(directory / ptr); - ptr += (lstrlenW(ptr) + 1); + ptr += lstrlenW(ptr); + if (multiSelect) + ptr++; } result = false; From f7c17d96ce23ea8a3b1871a2b359ffdac4cc2a4f Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Tue, 5 Jan 2021 01:06:42 +0100 Subject: [PATCH 032/419] Fixed a typo --- Source/Shaders/Lighting.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Shaders/Lighting.hlsl b/Source/Shaders/Lighting.hlsl index 9838cca52..b5021b711 100644 --- a/Source/Shaders/Lighting.hlsl +++ b/Source/Shaders/Lighting.hlsl @@ -4,7 +4,7 @@ #define __LIGHTING__ #if !defined(USE_GBUFFER_CUSTOM_DATA) -#error "Canot calculate lighting without custom data in GBuffer. Define USE_GBUFFER_CUSTOM_DATA." +#error "Cannot calculate lighting without custom data in GBuffer. Define USE_GBUFFER_CUSTOM_DATA." #endif #include "./Flax/LightingCommon.hlsl" From 6ab300025b0cddd0db05bdfa3475ce247fc23a25 Mon Sep 17 00:00:00 2001 From: VNC <52937757+VNNCC@users.noreply.github.com> Date: Tue, 5 Jan 2021 02:13:15 +0100 Subject: [PATCH 033/419] Fixed a ton of typos --- .../Platform/Android/AndroidPlatformTools.cpp | 2 +- .../Cooker/Platform/UWP/UWPPlatformTools.cpp | 2 +- Source/Editor/Cooker/PlatformTools.h | 2 +- Source/Editor/Editor.h | 2 +- Source/Editor/GUI/CurveEditor.cs | 2 +- Source/Editor/GUI/Dialogs/Dialog.cs | 2 +- Source/Editor/GUI/Table.cs | 2 +- Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs | 2 +- Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs | 2 +- Source/Editor/Modules/ContentFindingModule.cs | 2 +- Source/Editor/Options/OptionsModule.cs | 2 +- Source/Editor/Progress/ProgressHandler.cs | 2 +- Source/Editor/ProjectInfo.cpp | 4 ++-- .../VisualStudio/VisualStudioConnection.cpp | 2 +- .../Surface/Archetypes/Animation.StateMachine.cs | 2 +- Source/Editor/Surface/Archetypes/Function.cs | 4 ++-- Source/Editor/Surface/Archetypes/Parameters.cs | 2 +- Source/Editor/Surface/VisjectSurface.DragDrop.cs | 2 +- Source/Editor/Surface/VisualScriptSurface.cs | 2 +- Source/Editor/Tools/Terrain/Paint/Mode.cs | 2 +- Source/Editor/Tools/Terrain/Sculpt/Mode.cs | 2 +- Source/Editor/Tools/VertexPainting.cs | 2 +- Source/Editor/Utilities/EditorUtilities.cpp | 2 +- Source/Editor/Utilities/ObjectSnapshot.cs | 2 +- Source/Editor/Utilities/ShuntingYardParser.cs | 2 +- Source/Editor/Viewport/Cameras/FPSCamera.cs | 2 +- Source/Editor/Windows/Assets/AssetEditorWindow.cs | 2 +- Source/Engine/Core/Math/Color.cs | 2 +- Source/Engine/Graphics/Enums.h | 2 +- Source/Engine/Graphics/GPUBuffer.h | 2 +- Source/Engine/Graphics/GPUDevice.cpp | 2 +- Source/Engine/Graphics/GPUSwapChain.cpp | 2 +- Source/Engine/Graphics/Materials/IMaterial.h | 2 +- Source/Engine/Graphics/Mesh.cs | 2 +- Source/Engine/Graphics/Models/ModelData.Tool.cpp | 2 +- Source/Engine/Graphics/PostProcessSettings.h | 2 +- Source/Engine/Graphics/RenderTargetPool.cpp | 2 +- Source/Engine/Graphics/Shaders/GPUShader.h | 14 +++++++------- Source/Engine/Graphics/Textures/GPUTexture.h | 2 +- .../DirectX/DX11/GPUTimerQueryDX11.cpp | 2 +- .../GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp | 2 +- .../GraphicsDevice/Vulkan/RenderToolsVulkan.h | 6 +++--- Source/Engine/Input/Enums.h | 2 +- Source/Engine/Input/Mouse.h | 2 +- Source/Engine/Level/Actors/StaticModel.cpp | 2 +- Source/Engine/Level/Level.cpp | 4 ++-- Source/Engine/Level/Prefabs/Prefab.Apply.cpp | 2 +- Source/Engine/Navigation/NavMesh.cpp | 2 +- .../Engine/Particles/Graph/ParticleEmitterGraph.h | 2 +- Source/Engine/Physics/Colliders/Collider.h | 2 +- Source/Engine/Physics/CollisionCooking.cpp | 2 +- Source/Engine/Platform/Linux/LinuxPlatform.cpp | 2 +- Source/Engine/Renderer/AmbientOcclusionPass.h | 2 +- .../Attributes/Editor/EditorOrderAttribute.cs | 2 +- .../Scripting/Attributes/SerializeAttribute.cs | 2 +- .../Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp | 2 +- Source/Engine/Scripting/ManagedCLR/MAssembly.h | 2 +- Source/Engine/Scripting/ManagedCLR/MClass.h | 2 +- Source/Engine/Scripting/ScriptingType.h | 4 ++-- Source/Engine/Serialization/JsonWriter.cpp | 2 +- Source/Engine/Serialization/Stream.h | 4 ++-- Source/Engine/Terrain/Terrain.cpp | 2 +- Source/Engine/Threading/ConcurrentBuffer.h | 2 +- Source/Engine/Threading/IRunnable.h | 2 +- Source/Engine/Threading/ThreadPool.cpp | 2 +- .../Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp | 2 +- Source/Engine/Tools/ModelTool/ModelTool.cpp | 2 +- Source/Engine/UI/GUI/ContainerControl.cs | 2 +- Source/Engine/UI/GUI/Control.cs | 8 ++++---- Source/Engine/UI/GUI/Panels/UniformGridPanel.cs | 2 +- Source/Engine/UI/UICanvas.cs | 4 ++-- Source/Shaders/BakeLightmap.shader | 2 +- Source/Shaders/BitonicSort.shader | 2 +- 73 files changed, 90 insertions(+), 90 deletions(-) diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index bce6aa860..b4365b4bd 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -141,7 +141,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) const auto c = packageName[i]; if (c != '_' && c != '.' && !StringUtils::IsAlnum(c)) { - LOG(Error, "Android Package Name \'{0}\' contains invalid chaarcter. Only letters, numbers, dots and underscore characters are allowed.", packageName); + LOG(Error, "Android Package Name \'{0}\' contains invalid character. Only letters, numbers, dots and underscore characters are allowed.", packageName); return true; } } diff --git a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp index f6c846fed..d9d2fe612 100644 --- a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp @@ -24,7 +24,7 @@ bool UWPPlatformTools::OnScriptsStepDone(CookingData& data) const String assembliesPath = data.OutputPath; if (FileSystem::CopyFile(assembliesPath / TEXT("Newtonsoft.Json.dll"), customBinPath)) { - data.Error(TEXT("Failed to copy deloy custom assembly.")); + data.Error(TEXT("Failed to copy deploy custom assembly.")); return true; } FileSystem::DeleteFile(assembliesPath / TEXT("Newtonsoft.Json.pdb")); diff --git a/Source/Editor/Cooker/PlatformTools.h b/Source/Editor/Cooker/PlatformTools.h index 28f6346ca..c98c008bc 100644 --- a/Source/Editor/Cooker/PlatformTools.h +++ b/Source/Editor/Cooker/PlatformTools.h @@ -48,7 +48,7 @@ public: /// /// Gets the value indicating whenever platform requires AOT. /// - /// True if platform uses AOT and needs C# assemblies to be be precompiled, otherwise false. + /// True if platform uses AOT and needs C# assemblies to be precompiled, otherwise false. virtual bool UseAOT() const { return false; diff --git a/Source/Editor/Editor.h b/Source/Editor/Editor.h index 761e4b059..8c56043d9 100644 --- a/Source/Editor/Editor.h +++ b/Source/Editor/Editor.h @@ -43,7 +43,7 @@ public: public: /// - /// The flag used to determine if a project was used with the older engine version last time it was opened. Some cached data should be regenerated to prevent version difference issues. The version number comparision is based on major and minor part of the version. Build number is ignored. + /// The flag used to determine if a project was used with the older engine version last time it was opened. Some cached data should be regenerated to prevent version difference issues. The version number comparison is based on major and minor part of the version. Build number is ignored. /// static bool IsOldProjectOpened; diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 8840a7f06..cef9a9dec 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -221,7 +221,7 @@ namespace FlaxEditor.GUI } /// - /// Filters teh given value using the the . + /// Filters the given value using the . /// /// The mode. /// The value to process. diff --git a/Source/Editor/GUI/Dialogs/Dialog.cs b/Source/Editor/GUI/Dialogs/Dialog.cs index b8897b2e1..173dd9128 100644 --- a/Source/Editor/GUI/Dialogs/Dialog.cs +++ b/Source/Editor/GUI/Dialogs/Dialog.cs @@ -186,7 +186,7 @@ namespace FlaxEditor.GUI.Dialogs // Clean up _window = null; - // Check if any thead is blocked during ShowDialog, then wait for it + // Check if any thread is blocked during ShowDialog, then wait for it bool wait = true; while (wait) { diff --git a/Source/Editor/GUI/Table.cs b/Source/Editor/GUI/Table.cs index e070cba17..cca26cadc 100644 --- a/Source/Editor/GUI/Table.cs +++ b/Source/Editor/GUI/Table.cs @@ -120,7 +120,7 @@ namespace FlaxEditor.GUI /// /// Draws the column. /// - /// The the header area rectangle. + /// The header area rectangle. /// The zero-based index of the column. protected virtual void DrawColumn(ref Rectangle rect, int columnIndex) { diff --git a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs index abd3e78a8..a12a71e21 100644 --- a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs +++ b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs @@ -401,7 +401,7 @@ namespace FlaxEditor.GUI } /// - /// Converts the input point from editor editor contents control space into the keyframes time/value coordinates. + /// Converts the input point from editor contents control space into the keyframes time/value coordinates. /// /// The point. /// The keyframes contents area bounds. diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 57de46ee4..250fb73c3 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -179,7 +179,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks } /// - /// Evaluates the member value value at the specified time. + /// Evaluates the member value at the specified time. /// /// The time to evaluate the member at. /// The member value at provided time. diff --git a/Source/Editor/Modules/ContentFindingModule.cs b/Source/Editor/Modules/ContentFindingModule.cs index 88b5832af..9be15e0d1 100644 --- a/Source/Editor/Modules/ContentFindingModule.cs +++ b/Source/Editor/Modules/ContentFindingModule.cs @@ -108,7 +108,7 @@ namespace FlaxEditor.Modules /// /// Removes a quick action by name. /// - /// Thr action's name. + /// The action's name. /// True when it succeed, false if there is no Quick Action with this name. public bool RemoveQuickAction(string name) { diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 7c0cd11da..3c501de85 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.Options private readonly Dictionary _customSettings = new Dictionary(); /// - /// Gets the custom settings factories. Each entry defines the custom settings type identified by teh given key name. The value si a factory function that returns the default options fpr a given type. + /// Gets the custom settings factories. Each entry defines the custom settings type identified by the given key name. The value is a factory function that returns the default options for a given type. /// public IReadOnlyDictionary CustomSettings => _customSettings; diff --git a/Source/Editor/Progress/ProgressHandler.cs b/Source/Editor/Progress/ProgressHandler.cs index da2782d16..958612e51 100644 --- a/Source/Editor/Progress/ProgressHandler.cs +++ b/Source/Editor/Progress/ProgressHandler.cs @@ -79,7 +79,7 @@ namespace FlaxEditor.Progress } /// - /// Called when progress action gets updated (changed nfo text or progress value). + /// Called when progress action gets updated (changed info text or progress value). /// /// The progress (normalized to range [0;1]). /// The information text. diff --git a/Source/Editor/ProjectInfo.cpp b/Source/Editor/ProjectInfo.cpp index 0f916241e..47782ab9d 100644 --- a/Source/Editor/ProjectInfo.cpp +++ b/Source/Editor/ProjectInfo.cpp @@ -204,7 +204,7 @@ bool ProjectInfo::LoadProject(const String& projectPath) reference.Project = Load(referencePath); if (reference.Project == nullptr) { - LOG(Error, "Faield to load referenced project ({0}, from {1})", reference.Name, referencePath); + LOG(Error, "Failed to load referenced project ({0}, from {1})", reference.Name, referencePath); return true; } } @@ -277,7 +277,7 @@ bool ProjectInfo::LoadOldProject(const String& projectPath) flaxReference.Project = Load(Globals::StartupFolder / TEXT("Flax.flaxproj")); if (!flaxReference.Project) { - ShowProjectLoadError(TEXT("Failed to load Flax Engien project."), projectPath); + ShowProjectLoadError(TEXT("Failed to load Flax Engine project."), projectPath); return true; } diff --git a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp index d73c5279a..54ba119f2 100644 --- a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp +++ b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp @@ -49,7 +49,7 @@ public: { if (dwRejectType == SERVERCALL_RETRYLATER) { - // Retry immediatey + // Retry immediately return 99; } diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 9f9e21f4f..63faacb6d 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -1046,7 +1046,7 @@ namespace FlaxEditor.Surface.Archetypes startPos += nrm; endPos += nrm; - // Swap fo the other arrow + // Swap for the other arrow if (!diff) { var tmp = startPos; diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 7c4963072..14850a37d 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -1176,7 +1176,7 @@ namespace FlaxEditor.Surface.Archetypes [EditorOrder(0), Tooltip("The name of the parameter."), ExpandGroups] public string Name; - [EditorOrder(1), Tooltip("The type fo the parameter value.")] + [EditorOrder(1), Tooltip("The type for the parameter value.")] [TypeReference(typeof(object), nameof(IsTypeValid))] public ScriptType Type; @@ -1547,7 +1547,7 @@ namespace FlaxEditor.Surface.Archetypes // Check if return type has been changed if (_signature.ReturnType != prevReturnType) { - // Update all return nodes used by this function to match teh new type + // Update all return nodes used by this function to match the new type var usedNodes = DepthFirstTraversal(false); var hasAnyReturnNode = false; foreach (var node in usedNodes) diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index 43048d19d..5332b3475 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -32,7 +32,7 @@ namespace FlaxEditor.Surface.Archetypes public Dictionary Prototypes = DefaultPrototypes; /// - /// The default prototypes for thr node elements to use for the given parameter type. + /// The default prototypes for the node elements to use for the given parameter type. /// public static readonly Dictionary DefaultPrototypes = new Dictionary { diff --git a/Source/Editor/Surface/VisjectSurface.DragDrop.cs b/Source/Editor/Surface/VisjectSurface.DragDrop.cs index 323ef8510..422198f5b 100644 --- a/Source/Editor/Surface/VisjectSurface.DragDrop.cs +++ b/Source/Editor/Surface/VisjectSurface.DragDrop.cs @@ -107,7 +107,7 @@ namespace FlaxEditor.Surface /// Validates the parameter drag operation. /// /// Name of the parameter. - /// Tre if can drag that parameter, otherwise false. + /// True if can drag that parameter, otherwise false. protected virtual bool ValidateDragParameter(string parameterName) { return GetParameter(parameterName) != null; diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index ccabc8f2f..5a5a2e250 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -71,7 +71,7 @@ namespace FlaxEditor.Surface // Check if has cached groups if (_cache.Count != 0) { - // Check if context menu doesn;t have the recent cached groups + // Check if context menu doesn't have the recent cached groups if (!contextMenu.Groups.Any(g => g.Archetype.Tag is int asInt && asInt == _version)) { var groups = contextMenu.Groups.Where(g => g.Archetype.Tag is int).ToArray(); diff --git a/Source/Editor/Tools/Terrain/Paint/Mode.cs b/Source/Editor/Tools/Terrain/Paint/Mode.cs index 1a1373d05..e53071aa3 100644 --- a/Source/Editor/Tools/Terrain/Paint/Mode.cs +++ b/Source/Editor/Tools/Terrain/Paint/Mode.cs @@ -33,7 +33,7 @@ namespace FlaxEditor.Tools.Terrain.Paint } /// - /// The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or mre subtle. + /// The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or more subtle. /// [EditorOrder(0), Limit(0, 10, 0.01f), Tooltip("The tool strength (normalized to range 0-1). Defines the intensity of the paint operation to make it stronger or more subtle.")] public float Strength = 1.0f; diff --git a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs index 14fbe0175..eb32b3934 100644 --- a/Source/Editor/Tools/Terrain/Sculpt/Mode.cs +++ b/Source/Editor/Tools/Terrain/Sculpt/Mode.cs @@ -33,7 +33,7 @@ namespace FlaxEditor.Tools.Terrain.Sculpt } /// - /// The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or mre subtle. + /// The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or more subtle. /// [EditorOrder(0), Limit(0, 6, 0.01f), Tooltip("The tool strength (normalized to range 0-1). Defines the intensity of the sculpt operation to make it stronger or more subtle.")] public float Strength = 1.2f; diff --git a/Source/Editor/Tools/VertexPainting.cs b/Source/Editor/Tools/VertexPainting.cs index c130370e7..29d9daedc 100644 --- a/Source/Editor/Tools/VertexPainting.cs +++ b/Source/Editor/Tools/VertexPainting.cs @@ -49,7 +49,7 @@ namespace FlaxEditor.Tools set => Tab._gizmoMode.BrushStrength = value; } - [EditorOrder(20), EditorDisplay("Brush"), Limit(0.0f, 1.0f, 0.01f), Tooltip("The falloff parameter fo the brush. Adjusts the paint strength for the vertices that are far from the brush center. Use lower values to make painting smoother and softer.")] + [EditorOrder(20), EditorDisplay("Brush"), Limit(0.0f, 1.0f, 0.01f), Tooltip("The falloff parameter for the brush. Adjusts the paint strength for the vertices that are far from the brush center. Use lower values to make painting smoother and softer.")] public float BrushFalloff { get => Tab._gizmoMode.BrushFalloff; diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index 1571c39d1..d7136a5c8 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -247,7 +247,7 @@ void UpdateIconData(uint8* iconData, const TextureData* icon) iconTexSize = Math::RoundUpToPowerOf2(width); } - // Try to pick a proper mip (requrie the same size) + // Try to pick a proper mip (require the same size) const TextureMipData* srcPixels = nullptr; int32 mipLevels = icon->GetMipLevels(); for (int32 mipIndex = 0; mipIndex < mipLevels; mipIndex++) diff --git a/Source/Editor/Utilities/ObjectSnapshot.cs b/Source/Editor/Utilities/ObjectSnapshot.cs index a64ddf306..8f29d1145 100644 --- a/Source/Editor/Utilities/ObjectSnapshot.cs +++ b/Source/Editor/Utilities/ObjectSnapshot.cs @@ -251,7 +251,7 @@ namespace FlaxEditor.Utilities var list = new List(); #if DEBUG_OBJECT_SNAPSHOT_COMPARISION - Debug.Logger.LogHandler.LogWrite(LogType.Warning, "-------------- Comparision --------------"); + Debug.Logger.LogHandler.LogWrite(LogType.Warning, "-------------- Comparison --------------"); #endif for (int i = _members.Count - 1; i >= 0; i--) { diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index dfd83b1f8..100c5c3d7 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -126,7 +126,7 @@ namespace FlaxEditor.Utilities /// /// The first operator. /// The second operator. - /// The comparision result. + /// The comparison result. private static bool CompareOperators(string oper1, string oper2) { var op1 = Operators[oper1]; diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index c9e65d6e4..8ed219449 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -24,7 +24,7 @@ namespace FlaxEditor.Viewport.Cameras public bool IsAnimatingMove => _moveStartTime > Mathf.Epsilon; /// - /// The target point location. It's used to orbit around it whe user clicks Alt+LMB. + /// The target point location. It's used to orbit around it when user clicks Alt+LMB. /// public Vector3 TargetPoint = new Vector3(-200); diff --git a/Source/Editor/Windows/Assets/AssetEditorWindow.cs b/Source/Editor/Windows/Assets/AssetEditorWindow.cs index 756adbe05..92c697b88 100644 --- a/Source/Editor/Windows/Assets/AssetEditorWindow.cs +++ b/Source/Editor/Windows/Assets/AssetEditorWindow.cs @@ -288,7 +288,7 @@ namespace FlaxEditor.Windows.Assets public abstract class AssetEditorWindowBase : AssetEditorWindow where T : Asset { /// - /// Flag set to true if window is is waiting for asset to be loaded (to send or events). + /// Flag set to true if window is waiting for asset to be loaded (to send or events). /// protected bool _isWaitingForLoaded; diff --git a/Source/Engine/Core/Math/Color.cs b/Source/Engine/Core/Math/Color.cs index 5aee4c428..a865bde15 100644 --- a/Source/Engine/Core/Math/Color.cs +++ b/Source/Engine/Core/Math/Color.cs @@ -280,7 +280,7 @@ namespace FlaxEngine /// /// The hexadecimal string. /// Output value. - /// True if value has benn parsed, otherwise false. + /// True if value has been parsed, otherwise false. public static bool TryParseHex(string hexString, out Color value) { value = Black; diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h index 20836b300..8aa88756a 100644 --- a/Source/Engine/Graphics/Enums.h +++ b/Source/Engine/Graphics/Enums.h @@ -985,7 +985,7 @@ API_ENUM() enum class TessellationMethod enum class ShaderFlags : uint32 { /// - /// The default set fo flags. + /// The default set for flags. /// Default = 0, diff --git a/Source/Engine/Graphics/GPUBuffer.h b/Source/Engine/Graphics/GPUBuffer.h index 84611419e..97be261a4 100644 --- a/Source/Engine/Graphics/GPUBuffer.h +++ b/Source/Engine/Graphics/GPUBuffer.h @@ -215,7 +215,7 @@ public: /// Gets a CPU pointer to the resource by mapping its contents. Denies the GPU access to that resource. /// /// The map operation mode. - /// The pointer ot the mapped CPU buffer with resource data or null if failed. + /// The pointer of the mapped CPU buffer with resource data or null if failed. API_FUNCTION() virtual void* Map(GPUResourceMapMode mode) = 0; /// diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index a5195ff50..47ec6d3a6 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -239,7 +239,7 @@ void GPUDevice::preDispose() SAFE_DELETE_GPU_RESOURCE(_res->FullscreenTriangleVB); // Release GPU resources memory and unlink from device - // Note: after that noe GPU resources should be used/created, only deleted + // Note: after that no GPU resources should be used/created, only deleted Resources.OnDeviceDispose(); } diff --git a/Source/Engine/Graphics/GPUSwapChain.cpp b/Source/Engine/Graphics/GPUSwapChain.cpp index 347fa6e46..68b1486ad 100644 --- a/Source/Engine/Graphics/GPUSwapChain.cpp +++ b/Source/Engine/Graphics/GPUSwapChain.cpp @@ -39,7 +39,7 @@ Task* GPUSwapChain::DownloadDataAsync(TextureData& result) { if (_downloadTask) { - LOG(Warning, "Can download window backuffer data ony once at the time."); + LOG(Warning, "Can download window backuffer data only once at the time."); return nullptr; } diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index 8d5590354..09796c04b 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -108,7 +108,7 @@ public: /// /// Gets the mask of render passes supported by this material. /// - /// The drw passes supported by this material. + /// The draw passes supported by this material. virtual DrawPass GetDrawModes() const { return DrawPass::None; diff --git a/Source/Engine/Graphics/Mesh.cs b/Source/Engine/Graphics/Mesh.cs index f639bff8f..22579eb07 100644 --- a/Source/Engine/Graphics/Mesh.cs +++ b/Source/Engine/Graphics/Mesh.cs @@ -435,7 +435,7 @@ namespace FlaxEngine /// Downloads the third vertex buffer that contains mesh vertices data. To download data from GPU set to true and call this method from the thread other than main thread (see ). /// /// - /// If mesh has no vertex colors (stored in vertex buffer 2) the the returned value is null. + /// If mesh has no vertex colors (stored in vertex buffer 2) the returned value is null. /// /// If set to true the data will be downloaded from the GPU, otherwise it can be loaded from the drive (source asset file) or from memory (if cached). Downloading mesh from GPU requires this call to be made from the other thread than main thread. Virtual assets are always downloaded from GPU memory due to lack of dedicated storage container for the asset data. /// The gathered data or null if mesh has no vertex colors. diff --git a/Source/Engine/Graphics/Models/ModelData.Tool.cpp b/Source/Engine/Graphics/Models/ModelData.Tool.cpp index ee1b219dd..ee0fcb348 100644 --- a/Source/Engine/Graphics/Models/ModelData.Tool.cpp +++ b/Source/Engine/Graphics/Models/ModelData.Tool.cpp @@ -791,7 +791,7 @@ void MeshData::ImproveCacheLocality() Allocator::Free(piCandidates); const auto endTime = Platform::GetTimeSeconds(); - LOG(Info, "Cache relevant optimzie for {0} vertices and {1} indices. Average output ACMR is {2}. Time: {3}s", vertexCount, indexCount, (float)iCacheMisses / indexCount / 3, Utilities::RoundTo2DecimalPlaces(endTime - startTime)); + LOG(Info, "Cache relevant optimize for {0} vertices and {1} indices. Average output ACMR is {2}. Time: {3}s", vertexCount, indexCount, (float)iCacheMisses / indexCount / 3, Utilities::RoundTo2DecimalPlaces(endTime - startTime)); } float MeshData::CalculateTrianglesArea() const diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 4279cfc9b..400deb3c1 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -387,7 +387,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(ToneMappingSettings); float WhiteTemperature = 6500.0f; /// - /// Adjusts the white balance temperature tint for the scene by adjusting the cyan and magenta color ranges. Ideally, this setting should be used once you've adjusted the white balance temporature to get accurate colors. Under some light temperatures, the colors may appear to be more yellow or blue. This can be used to balance the resulting color to look more natural. The default value is `0`. + /// Adjusts the white balance temperature tint for the scene by adjusting the cyan and magenta color ranges. Ideally, this setting should be used once you've adjusted the white balance temperature to get accurate colors. Under some light temperatures, the colors may appear to be more yellow or blue. This can be used to balance the resulting color to look more natural. The default value is `0`. /// API_FIELD(Attributes="DefaultValue(0.0f), Limit(-1, 1, 0.001f), EditorOrder(1), PostProcessSetting((int)ToneMappingSettingsOverride.WhiteTint)") float WhiteTint = 0.0f; diff --git a/Source/Engine/Graphics/RenderTargetPool.cpp b/Source/Engine/Graphics/RenderTargetPool.cpp index 109ff8de9..a9db0283d 100644 --- a/Source/Engine/Graphics/RenderTargetPool.cpp +++ b/Source/Engine/Graphics/RenderTargetPool.cpp @@ -108,5 +108,5 @@ void RenderTargetPool::Release(GPUTexture* rt) } } - LOG(Error, "Trying to release temporary render target which has not been registred in service!"); + LOG(Error, "Trying to release temporary render target which has not been registered in service!"); } diff --git a/Source/Engine/Graphics/Shaders/GPUShader.h b/Source/Engine/Graphics/Shaders/GPUShader.h index 79b165396..76c8df546 100644 --- a/Source/Engine/Graphics/Shaders/GPUShader.h +++ b/Source/Engine/Graphics/Shaders/GPUShader.h @@ -97,7 +97,7 @@ public: /// /// Gets the vertex shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramVS* GetVS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -108,7 +108,7 @@ public: /// /// Gets the hull shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramHS* GetHS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -119,7 +119,7 @@ public: /// /// Gets domain shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramDS* GetDS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -130,7 +130,7 @@ public: /// /// Gets the geometry shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramGS* GetGS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -141,7 +141,7 @@ public: /// /// Gets the pixel shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramPS* GetPS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -152,7 +152,7 @@ public: /// /// Gets the compute shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// The shader object. API_FUNCTION() FORCE_INLINE GPUShaderProgramCS* GetCS(const StringAnsiView& name, int32 permutationIndex = 0) const @@ -176,7 +176,7 @@ public: /// /// Determines whether the specified shader program is in the shader. /// - /// Thr shader program name. + /// The shader program name. /// The shader permutation index. /// true if the shader is valid; otherwise, false. FORCE_INLINE bool HasShader(const StringAnsiView& name, int32 permutationIndex = 0) const diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h index d0ae27e7f..8b5385f9e 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.h +++ b/Source/Engine/Graphics/Textures/GPUTexture.h @@ -483,7 +483,7 @@ public: /// /// Creates new staging readback texture with the same dimensions and properties as a source texture (but without a data transferred; warning: caller must delete object). /// - /// Thr staging readback texture. + /// The staging readback texture. GPUTexture* ToStagingReadback() const; /// diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp index 63cd9bcb4..5df4a45a5 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp @@ -118,7 +118,7 @@ float GPUTimerQueryDX11::GetResult() if (!SingleShotLog) { SingleShotLog = true; - LOG(Warning, "Unrealiable GPU timer query detected."); + LOG(Warning, "Unreliable GPU timer query detected."); } #endif } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 29d417834..096a0ec11 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -391,7 +391,7 @@ bool GPUDeviceDX12::Init() { // Descriptor tables D3D12_DESCRIPTOR_RANGE r[2]; - // TODO: separate ranges for pixel/vertex visiblity and one shared for all? + // TODO: separate ranges for pixel/vertex visibility and one shared for all? { D3D12_DESCRIPTOR_RANGE& range = r[0]; range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h index b18ae9c1b..cf9b7e9cf 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h @@ -232,10 +232,10 @@ public: } /// - /// Converts Flax comparision function to the Vulkan comparision operation. + /// Converts Flax comparison function to the Vulkan comparison operation. /// - /// The Flax comparision function. - /// The Vulkan comparision operation. + /// The Flax comparison function. + /// The Vulkan comparison operation. static FORCE_INLINE VkCompareOp ToVulkanCompareOp(const ComparisonFunc value) { return ComparisonFuncToVkCompareOp[(int32)value]; diff --git a/Source/Engine/Input/Enums.h b/Source/Engine/Input/Enums.h index 764a17922..e397dd3c2 100644 --- a/Source/Engine/Input/Enums.h +++ b/Source/Engine/Input/Enums.h @@ -8,7 +8,7 @@ #define MAX_GAMEPADS 8 /// -/// Hardware mouse cursor behaviour. +/// Hardware mouse cursor behavior. /// API_ENUM() enum class CursorLockMode { diff --git a/Source/Engine/Input/Mouse.h b/Source/Engine/Input/Mouse.h index 15fa17e6d..94875ea3a 100644 --- a/Source/Engine/Input/Mouse.h +++ b/Source/Engine/Input/Mouse.h @@ -124,7 +124,7 @@ public: virtual void SetMousePosition(const Vector2& newPosition) = 0; /// - /// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programatically. + /// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programmatically. /// /// The new mouse position. void OnMouseMoved(const Vector2& newPosition) diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index cba13a601..a884c9707 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -98,7 +98,7 @@ void StaticModel::SetVertexColor(int32 lodIndex, int32 meshIndex, int32 vertexIn { if (!Model || Model->WaitForLoaded()) { - LOG(Warning, "Cannot set vertex color if model is missing or faied to load."); + LOG(Warning, "Cannot set vertex color if model is missing or failed to load."); return; } diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 18d9d4a52..04918f24b 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -961,7 +961,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, bool autoI } // Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it) - // TODO: resave and force sync scenes durign game cooking so this step could be skipped in game + // TODO: resave and force sync scenes during game cooking so this step could be skipped in game Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping); SceneObjectsFactory::SynchronizePrefabInstances(*sceneObjects.Value, actorToRemovedObjectsData, modifier.Value); Scripting::ObjectsLookupIdMapping.Set(nullptr); @@ -973,7 +973,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, bool autoI if (obj && obj->GetParent() == nullptr) { sceneObjects->At(i) = nullptr; - LOG(Warning, "Scene object {0} {1} has missing parent objct after scene load. Removing it.", obj->GetID(), obj->ToString()); + LOG(Warning, "Scene object {0} {1} has missing parent object after scene load. Removing it.", obj->GetID(), obj->ToString()); obj->DeleteObject(); } } diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 84bd17962..4305046e0 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -509,7 +509,7 @@ bool FindCyclicReferences(Actor* actor, const Guid& prefabRootId) bool Prefab::ApplyAll(Actor* targetActor) { - // TODO: use more cached dictionaries and other collections containers to prevent memory allocations during apply (optimize fo apply 10 times per second the same prefab on many changes in editor) + // TODO: use more cached dictionaries and other collections containers to prevent memory allocations during apply (optimize for apply 10 times per second the same prefab on many changes in editor) PROFILE_CPU(); const auto startTime = DateTime::NowUTC(); diff --git a/Source/Engine/Navigation/NavMesh.cpp b/Source/Engine/Navigation/NavMesh.cpp index 406aada30..d0bdba81f 100644 --- a/Source/Engine/Navigation/NavMesh.cpp +++ b/Source/Engine/Navigation/NavMesh.cpp @@ -79,7 +79,7 @@ void NavMesh::EnsureCapacity(int32 tilesToAddCount) // Ensure to have size assigned ASSERT(_tileSize != 0); - // Fre previous data (if any) + // Free previous data (if any) if (_navMesh) { dtFreeNavMesh(_navMesh); diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index 0f92d5f22..76af84011 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -103,7 +103,7 @@ protected: public: /// - /// The Particle Emitter Graph data version number. Used to sync the Particle Emitter Graph data with the instances state. Handles graph reloads to enure data is valid. + /// The Particle Emitter Graph data version number. Used to sync the Particle Emitter Graph data with the instances state. Handles graph reloads to ensure data is valid. /// uint32 Version = 0; diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index 8c6eeb59a..c836a7886 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -175,7 +175,7 @@ public: } /// - /// Attaches collider to the the specified rigid body. + /// Attaches collider to the specified rigid body. /// /// The rigid body. void Attach(RigidBody* rigidBody); diff --git a/Source/Engine/Physics/CollisionCooking.cpp b/Source/Engine/Physics/CollisionCooking.cpp index ea0f2f442..070f49936 100644 --- a/Source/Engine/Physics/CollisionCooking.cpp +++ b/Source/Engine/Physics/CollisionCooking.cpp @@ -13,7 +13,7 @@ #define ENSURE_CAN_COOK \ if (Physics::GetCooking() == nullptr) \ { \ - LOG(Warning, "Physics collisions cooking is disabled at runtime. Enable Physics Settigns option SupportCookingAtRuntime to use terrain generation at runtime."); \ + LOG(Warning, "Physics collisions cooking is disabled at runtime. Enable Physics Settings option SupportCookingAtRuntime to use terrain generation at runtime."); \ return true; \ } diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index f070c05f1..a68e40f72 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -346,7 +346,7 @@ static int X11_MessageBoxCreateWindow(MessageBoxData* data) { windowdata = data->Parent; windowdataWin = (X11::Window)windowdata->GetNativePtr(); - // TODO: place popup on the the screen that parent window is + // TODO: place popup on the screen that parent window is data->screen = X11_DefaultScreen(display); } else diff --git a/Source/Engine/Renderer/AmbientOcclusionPass.h b/Source/Engine/Renderer/AmbientOcclusionPass.h index 81d64a0e4..bfaf4bc19 100644 --- a/Source/Engine/Renderer/AmbientOcclusionPass.h +++ b/Source/Engine/Renderer/AmbientOcclusionPass.h @@ -60,7 +60,7 @@ private: float ShadowMultiplier; // [0.0, 5.0] Effect strength linear multiplier float ShadowPower; // [0.5, 5.0] Effect strength pow modifier float HorizonAngleThreshold; // [0.0, 0.2] Limits self-shadowing (makes the sampling area less of a hemisphere, more of a spherical cone, to avoid self-shadowing and various artifacts due to low tessellation and depth buffer imprecision, etc.) - float FadeOutFrom; // [0.0, ~ ] Distance to start start fading out the effect. + float FadeOutFrom; // [0.0, ~ ] Distance to start fading out the effect. float FadeOutTo; // [0.0, ~ ] Distance at which the effect is faded out. int QualityLevel; // [ 0, ] Effect quality; 0 - low, 1 - medium, 2 - high, 3 - very high; each quality level is roughly 2x more costly than the previous, except the q3 which is variable but, in general, above q2. int BlurPassCount; // [ 0, 6] Number of edge-sensitive smart blur passes to apply. Quality 0 is an exception with only one 'dumb' blur pass used. diff --git a/Source/Engine/Scripting/Attributes/Editor/EditorOrderAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/EditorOrderAttribute.cs index 506666def..5b783b514 100644 --- a/Source/Engine/Scripting/Attributes/Editor/EditorOrderAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/EditorOrderAttribute.cs @@ -27,7 +27,7 @@ namespace FlaxEngine /// /// Current order is resolved runtime, and can change if custom editor class has changed. /// - /// The order order. + /// The order. public EditorOrderAttribute(int order) { Order = order; diff --git a/Source/Engine/Scripting/Attributes/SerializeAttribute.cs b/Source/Engine/Scripting/Attributes/SerializeAttribute.cs index 3cda97473..d120756ea 100644 --- a/Source/Engine/Scripting/Attributes/SerializeAttribute.cs +++ b/Source/Engine/Scripting/Attributes/SerializeAttribute.cs @@ -5,7 +5,7 @@ using System; namespace FlaxEngine { /// - /// Indicates that a field or a property of a serializable class should be be serialized. This class cannot be inherited. + /// Indicates that a field or a property of a serializable class should be serialized. This class cannot be inherited. /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class SerializeAttribute : Attribute diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp b/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp index 50a8b689e..e24b71eff 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp @@ -303,7 +303,7 @@ bool MAssembly::LoadWithImage(const String& assemblyPath) const auto assembly = mono_assembly_load_from_full(assemblyImage, name.Substring(0, name.Length() - 3).Get(), &status, false); if (status != MONO_IMAGE_OK || assembly == nullptr) { - // Close image if error occured + // Close image if error occurred mono_image_close(assemblyImage); Log::CLRInnerException(TEXT("Mono assembly image is corrupted at ") + assemblyPath); diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.h b/Source/Engine/Scripting/ManagedCLR/MAssembly.h index ff8d5e8db..17cb8b624 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.h +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.h @@ -234,7 +234,7 @@ public: #endif /// - /// Gets the classes lookup cache. Performs ful initialization if not cached. The result cache contains all classes from the assembly. + /// Gets the classes lookup cache. Performs full initialization if not cached. The result cache contains all classes from the assembly. /// /// The cache. const ClassesDictionary& GetClasses() const; diff --git a/Source/Engine/Scripting/ManagedCLR/MClass.h b/Source/Engine/Scripting/ManagedCLR/MClass.h index bfa813fb4..a8d083d7b 100644 --- a/Source/Engine/Scripting/ManagedCLR/MClass.h +++ b/Source/Engine/Scripting/ManagedCLR/MClass.h @@ -193,7 +193,7 @@ public: /// /// Returns an object referencing a method with the specified name and number of parameters. /// - /// If the the type contains more than one method of the given name and parameters count the returned value can be non-deterministic (one of the matching methods). + /// If the type contains more than one method of the given name and parameters count the returned value can be non-deterministic (one of the matching methods). /// The method name. /// The method parameters count. /// The method or null if failed to get it. diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index 40ca2ed10..942edcd1a 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -190,7 +190,7 @@ struct FLAXENGINE_API ScriptingType SetupScriptObjectVTableHandler SetupScriptObjectVTable; /// - /// The default instance of the scripting type. Used by serialization system for comparision to save only modified properties of the object. + /// The default instance of the scripting type. Used by serialization system for comparison to save only modified properties of the object. /// mutable ScriptingObject* DefaultInstance; } Class; @@ -255,7 +255,7 @@ struct FLAXENGINE_API ScriptingType } /// - /// Gets the default instance of the scripting type. Used by serialization system for comparision to save only modified properties of the object. + /// Gets the default instance of the scripting type. Used by serialization system for comparison to save only modified properties of the object. /// ScriptingObject* GetDefaultInstance() const; diff --git a/Source/Engine/Serialization/JsonWriter.cpp b/Source/Engine/Serialization/JsonWriter.cpp index d3f04a684..de90b49b4 100644 --- a/Source/Engine/Serialization/JsonWriter.cpp +++ b/Source/Engine/Serialization/JsonWriter.cpp @@ -195,7 +195,7 @@ void JsonWriter::SceneObject(::SceneObject* obj) auto prefab = Content::Load(obj->GetPrefabID()); if (prefab) { - // Request the prefab to be deserialized to the default instance (used for comparision to generate a diff) + // Request the prefab to be deserialized to the default instance (used for comparison to generate a diff) prefab->GetDefaultInstance(); // Get prefab object instance from the prefab diff --git a/Source/Engine/Serialization/Stream.h b/Source/Engine/Serialization/Stream.h index de7911fb4..d988a24df 100644 --- a/Source/Engine/Serialization/Stream.h +++ b/Source/Engine/Serialization/Stream.h @@ -37,9 +37,9 @@ public: public: /// - /// Returns true if error occured during reading/writing to the stream + /// Returns true if error occurred during reading/writing to the stream /// - /// True if error occured during reading/writing to the stream + /// True if error occurred during reading/writing to the stream virtual bool HasError() const { return _hasError; diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index d15dd4cfa..38d1865bb 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -489,7 +489,7 @@ void Terrain::RemovePatch(const Int2& patchCoord) const auto patch = GetPatch(patchCoord); if (patch == nullptr) { - LOG(Warning, "Cannot remvoe patch at {0}x{1}. It does not exist.", patchCoord.X, patchCoord.Y); + LOG(Warning, "Cannot remove patch at {0}x{1}. It does not exist.", patchCoord.X, patchCoord.Y); return; } diff --git a/Source/Engine/Threading/ConcurrentBuffer.h b/Source/Engine/Threading/ConcurrentBuffer.h index 99159e206..99ebe5e86 100644 --- a/Source/Engine/Threading/ConcurrentBuffer.h +++ b/Source/Engine/Threading/ConcurrentBuffer.h @@ -382,7 +382,7 @@ public: /// Searches for the specified object and returns the zero-based index of the first occurrence within the entire collection. /// /// The item. - /// The zero-based index of the first occurrence of itm within the entire collection, if found; otherwise, INVALID_INDEX. + /// The zero-based index of the first occurrence of item within the entire collection, if found; otherwise, INVALID_INDEX. int32 IndexOf(const T& item) const { for (int32 i = 0; i < _count; i++) diff --git a/Source/Engine/Threading/IRunnable.h b/Source/Engine/Threading/IRunnable.h index 7dc42f73a..83a2af305 100644 --- a/Source/Engine/Threading/IRunnable.h +++ b/Source/Engine/Threading/IRunnable.h @@ -41,7 +41,7 @@ public: } // Called when thread ends work (via Kill or normally) - // @param wasKilled True if thead has been killed + // @param wasKilled True if thread has been killed virtual void AfterWork(bool wasKilled) { } diff --git a/Source/Engine/Threading/ThreadPool.cpp b/Source/Engine/Threading/ThreadPool.cpp index 56d6f6289..ffd80d655 100644 --- a/Source/Engine/Threading/ThreadPool.cpp +++ b/Source/Engine/Threading/ThreadPool.cpp @@ -52,7 +52,7 @@ bool ThreadPoolService::Init() // Create tread auto runnable = New(true); runnable->OnWork.Bind(ThreadPool::ThreadProc); - auto thread = Thread::Create(runnable, String::Format(TEXT("Therad Pool {0}"), i)); + auto thread = Thread::Create(runnable, String::Format(TEXT("Thread Pool {0}"), i)); if (thread == nullptr) { LOG(Error, "Failed to spawn {0} thread in the Thread Pool", i + 1); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index 4c2876125..757dfa6ea 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -744,7 +744,7 @@ bool ProcessMesh(OpenFbxImporterData& data, const ofbx::Mesh* aMesh, MeshData& m auto length = delta.Length(); if (length > ZeroTolerance) delta /= length;*/ - auto delta = Vector3::Zero; // TODO: blend shape normals deltas fix when importing from ofbx + auto delta = Vector3::Zero; // TODO: blend shape normals deltas fix when importing from fbx blendShapeData.Vertices[i].NormalDelta = delta; } } diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 3d6c74482..35cb3860b 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -1131,7 +1131,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt dstLod.Meshes.RemoveAt(i--); } - LOG(Info, "Generated LOD{0}: triangles: {1} ({2}% of base LOD), verteces: {3} ({4}% of base LOD)", + LOG(Info, "Generated LOD{0}: triangles: {1} ({2}% of base LOD), verticies: {3} ({4}% of base LOD)", lodIndex, lodTriangleCount, (int32)(lodTriangleCount * 100 / baseLodTriangleCount), lodVertexCount, (int32)(lodVertexCount * 100 / baseLodVertexCount)); diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs index 44f8c782a..fed4335c0 100644 --- a/Source/Engine/UI/GUI/ContainerControl.cs +++ b/Source/Engine/UI/GUI/ContainerControl.cs @@ -469,7 +469,7 @@ namespace FlaxEngine.GUI } /// - /// Checks if given point in thi container control space intersects with the child control content. + /// Checks if given point in this container control space intersects with the child control content. /// Also calculates result location in child control space which can be used to feed control with event at that point. /// /// The child control to check. diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs index 74041610c..1c4eda965 100644 --- a/Source/Engine/UI/GUI/Control.cs +++ b/Source/Engine/UI/GUI/Control.cs @@ -1211,7 +1211,7 @@ namespace FlaxEngine.GUI } /// - /// Action fred when parent control gets changed. + /// Action fired when parent control gets changed. /// protected virtual void OnParentChangedInternal() { @@ -1252,9 +1252,9 @@ namespace FlaxEngine.GUI } /// - /// Helper utility function to sets the update callback to the root. Does nothing if value has not been modified. Handles if control ahs no root or parent. + /// Helper utility function to sets the update callback to the root. Does nothing if value has not been modified. Handles if control has no root or parent. /// - /// The cached update callback delegate (field in teh custom control implementation). + /// The cached update callback delegate (field in the custom control implementation). /// The value to assign. protected void SetUpdate(ref UpdateDelegate onUpdate, UpdateDelegate value) { @@ -1268,7 +1268,7 @@ namespace FlaxEngine.GUI } /// - /// Action fred when parent control gets resized (also when control gets non-null parent). + /// Action fired when parent control gets resized (also when control gets non-null parent). /// public virtual void OnParentResized() { diff --git a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs index 089907901..62c6a9744 100644 --- a/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs +++ b/Source/Engine/UI/GUI/Panels/UniformGridPanel.cs @@ -14,7 +14,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the padding given to each slot. /// - [EditorOrder(0), Tooltip("The padding margin appied to each item slot.")] + [EditorOrder(0), Tooltip("The padding margin applied to each item slot.")] public Margin SlotPadding { get => _slotPadding; diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index 9ccbef214..f07910975 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -186,14 +186,14 @@ namespace FlaxEngine public CanvasRootControl GUI => _guiRoot; /// - /// Delegate schema for the callback used to perform custom canvas intersection test. Can be used to implement a canvas tha has a holes or non-rectangular shape. + /// Delegate schema for the callback used to perform custom canvas intersection test. Can be used to implement a canvas that has a holes or non-rectangular shape. /// /// The location of the point to test in coordinates of the canvas root control (see ). /// True if canvas was hit, otherwise false. public delegate bool TestCanvasIntersectionDelegate(ref Vector2 location); /// - /// The callback used to perform custom canvas intersection test. Can be used to implement a canvas tha has a holes or non-rectangular shape. + /// The callback used to perform custom canvas intersection test. Can be used to implement a canvas that has a holes or non-rectangular shape. /// [HideInEditor] public TestCanvasIntersectionDelegate TestCanvasIntersection; diff --git a/Source/Shaders/BakeLightmap.shader b/Source/Shaders/BakeLightmap.shader index 4f206469e..14f99b4f4 100644 --- a/Source/Shaders/BakeLightmap.shader +++ b/Source/Shaders/BakeLightmap.shader @@ -328,7 +328,7 @@ void CS_BlurEmpty(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThre const int2 location = int2(GroupID.x, GroupID.y); const uint texelAdress = (location.y * AtlasSize + location.x) * NUM_SH_TARGETS; - // TODO: use more therads to sample lightmap and final therad make it blur + // TODO: use more threads to sample lightmap and final therad make it blur // Simple box filter (using only valid samples) const int blurRadius = 2; diff --git a/Source/Shaders/BitonicSort.shader b/Source/Shaders/BitonicSort.shader index b75f553b1..8a53802d0 100644 --- a/Source/Shaders/BitonicSort.shader +++ b/Source/Shaders/BitonicSort.shader @@ -32,7 +32,7 @@ uint InsertOneBit(uint value, uint oneBitMask) // Determines if two sort keys should be swapped in the list. KeySign is // either 1 or -1. Multiplication with the KeySign will either invert the sign -// (effectively a negation) or leave the value alone. When the the KeySign is +// (effectively a negation) or leave the value alone. When the KeySign is // 1, we are sorting descending, so when A < B, they should swap. For an // ascending sort, -A < -B should swap. bool ShouldSwap(Item a, Item b) From 4d8cc9aef7d220c3992494dea552fa239b915f31 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Tue, 5 Jan 2021 02:14:21 +0100 Subject: [PATCH 034/419] Fixed additional typos Went through the source with VNNCC to correct as many found typos as possible Co-Authored-By: VNC <52937757+VNNCC@users.noreply.github.com> --- .../Editor/Modules/ContentImportingModule.cs | 2 +- Source/Engine/CSG/CSGMesh.Triangulate.cpp | 16 ++++++++-------- Source/Engine/CSG/CSGMesh.cpp | 8 ++++---- Source/Engine/Debug/Exception.h | 2 +- .../Graph/CPU/ParticleEmitterGraph.CPU.h | 2 +- .../Engine/Particles/ParticlesSimulation.cpp | 2 +- Source/Engine/Render2D/Font.cpp | 2 +- Source/Engine/Render2D/FontManager.cpp | 2 +- Source/Engine/Serialization/Stream.h | 4 ++-- Source/Engine/Streaming/StreamingManager.cpp | 2 +- .../Tools/MaterialGenerator/MaterialLayer.cpp | 2 +- .../Tools/ModelTool/ModelTool.Assimp.cpp | 2 +- .../Engine/Tools/TextureTool/TextureTool.cpp | 4 ++-- Source/Engine/UI/GUI/Common/Dropdown.cs | 2 +- Source/Engine/UI/GUI/ContainerControl.cs | 2 +- Source/Engine/UI/GUI/Panels/SplitPanel.cs | 16 ++++++++-------- Source/Engine/UI/GUI/WindowRootControl.cs | 8 ++++---- Source/Engine/Utilities/StateMachine.h | 2 +- .../Build/Plugins/VisualScriptingPlugin.cs | 2 +- Source/Tools/Flax.Build/Build/Target.cs | 2 +- Source/Tools/Flax.Stats/CodeFrameNode.cs | 18 +++++++++--------- Source/Tools/Flax.Stats/Tools.cs | 4 ++-- 22 files changed, 53 insertions(+), 53 deletions(-) diff --git a/Source/Editor/Modules/ContentImportingModule.cs b/Source/Editor/Modules/ContentImportingModule.cs index 03f434b9c..0c934f130 100644 --- a/Source/Editor/Modules/ContentImportingModule.cs +++ b/Source/Editor/Modules/ContentImportingModule.cs @@ -410,7 +410,7 @@ namespace FlaxEditor.Modules { if (request.Settings != null && entry.TryOverrideSettings(request.Settings)) { - // Use overriden settings + // Use overridden settings } else if (!request.SkipSettingsDialog) { diff --git a/Source/Engine/CSG/CSGMesh.Triangulate.cpp b/Source/Engine/CSG/CSGMesh.Triangulate.cpp index bb814653b..8a64f4042 100644 --- a/Source/Engine/CSG/CSGMesh.Triangulate.cpp +++ b/Source/Engine/CSG/CSGMesh.Triangulate.cpp @@ -22,7 +22,7 @@ bool CSG::Mesh::Triangulate(RawData& data, Array& cacheVB) const Array surfaceCacheVB(32); // Cache submeshes by material to lay them down - // key- brush index, value- direcotry for surafecs (key: surface index, value: list with start vertex per triangle) + // key- brush index, value- direcotry for surfaces (key: surface index, value: list with start vertex per triangle) Dictionary>> polygonsPerBrush(64); // Build index buffer @@ -115,8 +115,8 @@ bool CSG::Mesh::Triangulate(RawData& data, Array& cacheVB) const tangent.Normalize(); // Gram-Schmidt orthogonalize - Vector3 newTangentUnormalized = tangent - normal * Vector3::Dot(normal, tangent); - const float length = newTangentUnormalized.Length(); + Vector3 newTangentUnnormalized = tangent - normal * Vector3::Dot(normal, tangent); + const float length = newTangentUnnormalized.Length(); // Workaround to handle degenerated case if (Math::IsZero(length)) @@ -129,7 +129,7 @@ bool CSG::Mesh::Triangulate(RawData& data, Array& cacheVB) const } else { - tangent = newTangentUnormalized / length; + tangent = newTangentUnnormalized / length; bitangent.Normalize(); } @@ -217,12 +217,12 @@ bool CSG::Mesh::Triangulate(RawData& data, Array& cacheVB) const auto& vertex = cacheVB[triangleStartVertex + k]; Vector3 projected = Vector3::Project(vertex.Position, 0, 0, 1000, 1000, 0, 1, vp); - Vector2 projectecXY = Vector2(projected); + Vector2 projectedXY = Vector2(projected); - min = Vector2::Min(projectecXY, min); - max = Vector2::Max(projectecXY, max); + min = Vector2::Min(projectedXY, min); + max = Vector2::Max(projectedXY, max); - pointsCache.Add(projectecXY); + pointsCache.Add(projectedXY); } } diff --git a/Source/Engine/CSG/CSGMesh.cpp b/Source/Engine/CSG/CSGMesh.cpp index cc4897d3a..9a885a35d 100644 --- a/Source/Engine/CSG/CSGMesh.cpp +++ b/Source/Engine/CSG/CSGMesh.cpp @@ -38,7 +38,7 @@ void CSG::Mesh::PerformOperation(Mesh* other) { case Mode::Additive: { - // Check if both meshes do not intesect + // Check if both meshes do not intersect if (AABB::IsOutside(_bounds, other->GetBounds())) // TODO: test every sub bounds not whole _bounds { // Add vertices to the mesh without any additional calculations @@ -57,7 +57,7 @@ void CSG::Mesh::PerformOperation(Mesh* other) case Mode::Subtractive: { - // Check if both meshes do not intesect + // Check if both meshes do not intersect if (AABB::IsOutside(_bounds, other->GetBounds())) // TODO: test every sub bounds not whole _bounds { // Do nothing @@ -141,7 +141,7 @@ void CSG::Mesh::intersect(const Mesh* other, PolygonOperation insideOp, PolygonO // insideOp - operation for polygons being inside the other brush // outsideOp - operation for polygons being outside the other brush - // Prevent from redudant action + // Prevent from redundant action if (insideOp == Keep && outsideOp == Keep) return; @@ -180,7 +180,7 @@ void CSG::Mesh::intersectSubMesh(const Mesh* other, int32 subMeshIndex, PolygonO int32 startBrushSurface = brushMeta.StartSurfaceIndex; int32 endBrushSurface = startBrushSurface + brushMeta.SurfacesCount; - // Check every polygon (itereate fron end since we can ass new polygons and we don't want to process them) + // Check every polygon (iterate from end since we can ass new polygons and we don't want to process them) for (int32 i = _polygons.Count() - 1; i >= 0; i--) { auto& polygon = _polygons[i]; diff --git a/Source/Engine/Debug/Exception.h b/Source/Engine/Debug/Exception.h index 481c78589..5bef7aa55 100644 --- a/Source/Engine/Debug/Exception.h +++ b/Source/Engine/Debug/Exception.h @@ -36,7 +36,7 @@ namespace Log /// /// Additional information that help describe error Exception(const String& additionalInfo) - : Exception(TEXT("An exception has occured."), additionalInfo) + : Exception(TEXT("An exception has occurred."), additionalInfo) { } diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h index a93c50ec2..cc1b5b6b2 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h @@ -100,7 +100,7 @@ public: public: /// - /// Determinates whenever this emitter uses lights rendering. + /// Determinate whenever this emitter uses lights rendering. /// /// True if emitter uses lights rendering, otherwise false. FORCE_INLINE bool UsesLightRendering() const diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index bbba833ff..0b6e3691d 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -115,7 +115,7 @@ int32 ParticleSystemInstance::GetParticlesCount() const const auto desc = GPUBufferDescription::Buffer(Emitters.Count() * sizeof(uint32), GPUBufferFlags::None, PixelFormat::Unknown, nullptr, sizeof(uint32), GPUResourceUsage::StagingReadback); if (GPUParticlesCountReadback->Init(desc)) { - LOG(Error, "Failed to create GPU particles coun readback buffer."); + LOG(Error, "Failed to create GPU particles count readback buffer."); } } diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index c9dd7b55c..209989d4f 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -33,7 +33,7 @@ Font::~Font() void Font::GetCharacter(Char c, FontCharacterEntry& result) { - // Try get character or cache it if cannot find + // Try to get the character or cache it if cannot be found if (!_characters.TryGet(c, result)) { // This thread race condition may happen in editor but in game we usually do all stuff with fonts on main thread (chars caching) diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp index f05cd7280..ec7a61257 100644 --- a/Source/Engine/Render2D/FontManager.cpp +++ b/Source/Engine/Render2D/FontManager.cpp @@ -214,7 +214,7 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) return false; } - // Copy glyph data after rasterize (row by row) + // Copy glyph data after rasterization (row by row) for (int32 row = 0; row < glyphHeight; row++) { Platform::MemoryCopy(&GlyphImageData[row * glyphWidth], &bitmap->buffer[row * bitmap->pitch], glyphWidth); diff --git a/Source/Engine/Serialization/Stream.h b/Source/Engine/Serialization/Stream.h index de7911fb4..d988a24df 100644 --- a/Source/Engine/Serialization/Stream.h +++ b/Source/Engine/Serialization/Stream.h @@ -37,9 +37,9 @@ public: public: /// - /// Returns true if error occured during reading/writing to the stream + /// Returns true if error occurred during reading/writing to the stream /// - /// True if error occured during reading/writing to the stream + /// True if error occurred during reading/writing to the stream virtual bool HasError() const { return _hasError; diff --git a/Source/Engine/Streaming/StreamingManager.cpp b/Source/Engine/Streaming/StreamingManager.cpp index d82a3b5ab..3b557e85f 100644 --- a/Source/Engine/Streaming/StreamingManager.cpp +++ b/Source/Engine/Streaming/StreamingManager.cpp @@ -92,7 +92,7 @@ void UpdateResource(StreamableResource* resource, DateTime now) } } - // Calculate residency level to stream in (resources may want to incease/decrease it's quality in steps rather than at once) + // Calculate residency level to stream in (resources may want to increase/decrease it's quality in steps rather than at once) int32 requestedResidency = handler->CalculateRequestedResidency(resource, targetResidency); // Create streaming task (resource type specific) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp index c7f940574..41679a6a3 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialLayer.cpp @@ -64,7 +64,7 @@ void MaterialLayer::Prepare() Guid MaterialLayer::GetMappedParamId(const Guid& id) { - // TODO: test ParamIdsMappings using Dictionary. will performance change? mamybe we don't wont to allocate too much memory + // TODO: test ParamIdsMappings using Dictionary. will performance change? maybe we don't wont to allocate too much memory for (int32 i = 0; i < ParamIdsMappings.Count(); i++) { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp index fef8d7f3c..50914ea2e 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Assimp.cpp @@ -355,7 +355,7 @@ bool ProcessMesh(AssimpImporterData& data, const aiMesh* aMesh, MeshData& mesh, mesh.BlendIndices.SetAll(Int4::Zero); mesh.BlendWeights.SetAll(Vector4::Zero); - // Build skinning clusters and fill controls points data stutcture + // Build skinning clusters and fill controls points data structure for (unsigned boneId = 0; boneId < aMesh->mNumBones; boneId++) { const auto aBone = aMesh->mBones[boneId]; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index a8af48764..97e4df253 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -316,7 +316,7 @@ bool TextureTool::Convert(TextureData& dst, const TextureData& src, const PixelF } if (src.Format == dstFormat) { - LOG(Warning, "Soure data and destination format are the same. Cannot perform conversion."); + LOG(Warning, "Source data and destination format are the same. Cannot perform conversion."); return true; } if (src.Depth != 1) @@ -343,7 +343,7 @@ bool TextureTool::Resize(TextureData& dst, const TextureData& src, int32 dstWidt } if (src.Width == dstWidth && src.Height == dstHeight) { - LOG(Warning, "Soure data and destination dimensions are the same. Cannot perform resizing."); + LOG(Warning, "Source data and destination dimensions are the same. Cannot perform resizing."); return true; } if (src.Depth != 1) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index e78300e1a..917e51f6e 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -26,7 +26,7 @@ namespace FlaxEngine.GUI public Action ItemClicked; /// - /// Occurs when popup losts focus. + /// Occurs when popup lost focus. /// public Action LostFocus; diff --git a/Source/Engine/UI/GUI/ContainerControl.cs b/Source/Engine/UI/GUI/ContainerControl.cs index 44f8c782a..0fecef9ef 100644 --- a/Source/Engine/UI/GUI/ContainerControl.cs +++ b/Source/Engine/UI/GUI/ContainerControl.cs @@ -636,7 +636,7 @@ namespace FlaxEngine.GUI } /// - /// Draws the children. Can be overriden to provide some customizations. Draw is performed with applied clipping mask for the client area. + /// Draws the children. Can be overridden to provide some customizations. Draw is performed with applied clipping mask for the client area. /// protected virtual void DrawChildren() { diff --git a/Source/Engine/UI/GUI/Panels/SplitPanel.cs b/Source/Engine/UI/GUI/Panels/SplitPanel.cs index 733e0b3a1..04160db25 100644 --- a/Source/Engine/UI/GUI/Panels/SplitPanel.cs +++ b/Source/Engine/UI/GUI/Panels/SplitPanel.cs @@ -12,12 +12,12 @@ namespace FlaxEngine.GUI /// /// The splitter size (in pixels). /// - public const int SpliterSize = 4; + public const int SplitterSize = 4; /// /// The splitter half size (in pixels). /// - private const int SpliterSizeHalf = SpliterSize / 2; + private const int SplitterSizeHalf = SplitterSize / 2; private Orientation _orientation; private float _splitterValue; @@ -105,12 +105,12 @@ namespace FlaxEngine.GUI if (_orientation == Orientation.Horizontal) { var split = Mathf.RoundToInt(_splitterValue * Width); - _splitterRect = new Rectangle(Mathf.Clamp(split - SpliterSizeHalf, 0.0f, Width), 0, SpliterSize, Height); + _splitterRect = new Rectangle(Mathf.Clamp(split - SplitterSizeHalf, 0.0f, Width), 0, SplitterSize, Height); } else { var split = Mathf.RoundToInt(_splitterValue * Height); - _splitterRect = new Rectangle(0, Mathf.Clamp(split - SpliterSizeHalf, 0.0f, Height), Width, SpliterSize); + _splitterRect = new Rectangle(0, Mathf.Clamp(split - SplitterSizeHalf, 0.0f, Height), Width, SplitterSize); } } @@ -226,14 +226,14 @@ namespace FlaxEngine.GUI if (_orientation == Orientation.Horizontal) { var split = Mathf.RoundToInt(_splitterValue * Width); - Panel1.Bounds = new Rectangle(0, 0, split - SpliterSizeHalf, Height); - Panel2.Bounds = new Rectangle(split + SpliterSizeHalf, 0, Width - split - SpliterSizeHalf, Height); + Panel1.Bounds = new Rectangle(0, 0, split - SplitterSizeHalf, Height); + Panel2.Bounds = new Rectangle(split + SplitterSizeHalf, 0, Width - split - SplitterSizeHalf, Height); } else { var split = Mathf.RoundToInt(_splitterValue * Height); - Panel1.Bounds = new Rectangle(0, 0, Width, split - SpliterSizeHalf); - Panel2.Bounds = new Rectangle(0, split + SpliterSizeHalf, Width, Height - split - SpliterSizeHalf); + Panel1.Bounds = new Rectangle(0, 0, Width, split - SplitterSizeHalf); + Panel2.Bounds = new Rectangle(0, split + SplitterSizeHalf, Width, Height - split - SplitterSizeHalf); } } } diff --git a/Source/Engine/UI/GUI/WindowRootControl.cs b/Source/Engine/UI/GUI/WindowRootControl.cs index 03a994aa6..23330ffae 100644 --- a/Source/Engine/UI/GUI/WindowRootControl.cs +++ b/Source/Engine/UI/GUI/WindowRootControl.cs @@ -262,14 +262,14 @@ namespace FlaxEngine.GUI return false; // Change focused control - Control prevous = _focusedControl; + Control previous = _focusedControl; _focusedControl = c; // Fire events - if (prevous != null) + if (previous != null) { - prevous.OnLostFocus(); - Assert.IsFalse(prevous.IsFocused); + previous.OnLostFocus(); + Assert.IsFalse(previous.IsFocused); } if (_focusedControl != null) { diff --git a/Source/Engine/Utilities/StateMachine.h b/Source/Engine/Utilities/StateMachine.h index 292a67978..38e69a06b 100644 --- a/Source/Engine/Utilities/StateMachine.h +++ b/Source/Engine/Utilities/StateMachine.h @@ -50,7 +50,7 @@ public: /// /// Checks if can exit from that state /// - /// Next state to ener after exit from the current state + /// Next state to enter after exit from the current state /// True if can exit from that state, otherwise false virtual bool CanExit(State* nextState) const { diff --git a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs index 8ebe13518..ecfd97d88 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/VisualScriptingPlugin.cs @@ -22,7 +22,7 @@ namespace Flax.Build.Plugins private void OnGenerateCppScriptWrapperFunction(Builder.BuildData buildData, ClassInfo classInfo, FunctionInfo functionInfo, int scriptVTableSize, int scriptVTableIndex, StringBuilder contents) { - // Generate C++ wrapper function to invoke Visual Script instead of overriden native function (with support for base method callback) + // Generate C++ wrapper function to invoke Visual Script instead of overridden native function (with support for base method callback) BindingsGenerator.CppIncludeFiles.Add("Engine/Content/Assets/VisualScript.h"); diff --git a/Source/Tools/Flax.Build/Build/Target.cs b/Source/Tools/Flax.Build/Build/Target.cs index b7a6b2fbe..ebf1d36c9 100644 --- a/Source/Tools/Flax.Build/Build/Target.cs +++ b/Source/Tools/Flax.Build/Build/Target.cs @@ -225,7 +225,7 @@ namespace Flax.Build } /// - /// Setups the target building environment (native C++). Allows to modify compiler and linker options. Options applied here are used by all modules included into this target (can be overriden per module). + /// Setups the target building environment (native C++). Allows to modify compiler and linker options. Options applied here are used by all modules included into this target (can be overridden per module). /// /// The build options. public virtual void SetupTargetEnvironment(BuildOptions options) diff --git a/Source/Tools/Flax.Stats/CodeFrameNode.cs b/Source/Tools/Flax.Stats/CodeFrameNode.cs index 25d326ff4..e42bdcfef 100644 --- a/Source/Tools/Flax.Stats/CodeFrameNode.cs +++ b/Source/Tools/Flax.Stats/CodeFrameNode.cs @@ -75,14 +75,14 @@ namespace Flax.Stats /// /// Gets total amount of memory used by that node and all child nodes /// - public long TotaSizeOnDisk + public long TotalSizeOnDisk { get { long result = SizeOnDisk; for (int i = 0; i < Children.Length; i++) { - result += Children[i].TotaSizeOnDisk; + result += Children[i].TotalSizeOnDisk; } return result; } @@ -153,15 +153,15 @@ namespace Flax.Stats /// /// Gets total amount of lines of code per language /// - /// Language + /// Language /// Result amount of lines - public long GetTotalLinesOfCode(Languages languge) + public long GetTotalLinesOfCode(Languages language) { long result = 0; - result += LinesOfCode[(int)languge]; + result += LinesOfCode[(int)language]; for (int i = 0; i < Children.Length; i++) { - result += Children[i].GetTotalLinesOfCode(languge); + result += Children[i].GetTotalLinesOfCode(language); } return result; } @@ -270,9 +270,9 @@ namespace Flax.Stats public void CleanupDirectories() { - var chld = Children.ToList(); - chld.RemoveAll(e => ignoredFolders.Contains(e.ShortName.ToLower())); - Children = chld.ToArray(); + var child = Children.ToList(); + child.RemoveAll(e => ignoredFolders.Contains(e.ShortName.ToLower())); + Children = child.ToArray(); foreach (var a in Children) { diff --git a/Source/Tools/Flax.Stats/Tools.cs b/Source/Tools/Flax.Stats/Tools.cs index d947ae758..1e3ba1f74 100644 --- a/Source/Tools/Flax.Stats/Tools.cs +++ b/Source/Tools/Flax.Stats/Tools.cs @@ -103,7 +103,7 @@ namespace Flax.Stats } /// - /// Write string in UTF-8 encoding to the stream and ofset data + /// Write string in UTF-8 encoding to the stream and offset data /// /// File stream /// Data to write @@ -292,7 +292,7 @@ namespace Flax.Stats } /// - /// Write arry of Guids to the stream + /// Write array of Guids to the stream /// /// File stream /// Value to write From be319c446dac20eb505e6e97ab475d02a4904605 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 5 Jan 2021 14:14:34 +0100 Subject: [PATCH 035/419] Refactor settings types to use scripting API --- .../Settings/AndroidPlatformSettings.cs | 30 -- .../Editor/Content/Settings/AudioSettings.cs | 34 --- .../Editor/Content/Settings/BuildSettings.cs | 67 +---- .../Editor/Content/Settings/GameSettings.cs | 23 +- .../Content/Settings/GraphicsSettings.cs | 61 ----- .../Editor/Content/Settings/InputSettings.cs | 5 +- .../Content/Settings/LayersAndTagsSettings.cs | 5 +- .../Content/Settings/LinuxPlatformSettings.cs | 60 ---- .../Content/Settings/PhysicsSettings.cs | 97 +------ .../Editor/Content/Settings/TimeSettings.cs | 48 ---- .../Content/Settings/UWPPlatformSettings.cs | 97 ------- .../Settings/WindowsPlatformSettings.cs | 90 ------ Source/Editor/Cooker/GameCooker.cpp | 42 --- .../Platform/Android/AndroidPlatformTools.cpp | 20 +- .../Platform/Linux/LinuxPlatformTools.cpp | 6 +- .../Cooker/Platform/UWP/UWPPlatformTools.cpp | 17 +- .../Platform/Windows/WindowsPlatformTools.cpp | 6 +- Source/Editor/Cooker/Steps/CookAssetsStep.cpp | 39 +-- Source/Editor/Cooker/Steps/DeployDataStep.cpp | 5 +- Source/Editor/Cooker/Steps/ValidateStep.cpp | 13 +- Source/Editor/Editor.Build.cs | 2 +- .../Editor/Managed/ManagedEditor.Internal.cpp | 18 +- Source/Editor/Utilities/EditorUtilities.cpp | 6 +- Source/Engine/Audio/Audio.cpp | 22 +- Source/Engine/Audio/AudioSettings.h | 19 +- .../Engine/Audio/OpenAL/AudioBackendOAL.cpp | 2 +- .../Audio/XAudio2/AudioBackendXAudio2.cpp | 2 +- Source/Engine/Content/JsonAsset.cpp | 2 + Source/Engine/Content/JsonAsset.h | 6 +- Source/Engine/Core/Config/BuildSettings.h | 32 ++- Source/Engine/Core/Config/GameSettings.cpp | 259 ++++++++++-------- Source/Engine/Core/Config/GameSettings.h | 56 +++- Source/Engine/Core/Config/GraphicsSettings.h | 29 +- .../Engine/Core/Config/LayersTagsSettings.h | 39 +-- Source/Engine/Core/Config/Settings.h | 85 ++---- Source/Engine/Core/Config/TimeSettings.h | 35 +-- Source/Engine/Core/Math/Math.cpp | 4 +- Source/Engine/Engine/Base/GameBase.cpp | 7 +- Source/Engine/Engine/Engine.cpp | 17 +- Source/Engine/Engine/Linux/LinuxGame.cpp | 4 +- Source/Engine/Engine/Time.cpp | 28 +- Source/Engine/Engine/Time.h | 2 + Source/Engine/Engine/Windows/WindowsGame.cpp | 4 +- .../DirectX/DX11/GPUDeviceDX11.cpp | 2 +- .../DirectX/DX12/GPUDeviceDX12.cpp | 2 +- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 2 +- Source/Engine/Input/Input.cpp | 6 + Source/Engine/Input/Input.h | 1 - Source/Engine/Input/InputSettings.h | 24 +- Source/Engine/Level/Actor.cpp | 17 +- Source/Engine/Level/Level.cpp | 32 +++ Source/Engine/Level/Level.h | 25 ++ Source/Engine/Navigation/NavMeshBuilder.cpp | 4 +- Source/Engine/Navigation/Navigation.cpp | 6 + .../Engine/Navigation/NavigationSettings.cs | 8 + Source/Engine/Navigation/NavigationSettings.h | 24 +- Source/Engine/Physics/Colliders/Collider.cpp | 2 +- Source/Engine/Physics/PhysicalMaterial.cpp | 52 ---- Source/Engine/Physics/Physics.cpp | 216 ++++++++++++--- Source/Engine/Physics/Physics.h | 5 + Source/Engine/Physics/PhysicsSettings.cpp | 82 ------ Source/Engine/Physics/PhysicsSettings.h | 161 ++++++----- Source/Engine/Physics/Utilities.h | 32 +-- .../Android/AndroidPlatformSettings.h | 24 +- .../Platform/Linux/LinuxPlatformSettings.h | 41 +-- Source/Engine/Platform/Platform.Build.cs | 10 + .../Engine/Platform/UWP/UWPPlatformSettings.h | 27 +- .../Windows/WindowsPlatformSettings.h | 45 +-- Source/Engine/Terrain/Terrain.cpp | 2 +- .../Bindings/BindingsGenerator.CSharp.cs | 14 + .../Bindings/BindingsGenerator.Cpp.cs | 11 +- .../Bindings/BindingsGenerator.Parsing.cs | 3 +- .../Flax.Build/Bindings/BindingsGenerator.cs | 45 +-- Source/Tools/Flax.Build/Bindings/FieldInfo.cs | 5 + Source/Tools/Flax.Build/Build/Module.cs | 11 + 75 files changed, 955 insertions(+), 1431 deletions(-) delete mode 100644 Source/Editor/Content/Settings/AndroidPlatformSettings.cs delete mode 100644 Source/Editor/Content/Settings/AudioSettings.cs delete mode 100644 Source/Editor/Content/Settings/GraphicsSettings.cs delete mode 100644 Source/Editor/Content/Settings/LinuxPlatformSettings.cs delete mode 100644 Source/Editor/Content/Settings/TimeSettings.cs delete mode 100644 Source/Editor/Content/Settings/UWPPlatformSettings.cs delete mode 100644 Source/Editor/Content/Settings/WindowsPlatformSettings.cs create mode 100644 Source/Engine/Navigation/NavigationSettings.cs delete mode 100644 Source/Engine/Physics/PhysicalMaterial.cpp delete mode 100644 Source/Engine/Physics/PhysicsSettings.cpp diff --git a/Source/Editor/Content/Settings/AndroidPlatformSettings.cs b/Source/Editor/Content/Settings/AndroidPlatformSettings.cs deleted file mode 100644 index 2c540109f..000000000 --- a/Source/Editor/Content/Settings/AndroidPlatformSettings.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using FlaxEngine; - -namespace FlaxEditor.Content.Settings -{ - /// - /// The Android platform settings asset archetype. Allows to edit asset via editor. - /// - public class AndroidPlatformSettings : SettingsBase - { - /// - /// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}. - /// - [EditorOrder(0), EditorDisplay("General"), Tooltip("The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}.")] - public string PackageName = "com.${COMPANY_NAME}.${PROJECT_NAME}"; - - /// - /// The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file. - /// - [EditorOrder(100), EditorDisplay("General"), Tooltip("The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file.")] - public string[] Permissions; - - /// - /// Custom icon texture to use for the application (overrides the default one). - /// - [EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")] - public Texture OverrideIcon; - } -} diff --git a/Source/Editor/Content/Settings/AudioSettings.cs b/Source/Editor/Content/Settings/AudioSettings.cs deleted file mode 100644 index f011cad46..000000000 --- a/Source/Editor/Content/Settings/AudioSettings.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using System.ComponentModel; -using FlaxEngine; - -namespace FlaxEditor.Content.Settings -{ - /// - /// The audio payback engine settings container. Allows to edit asset via editor. - /// - public sealed class AudioSettings : SettingsBase - { - /// - /// If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine. - /// - [DefaultValue(false)] - [EditorOrder(0), EditorDisplay("General"), Tooltip("If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine.")] - public bool DisableAudio; - - /// - /// The doppler doppler effect factor. Scale for source and listener velocities. Default is 1. - /// - [DefaultValue(1.0f)] - [EditorOrder(100), EditorDisplay("General"), Limit(0, 10.0f, 0.01f), Tooltip("The doppler doppler effect factor. Scale for source and listener velocities. Default is 1.")] - public float DopplerFactor = 1.0f; - - /// - /// True if mute all audio playback when game has no use focus. - /// - [DefaultValue(true)] - [EditorOrder(200), EditorDisplay("General", "Mute On Focus Loss"), Tooltip("If checked, engine will mute all audio playback when game has no use focus.")] - public bool MuteOnFocusLoss = true; - } -} diff --git a/Source/Editor/Content/Settings/BuildSettings.cs b/Source/Editor/Content/Settings/BuildSettings.cs index e74a5fd41..87530f1fb 100644 --- a/Source/Editor/Content/Settings/BuildSettings.cs +++ b/Source/Editor/Content/Settings/BuildSettings.cs @@ -1,77 +1,12 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; -using System.ComponentModel; using FlaxEngine; namespace FlaxEditor.Content.Settings { - /// - /// The game building settings container. Allows to edit asset via editor. - /// - public sealed class BuildSettings : SettingsBase + partial class BuildSettings { - /// - /// The maximum amount of assets to include into a single assets package. Assets will be split into several packages if need to. - /// - [DefaultValue(4096)] - [EditorOrder(10), Limit(32, short.MaxValue), EditorDisplay("General", "Max assets per package"), Tooltip("The maximum amount of assets to include into a single assets package. Assets will be split into several packages if need to.")] - public int MaxAssetsPerPackage = 4096; - - /// - /// The maximum size of the single assets package (in megabytes). Assets will be split into several packages if need to. - /// - [DefaultValue(1024)] - [EditorOrder(20), Limit(16, short.MaxValue), EditorDisplay("General", "Max package size (in MB)"), Tooltip("The maximum size of the single assets package (in megabytes). Assets will be split into several packages if need to.")] - public int MaxPackageSizeMB = 1024; - - /// - /// The game content cooking Keys. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building. - /// - [DefaultValue(0)] - [EditorOrder(30), EditorDisplay("General"), Tooltip("The game content cooking Keys. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building.")] - public int ContentKey = 0; - - /// - /// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett). - /// - [DefaultValue(false)] - [EditorOrder(40), EditorDisplay("General"), Tooltip("If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett).")] - public bool ForDistribution; - - /// - /// If checked, the output build files won't be packaged for the destination platform. Useful when debugging build from local PC. - /// - [DefaultValue(false)] - [EditorOrder(50), EditorDisplay("General"), Tooltip("If checked, the output build files won't be packaged for the destination platform. Useful when debugging build from local PC.")] - public bool SkipPackaging; - - /// - /// The additional assets to include into build (into root assets set). - /// - [EditorOrder(1000), EditorDisplay("Additional Data"), Tooltip("The additional assets to include into build (into root assets set).")] - public Asset[] AdditionalAssets; - - /// - /// The additional folders with assets to include into build (into root assets set). List of paths relative to the project directory (or absolute). - /// - [EditorOrder(1010), EditorDisplay("Additional Data"), Tooltip("The additional folders with assets to include into build (to root assets set). List of paths relative to the project directory (or absolute).")] - public string[] AdditionalAssetFolders; - - /// - /// Disables shaders compiler optimizations in cooked game. Can be used to debug shaders on a target platform or to speed up the shaders compilation time. - /// - [DefaultValue(false)] - [EditorOrder(2000), EditorDisplay("Content", "Shaders No Optimize"), Tooltip("Disables shaders compiler optimizations in cooked game. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.")] - public bool ShadersNoOptimize; - - /// - /// Enables shader debug data generation for shaders in cooked game (depends on the target platform rendering backend). - /// - [DefaultValue(false)] - [EditorOrder(2010), EditorDisplay("Content"), Tooltip("Enables shader debug data generation for shaders in cooked game (depends on the target platform rendering backend).")] - public bool ShadersGenerateDebugData; - /// /// The build presets. /// diff --git a/Source/Editor/Content/Settings/GameSettings.cs b/Source/Editor/Content/Settings/GameSettings.cs index 28c13c905..eaca85c55 100644 --- a/Source/Editor/Content/Settings/GameSettings.cs +++ b/Source/Editor/Content/Settings/GameSettings.cs @@ -7,32 +7,11 @@ using FlaxEngine; namespace FlaxEditor.Content.Settings { - /// - /// The game settings asset archetype. Allows to edit asset via editor. - /// - public sealed class GameSettings : SettingsBase + partial class GameSettings { internal const string PS4PlatformSettingsTypename = "FlaxEditor.Content.Settings.PS4PlatformSettings"; internal const string XboxScarlettPlatformSettingsTypename = "FlaxEditor.Content.Settings.XboxScarlettPlatformSettings"; - /// - /// The product full name. - /// - [EditorOrder(0), EditorDisplay("General"), Tooltip("The name of your product.")] - public string ProductName; - - /// - /// The company full name. - /// - [EditorOrder(10), EditorDisplay("General"), Tooltip("The name of your company or organization.")] - public string CompanyName; - - /// - /// The copyright note used for content signing (eg. source code header). - /// - [EditorOrder(15), EditorDisplay("General"), Tooltip("The copyright note used for content signing (eg. source code header).")] - public string CopyrightNotice; - /// /// The default application icon. /// diff --git a/Source/Editor/Content/Settings/GraphicsSettings.cs b/Source/Editor/Content/Settings/GraphicsSettings.cs deleted file mode 100644 index 731bc750d..000000000 --- a/Source/Editor/Content/Settings/GraphicsSettings.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using FlaxEngine; - -namespace FlaxEditor.Content.Settings -{ - /// - /// The graphics rendering settings container. Allows to edit asset via editor. To modify those settings at runtime use . - /// - /// - public sealed class GraphicsSettings : SettingsBase - { - /// - /// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts. - /// - [EditorOrder(20), EditorDisplay("General", "Use V-Sync"), Tooltip("Enables rendering synchronization with the refresh rate of the display device to avoid \"tearing\" artifacts.")] - public bool UseVSync = false; - - /// - /// Anti Aliasing quality setting. - /// - [EditorOrder(1000), EditorDisplay("Quality", "AA Quality"), Tooltip("Anti Aliasing quality.")] - public Quality AAQuality = Quality.Medium; - - /// - /// Screen Space Reflections quality. - /// - [EditorOrder(1100), EditorDisplay("Quality", "SSR Quality"), Tooltip("Screen Space Reflections quality.")] - public Quality SSRQuality = Quality.Medium; - - /// - /// Screen Space Ambient Occlusion quality setting. - /// - [EditorOrder(1200), EditorDisplay("Quality", "SSAO Quality"), Tooltip("Screen Space Ambient Occlusion quality setting.")] - public Quality SSAOQuality = Quality.Medium; - - /// - /// Volumetric Fog quality setting. - /// - [EditorOrder(1250), EditorDisplay("Quality", "Volumetric Fog Quality"), Tooltip("Volumetric Fog quality setting.")] - public Quality VolumetricFogQuality = Quality.High; - - /// - /// The shadows quality. - /// - [EditorOrder(1300), EditorDisplay("Quality", "Shadows Quality"), Tooltip("The shadows quality.")] - public Quality ShadowsQuality = Quality.Medium; - - /// - /// The shadow maps quality (textures resolution). - /// - [EditorOrder(1310), EditorDisplay("Quality", "Shadow Maps Quality"), Tooltip("The shadow maps quality (textures resolution).")] - public Quality ShadowMapsQuality = Quality.Medium; - - /// - /// Enables cascades splits blending for directional light shadows. - /// - [EditorOrder(1320), EditorDisplay("Quality", "Allow CSM Blending"), Tooltip("Enables cascades splits blending for directional light shadows.")] - public bool AllowCSMBlending = false; - } -} diff --git a/Source/Editor/Content/Settings/InputSettings.cs b/Source/Editor/Content/Settings/InputSettings.cs index 69a8dfba3..03ed4b7c7 100644 --- a/Source/Editor/Content/Settings/InputSettings.cs +++ b/Source/Editor/Content/Settings/InputSettings.cs @@ -4,10 +4,7 @@ using FlaxEngine; namespace FlaxEditor.Content.Settings { - /// - /// The input settings container. Allows to edit asset via editor. - /// - public sealed class InputSettings : SettingsBase + partial class InputSettings { /// /// Maps a discrete button or key press events to a "friendly name" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button. diff --git a/Source/Editor/Content/Settings/LayersAndTagsSettings.cs b/Source/Editor/Content/Settings/LayersAndTagsSettings.cs index 5f3884ee1..31e8dfff8 100644 --- a/Source/Editor/Content/Settings/LayersAndTagsSettings.cs +++ b/Source/Editor/Content/Settings/LayersAndTagsSettings.cs @@ -6,10 +6,7 @@ using FlaxEngine; namespace FlaxEditor.Content.Settings { - /// - /// The layers and objects tags settings. Allows to edit asset via editor. - /// - public sealed class LayersAndTagsSettings : SettingsBase + partial class LayersAndTagsSettings { /// /// The tag names. diff --git a/Source/Editor/Content/Settings/LinuxPlatformSettings.cs b/Source/Editor/Content/Settings/LinuxPlatformSettings.cs deleted file mode 100644 index 8b26e77d4..000000000 --- a/Source/Editor/Content/Settings/LinuxPlatformSettings.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using FlaxEngine; - -namespace FlaxEditor.Content.Settings -{ - /// - /// The Linux platform settings asset archetype. Allows to edit asset via editor. - /// - public class LinuxPlatformSettings : SettingsBase - { - /// - /// The default game window mode. - /// - [EditorOrder(10), EditorDisplay("Window"), Tooltip("The default game window mode.")] - public GameWindowMode WindowMode = GameWindowMode.Windowed; - - /// - /// The default game window width (in pixels). - /// - [EditorOrder(20), EditorDisplay("Window"), Tooltip("The default game window width (in pixels).")] - public int ScreenWidth = 1280; - - /// - /// The default game window height (in pixels). - /// - [EditorOrder(30), EditorDisplay("Window"), Tooltip("The default game window height (in pixels).")] - public int ScreenHeight = 720; - - /// - /// Enables resizing the game window by the user. - /// - [EditorOrder(40), EditorDisplay("Window"), Tooltip("Enables resizing the game window by the user.")] - public bool ResizableWindow = false; - - /// - /// Enables game running when application window loses focus. - /// - [EditorOrder(1010), EditorDisplay("Other", "Run In Background"), Tooltip("Enables game running when application window loses focus.")] - public bool RunInBackground = false; - - /// - /// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once. - /// - [EditorOrder(1020), EditorDisplay("Other"), Tooltip("Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.")] - public bool ForceSingleInstance = false; - - /// - /// Custom icon texture to use for the application (overrides the default one). - /// - [EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")] - public Texture OverrideIcon; - - /// - /// Enables support for Vulkan. Disabling it reduces compiled shaders count. - /// - [EditorOrder(2020), EditorDisplay("Graphics", "Support Vulkan"), Tooltip("Enables support for Vulkan. Disabling it reduces compiled shaders count.")] - public bool SupportVulkan = true; - } -} diff --git a/Source/Editor/Content/Settings/PhysicsSettings.cs b/Source/Editor/Content/Settings/PhysicsSettings.cs index 540b11772..d217512b3 100644 --- a/Source/Editor/Content/Settings/PhysicsSettings.cs +++ b/Source/Editor/Content/Settings/PhysicsSettings.cs @@ -1,112 +1,17 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -using System.ComponentModel; using FlaxEngine; namespace FlaxEditor.Content.Settings { - /// - /// The physics simulation settings container. Allows to edit asset via editor. - /// - public sealed class PhysicsSettings : SettingsBase + partial class PhysicsSettings { - /// - /// The default gravity force value (in cm^2/s). - /// - [DefaultValue(typeof(Vector3), "0,-981.0,0")] - [EditorOrder(0), EditorDisplay("Simulation"), Tooltip("The default gravity force value (in cm^2/s).")] - public Vector3 DefaultGravity = new Vector3(0, -981.0f, 0); - - /// - /// If enabled, any Raycast or other scene query that intersects with a Collider marked as a Trigger will returns with a hit. Individual raycasts can override this behavior. - /// - [DefaultValue(true)] - [EditorOrder(10), EditorDisplay("Simulation"), Tooltip("If enabled, any Raycast or other scene query that intersects with a Collider marked as a Trigger will returns with a hit. Individual raycasts can override this behavior.")] - public bool QueriesHitTriggers = true; - - /// - /// Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable. - /// - [DefaultValue(5.0f)] - [EditorOrder(20), EditorDisplay("Simulation"), Limit(-1, 10), Tooltip("Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable.")] - public float TriangleMeshTriangleMinAreaThreshold = 5.0f; - - /// - /// Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity - /// - [DefaultValue(200.0f)] - [EditorOrder(30), EditorDisplay("Simulation"), Limit(0), Tooltip("Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity")] - public float BounceThresholdVelocity = 200.0f; - - /// - /// Default friction combine mode, controls how friction is computed for multiple materials. - /// - [DefaultValue(PhysicsCombineMode.Average)] - [EditorOrder(40), EditorDisplay("Simulation"), Tooltip("Default friction combine mode, controls how friction is computed for multiple materials.")] - public PhysicsCombineMode FrictionCombineMode = PhysicsCombineMode.Average; - - /// - /// Default restitution combine mode, controls how restitution is computed for multiple materials. - /// - [DefaultValue(PhysicsCombineMode.Average)] - [EditorOrder(50), EditorDisplay("Simulation"), Tooltip("Default restitution combine mode, controls how restitution is computed for multiple materials.")] - public PhysicsCombineMode RestitutionCombineMode = PhysicsCombineMode.Average; - - /// - /// If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for PhysX to check it internally. - /// - [DefaultValue(false)] - [EditorOrder(70), EditorDisplay("Simulation", "Disable CCD"), Tooltip("If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for PhysX to check it internally.")] - public bool DisableCCD; - - /// - /// Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts. - /// - [DefaultValue(false)] - [EditorOrder(80), EditorDisplay("Simulation"), Tooltip("Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts.")] - public bool EnableAdaptiveForce; - - /// - /// The maximum allowed delta time (in seconds) for the physics simulation step. - /// - [DefaultValue(1.0f / 10.0f)] - [EditorOrder(1000), EditorDisplay("Framerate"), Limit(0.0013f, 2.0f), Tooltip("The maximum allowed delta time (in seconds) for the physics simulation step.")] - public float MaxDeltaTime = 1.0f / 10.0f; - - /// - /// Whether to substep the physics simulation. - /// - [DefaultValue(false)] - [EditorOrder(1005), EditorDisplay("Framerate"), Tooltip("Whether to substep the physics simulation.")] - public bool EnableSubstepping; - - /// - /// Delta time (in seconds) for an individual simulation substep. - /// - [DefaultValue(1.0f / 120.0f)] - [EditorOrder(1010), EditorDisplay("Framerate"), Limit(0.0013f, 1.0f), Tooltip("Delta time (in seconds) for an individual simulation substep.")] - public float SubstepDeltaTime = 1.0f / 120.0f; - - /// - /// The maximum number of substeps for physics simulation. - /// - [DefaultValue(5)] - [EditorOrder(1020), EditorDisplay("Framerate"), Limit(1, 16), Tooltip("The maximum number of substeps for physics simulation.")] - public int MaxSubsteps = 5; - /// /// The collision layers masks. Used to define layer-based collision detection. /// [EditorOrder(1040), EditorDisplay("Layers Matrix"), CustomEditor(typeof(FlaxEditor.CustomEditors.Dedicated.LayersMatrixEditor))] public uint[] LayerMasks = new uint[32]; - /// - /// Enables support for cooking physical collision shapes geometry at runtime. Use it to enable generating runtime terrain collision or convex mesh colliders. - /// - [DefaultValue(false)] - [EditorOrder(1100), EditorDisplay("Other", "Support Cooking At Runtime"), Tooltip("Enables support for cooking physical collision shapes geometry at runtime. Use it to enable generating runtime terrain collision or convex mesh colliders.")] - public bool SupportCookingAtRuntime; - /// /// Initializes a new instance of the class. /// diff --git a/Source/Editor/Content/Settings/TimeSettings.cs b/Source/Editor/Content/Settings/TimeSettings.cs deleted file mode 100644 index af352f5e1..000000000 --- a/Source/Editor/Content/Settings/TimeSettings.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using System.ComponentModel; -using FlaxEngine; - -namespace FlaxEditor.Content.Settings -{ - /// - /// The time settings asset archetype. Allows to edit asset via editor. - /// - public sealed class TimeSettings : SettingsBase - { - /// - /// The target amount of the game logic updates per second (script updates frequency). Use 0 for infinity. - /// - [DefaultValue(30.0f)] - [EditorOrder(1), Limit(0, 1000), EditorDisplay(null, "Update FPS"), Tooltip("Target amount of the game logic updates per second (script updates frequency). Use 0 for infinity.")] - public float UpdateFPS = 30.0f; - - /// - /// The target amount of the physics simulation updates per second (also fixed updates frequency). Use 0 for infinity. - /// - [DefaultValue(60.0f)] - [EditorOrder(2), Limit(0, 1000), EditorDisplay(null, "Physics FPS"), Tooltip("Target amount of the physics simulation updates per second (also fixed updates frequency). Use 0 for infinity.")] - public float PhysicsFPS = 60.0f; - - /// - /// The target amount of the frames rendered per second (actual game FPS). Use 0 for infinity. - /// - [DefaultValue(60.0f)] - [EditorOrder(3), Limit(0, 1000), EditorDisplay(null, "Draw FPS"), Tooltip("Target amount of the frames rendered per second (actual game FPS). Use 0 for infinity.")] - public float DrawFPS = 60.0f; - - /// - /// The game time scale factor. Default is 1. - /// - [DefaultValue(1.0f)] - [EditorOrder(10), Limit(0, 1000.0f, 0.1f), Tooltip("Game time scaling factor. Default is 1 for real-time simulation.")] - public float TimeScale = 1.0f; - - /// - /// The maximum allowed delta time (in seconds) for the game logic update step. - /// - [DefaultValue(1.0f / 10.0f)] - [EditorOrder(20), Limit(0.1f, 1000.0f, 0.01f), Tooltip("The maximum allowed delta time (in seconds) for the game logic update step.")] - public float MaxUpdateDeltaTime = 1.0f / 10.0f; - } -} diff --git a/Source/Editor/Content/Settings/UWPPlatformSettings.cs b/Source/Editor/Content/Settings/UWPPlatformSettings.cs deleted file mode 100644 index 5c5696936..000000000 --- a/Source/Editor/Content/Settings/UWPPlatformSettings.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using System; -using System.ComponentModel; -using FlaxEngine; - -namespace FlaxEditor.Content.Settings -{ - /// - /// The Universal Windows Platform (UWP) platform settings asset archetype. Allows to edit asset via editor. - /// - public class UWPPlatformSettings : SettingsBase - { - /// - /// The preferred launch windowing mode. - /// - public enum WindowMode - { - /// - /// The full screen mode - /// - FullScreen = 0, - - /// - /// The view size. - /// - ViewSize = 1, - } - - /// - /// The display orientation modes. Can be combined as flags. - /// - [Flags] - public enum DisplayOrientations - { - /// - /// The none. - /// - None = 0, - - /// - /// The landscape. - /// - Landscape = 1, - - /// - /// The landscape flipped. - /// - LandscapeFlipped = 2, - - /// - /// The portrait. - /// - Portrait = 4, - - /// - /// The portrait flipped. - /// - PortraitFlipped = 8, - } - - /// - /// The preferred launch windowing mode. Always fullscreen on Xbox. - /// - [DefaultValue(WindowMode.FullScreen)] - [EditorOrder(10), EditorDisplay("Window"), Tooltip("The preferred launch windowing mode. Always fullscreen on Xbox.")] - public WindowMode PreferredLaunchWindowingMode = WindowMode.FullScreen; - - /// - /// The display orientation modes. Can be combined as flags. - /// - [DefaultValue(DisplayOrientations.Landscape | DisplayOrientations.LandscapeFlipped | DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped)] - [EditorOrder(20), EditorDisplay("Window"), Tooltip("The display orientation modes. Can be combined as flags.")] - public DisplayOrientations AutoRotationPreferences = DisplayOrientations.Landscape | DisplayOrientations.LandscapeFlipped | DisplayOrientations.Portrait | DisplayOrientations.PortraitFlipped; - - /// - /// The location of the package certificate (relative to the project). - /// - [DefaultValue("")] - [EditorOrder(1010), EditorDisplay("Other"), Tooltip("The location of the package certificate (relative to the project).")] - public string CertificateLocation = string.Empty; - - /// - /// Enables support for DirectX 11. Disabling it reduces compiled shaders count. - /// - [DefaultValue(true)] - [EditorOrder(2000), EditorDisplay("Graphics", "Support DirectX 11"), Tooltip("Enables support for DirectX 11. Disabling it reduces compiled shaders count.")] - public bool SupportDX11 = true; - - /// - /// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count. - /// - [DefaultValue(false)] - [EditorOrder(2010), EditorDisplay("Graphics", "Support DirectX 10"), Tooltip("Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.")] - public bool SupportDX10 = false; - } -} diff --git a/Source/Editor/Content/Settings/WindowsPlatformSettings.cs b/Source/Editor/Content/Settings/WindowsPlatformSettings.cs deleted file mode 100644 index dc873e9f4..000000000 --- a/Source/Editor/Content/Settings/WindowsPlatformSettings.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using System.ComponentModel; -using FlaxEngine; - -namespace FlaxEditor.Content.Settings -{ - /// - /// The Windows platform settings asset archetype. Allows to edit asset via editor. - /// - public class WindowsPlatformSettings : SettingsBase - { - /// - /// The default game window mode. - /// - [DefaultValue(GameWindowMode.Windowed)] - [EditorOrder(10), EditorDisplay("Window"), Tooltip("The default game window mode.")] - public GameWindowMode WindowMode = GameWindowMode.Windowed; - - /// - /// The default game window width (in pixels). - /// - [DefaultValue(1280)] - [EditorOrder(20), EditorDisplay("Window"), Tooltip("The default game window width (in pixels).")] - public int ScreenWidth = 1280; - - /// - /// The default game window height (in pixels). - /// - [DefaultValue(720)] - [EditorOrder(30), EditorDisplay("Window"), Tooltip("The default game window height (in pixels).")] - public int ScreenHeight = 720; - - /// - /// Enables resizing the game window by the user. - /// - [DefaultValue(false)] - [EditorOrder(40), EditorDisplay("Window"), Tooltip("Enables resizing the game window by the user.")] - public bool ResizableWindow = false; - - /// - /// Enables game running when application window loses focus. - /// - [DefaultValue(false)] - [EditorOrder(1010), EditorDisplay("Other", "Run In Background"), Tooltip("Enables game running when application window loses focus.")] - public bool RunInBackground = false; - - /// - /// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once. - /// - [DefaultValue(false)] - [EditorOrder(1020), EditorDisplay("Other"), Tooltip("Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once.")] - public bool ForceSingleInstance = false; - - /// - /// Custom icon texture to use for the application (overrides the default one). - /// - [DefaultValue(null)] - [EditorOrder(1030), EditorDisplay("Other"), Tooltip("Custom icon texture to use for the application (overrides the default one).")] - public Texture OverrideIcon; - - /// - /// Enables support for DirectX 12. Disabling it reduces compiled shaders count. - /// - [DefaultValue(false)] - [EditorOrder(2000), EditorDisplay("Graphics", "Support DirectX 12"), Tooltip("Enables support for DirectX 12. Disabling it reduces compiled shaders count.")] - public bool SupportDX12 = false; - - /// - /// Enables support for DirectX 11. Disabling it reduces compiled shaders count. - /// - [DefaultValue(true)] - [EditorOrder(2010), EditorDisplay("Graphics", "Support DirectX 11"), Tooltip("Enables support for DirectX 11. Disabling it reduces compiled shaders count.")] - public bool SupportDX11 = true; - - /// - /// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count. - /// - [DefaultValue(false)] - [EditorOrder(2020), EditorDisplay("Graphics", "Support DirectX 10"), Tooltip("Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count.")] - public bool SupportDX10 = false; - - /// - /// Enables support for Vulkan. Disabling it reduces compiled shaders count. - /// - [DefaultValue(false)] - [EditorOrder(2030), EditorDisplay("Graphics", "Support Vulkan"), Tooltip("Enables support for Vulkan. Disabling it reduces compiled shaders count.")] - public bool SupportVulkan = false; - } -} diff --git a/Source/Editor/Cooker/GameCooker.cpp b/Source/Editor/Cooker/GameCooker.cpp index 1fdf0755f..d68ca587a 100644 --- a/Source/Editor/Cooker/GameCooker.cpp +++ b/Source/Editor/Cooker/GameCooker.cpp @@ -43,53 +43,11 @@ #endif #if PLATFORM_TOOLS_XBOX_SCARLETT #include "Platforms/XboxScarlett/Editor/PlatformTools/XboxScarlettPlatformTools.h" -#include "Platforms/XboxScarlett/Engine/Platform/XboxScarlettPlatformSettings.h" #endif #if PLATFORM_TOOLS_ANDROID #include "Platform/Android/AndroidPlatformTools.h" -#include "Engine/Platform/Android/AndroidPlatformSettings.h" #endif -void LoadPlatformSettingsEditor(ISerializable::DeserializeStream& data) -{ -#define LOAD_SETTINGS(nodeName, settingsType) \ - { \ - Guid id = JsonTools::GetGuid(data, nodeName); \ - if (id.IsValid()) \ - { \ - AssetReference subAsset = Content::LoadAsync(id); \ - if (subAsset) \ - { \ - if (!subAsset->WaitForLoaded()) \ - { \ - settingsType::Instance()->Deserialize(*subAsset->Data, nullptr); \ - settingsType::Instance()->Apply(); \ - } \ - } \ - else \ - { LOG(Warning, "Cannot load " nodeName " settings"); } \ - } \ - } -#if PLATFORM_TOOLS_WINDOWS - LOAD_SETTINGS("WindowsPlatform", WindowsPlatformSettings); -#endif -#if PLATFORM_TOOLS_UWP || PLATFORM_TOOLS_XBOX_ONE - LOAD_SETTINGS("UWPPlatform", UWPPlatformSettings); -#endif -#if PLATFORM_TOOLS_LINUX - LOAD_SETTINGS("LinuxPlatform", LinuxPlatformSettings); -#endif -#if PLATFORM_TOOLS_PS4 - LOAD_SETTINGS("PS4Platform", PS4PlatformSettings); -#endif -#if PLATFORM_TOOLS_XBOX_SCARLETT - LOAD_SETTINGS("XboxScarlettPlatform", XboxScarlettPlatformSettings); -#endif -#if PLATFORM_TOOLS_ANDROID - LOAD_SETTINGS("AndroidPlatform", AndroidPlatformSettings); -#endif -} - namespace GameCookerImpl { MMethod* Internal_OnEvent = nullptr; diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index bce6aa860..b687e5a2b 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -12,6 +12,10 @@ #include "Engine/Core/Config/GameSettings.h" #include "Engine/Core/Config/BuildSettings.h" #include "Editor/Utilities/EditorUtilities.h" +#include "Engine/Content/Content.h" +#include "Engine/Content/JsonAsset.h" + +IMPLEMENT_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform); namespace { @@ -106,7 +110,8 @@ void AndroidPlatformTools::OnBuildStarted(CookingData& data) bool AndroidPlatformTools::OnPostProcess(CookingData& data) { - const auto platformSettings = AndroidPlatformSettings::Instance(); + const auto gameSettings = GameSettings::Get(); + const auto platformSettings = AndroidPlatformSettings::Get(); const auto platformDataPath = data.GetPlatformBinariesRoot(); const auto assetsPath = data.OutputPath; const auto jniLibsPath = data.OriginalOutputPath / TEXT("app/jniLibs"); @@ -125,11 +130,11 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) // Setup package name (eg. com.company.project) String packageName = platformSettings->PackageName; { - String productName = GameSettings::ProductName; + String productName = gameSettings->ProductName; productName.Replace(TEXT(" "), TEXT("")); productName.Replace(TEXT("."), TEXT("")); productName.Replace(TEXT("-"), TEXT("")); - String companyName = GameSettings::CompanyName; + String companyName = gameSettings->CompanyName; companyName.Replace(TEXT(" "), TEXT("")); companyName.Replace(TEXT("."), TEXT("")); companyName.Replace(TEXT("-"), TEXT("")); @@ -235,7 +240,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) EditorUtilities::ReplaceInFile(manifestPath, TEXT("${AndroidPermissions}"), permissions); EditorUtilities::ReplaceInFile(manifestPath, TEXT("${AndroidAttributes}"), attributes); const String stringsPath = data.OriginalOutputPath / TEXT("app/src/main/res/values/strings.xml"); - EditorUtilities::ReplaceInFile(stringsPath, TEXT("${ProjectName}"), GameSettings::ProductName); + EditorUtilities::ReplaceInFile(stringsPath, TEXT("${ProjectName}"), gameSettings->ProductName); // Deploy native binaries to the output location (per-ABI) const String abiBinariesPath = jniLibsPath / abi; @@ -256,7 +261,8 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) // TODO: expose event to inject custom gradle and manifest options or custom binaries into app - if (BuildSettings::Instance()->SkipPackaging) + const auto buildSettings = BuildSettings::Get(); + if (buildSettings->SkipPackaging) { return false; } @@ -286,7 +292,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) #else const Char* gradlew = TEXT("gradlew"); #endif - const bool distributionPackage = BuildSettings::Instance()->ForDistribution; + const bool distributionPackage = buildSettings->ForDistribution; const String gradleCommand = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug")); const int32 result = Platform::RunProcess(gradleCommand, data.OriginalOutputPath, envVars, true); if (result != 0) @@ -297,7 +303,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) // Copy result package const String apk = data.OriginalOutputPath / (distributionPackage ? TEXT("app/build/outputs/apk/release/app-release-unsigned.apk") : TEXT("app/build/outputs/apk/debug/app-debug.apk")); - const String outputApk = data.OriginalOutputPath / GameSettings::ProductName + TEXT(".apk"); + const String outputApk = data.OriginalOutputPath / gameSettings->ProductName + TEXT(".apk"); if (FileSystem::CopyFile(outputApk, apk)) { LOG(Error, "Failed to copy package from {0} to {1}", apk, outputApk); diff --git a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp index eccc299bb..0f650da2c 100644 --- a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp @@ -9,6 +9,10 @@ #include "Editor/Utilities/EditorUtilities.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Graphics/Textures/TextureData.h" +#include "Engine/Content/Content.h" +#include "Engine/Content/JsonAsset.h" + +IMPLEMENT_SETTINGS_GETTER(LinuxPlatformSettings, LinuxPlatform); const Char* LinuxPlatformTools::GetDisplayName() const { @@ -32,7 +36,7 @@ ArchitectureType LinuxPlatformTools::GetArchitecture() const bool LinuxPlatformTools::OnDeployBinaries(CookingData& data) { - const auto platformSettings = LinuxPlatformSettings::Instance(); + const auto platformSettings = LinuxPlatformSettings::Get(); const auto outputPath = data.OutputPath; // Copy binaries diff --git a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp index f6c846fed..7f7851941 100644 --- a/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/UWP/UWPPlatformTools.cpp @@ -11,6 +11,10 @@ #include "Engine/Serialization/FileWriteStream.h" #include "Editor/Utilities/EditorUtilities.h" #include "Engine/Engine/Globals.h" +#include "Engine/Content/Content.h" +#include "Engine/Content/JsonAsset.h" + +IMPLEMENT_SETTINGS_GETTER(UWPPlatformSettings, UWPPlatform); bool UWPPlatformTools::UseAOT() const { @@ -37,7 +41,8 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) bool isXboxOne = data.Platform == BuildPlatform::XboxOne; const auto platformDataPath = Globals::StartupFolder / TEXT("Source/Platforms"); const auto uwpDataPath = platformDataPath / (isXboxOne ? TEXT("XboxOne") : TEXT("UWP")) / TEXT("Binaries"); - const auto platformSettings = UWPPlatformSettings::Instance(); + const auto gameSettings = GameSettings::Get(); + const auto platformSettings = UWPPlatformSettings::Get(); Array fileTemplate; // Copy binaries @@ -66,7 +71,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) } } - const auto projectName = GameSettings::ProductName; + const auto projectName = gameSettings->ProductName; auto defaultNamespace = projectName; ScriptsBuilder::FilterNamespaceText(defaultNamespace); const StringAnsi projectGuid = "{3A9A2246-71DD-4567-9ABF-3E040310E30E}"; @@ -102,7 +107,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) // Generate new temp cert if missing if (!FileSystem::FileExists(dstCertificatePath)) { - if (EditorUtilities::GenerateCertificate(GameSettings::CompanyName, dstCertificatePath)) + if (EditorUtilities::GenerateCertificate(gameSettings->CompanyName, dstCertificatePath)) { LOG(Warning, "Failed to create certificate."); } @@ -159,8 +164,8 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) auto now = DateTime::Now(); file->WriteTextFormatted( (char*)fileTemplate.Get() - , GameSettings::ProductName.ToStringAnsi() - , GameSettings::CompanyName.ToStringAnsi() + , gameSettings->ProductName.ToStringAnsi() + , gameSettings->CompanyName.ToStringAnsi() , now.GetYear() ); hasError = file->HasError(); @@ -382,7 +387,7 @@ bool UWPPlatformTools::OnDeployBinaries(CookingData& data) file->WriteTextFormatted( (char*)fileTemplate.Get() , projectName.ToStringAnsi() // {0} Display Name - , GameSettings::CompanyName.ToStringAnsi() // {1} Company Name + , gameSettings->CompanyName.ToStringAnsi() // {1} Company Name , productId.ToStringAnsi() // {2} Product ID , defaultNamespace.ToStringAnsi() // {3} Default Namespace ); diff --git a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp index 35ed27639..348930dd5 100644 --- a/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Windows/WindowsPlatformTools.cpp @@ -8,6 +8,10 @@ #include "Engine/Core/Config/GameSettings.h" #include "Editor/Utilities/EditorUtilities.h" #include "Engine/Graphics/Textures/TextureData.h" +#include "Engine/Content/Content.h" +#include "Engine/Content/JsonAsset.h" + +IMPLEMENT_SETTINGS_GETTER(WindowsPlatformSettings, WindowsPlatform); const Char* WindowsPlatformTools::GetDisplayName() const { @@ -31,7 +35,7 @@ ArchitectureType WindowsPlatformTools::GetArchitecture() const bool WindowsPlatformTools::OnDeployBinaries(CookingData& data) { - const auto platformSettings = WindowsPlatformSettings::Instance(); + const auto platformSettings = WindowsPlatformSettings::Get(); const auto& outputPath = data.OutputPath; // Apply executable icon diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 396626fc1..af6f003b8 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -160,8 +160,9 @@ void CookAssetsStep::CacheData::Load(CookingData& data) // Invalidate shaders and assets with shaders if need to rebuild them bool invalidateShaders = false; - const bool shadersNoOptimize = BuildSettings::Instance()->ShadersNoOptimize; - const bool shadersGenerateDebugData = BuildSettings::Instance()->ShadersGenerateDebugData; + const auto buildSettings = BuildSettings::Get(); + const bool shadersNoOptimize = buildSettings->ShadersNoOptimize; + const bool shadersGenerateDebugData = buildSettings->ShadersGenerateDebugData; if (shadersNoOptimize != Settings.Global.ShadersNoOptimize) { LOG(Info, "ShadersNoOptimize option has been modified."); @@ -175,7 +176,7 @@ void CookAssetsStep::CacheData::Load(CookingData& data) #if PLATFORM_TOOLS_WINDOWS if (data.Platform == BuildPlatform::Windows32 || data.Platform == BuildPlatform::Windows64) { - const auto settings = WindowsPlatformSettings::Instance(); + const auto settings = WindowsPlatformSettings::Get(); const bool modified = Settings.Windows.SupportDX11 != settings->SupportDX11 || Settings.Windows.SupportDX10 != settings->SupportDX10 || @@ -190,7 +191,7 @@ void CookAssetsStep::CacheData::Load(CookingData& data) #if PLATFORM_TOOLS_UWP if (data.Platform == BuildPlatform::UWPx86 || data.Platform == BuildPlatform::UWPx64) { - const auto settings = UWPPlatformSettings::Instance(); + const auto settings = UWPPlatformSettings::Get(); const bool modified = Settings.UWP.SupportDX11 != settings->SupportDX11 || Settings.UWP.SupportDX10 != settings->SupportDX10; @@ -204,7 +205,7 @@ void CookAssetsStep::CacheData::Load(CookingData& data) #if PLATFORM_TOOLS_LINUX if (data.Platform == BuildPlatform::LinuxX64) { - const auto settings = LinuxPlatformSettings::Instance(); + const auto settings = LinuxPlatformSettings::Get(); const bool modified = Settings.Linux.SupportVulkan != settings->SupportVulkan; if (modified) @@ -369,7 +370,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass case BuildPlatform::Windows64: { const char* platformDefineName = "PLATFORM_WINDOWS"; - const auto settings = WindowsPlatformSettings::Instance(); + const auto settings = WindowsPlatformSettings::Get(); if (settings->SupportDX12) { COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE); @@ -393,7 +394,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass case BuildPlatform::UWPx64: { const char* platformDefineName = "PLATFORM_UWP"; - const auto settings = UWPPlatformSettings::Instance(); + const auto settings = UWPPlatformSettings::Get(); if (settings->SupportDX11) { COMPILE_PROFILE(DirectX_SM5, SHADER_FILE_CHUNK_INTERNAL_D3D_SM5_CACHE); @@ -415,7 +416,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass case BuildPlatform::LinuxX64: { const char* platformDefineName = "PLATFORM_LINUX"; - const auto settings = LinuxPlatformSettings::Instance(); + const auto settings = LinuxPlatformSettings::Get(); if (settings->SupportVulkan) { COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE); @@ -881,7 +882,8 @@ bool CookAssetsStep::Perform(CookingData& data) data.StepProgress(TEXT("Loading build cache"), 0); // Prepare - const auto buildSettings = BuildSettings::Instance(); + const auto gameSettings = GameSettings::Get(); + const auto buildSettings = BuildSettings::Get(); const int32 contentKey = buildSettings->ContentKey == 0 ? rand() : buildSettings->ContentKey; AssetsRegistry.Clear(); AssetPathsMapping.Clear(); @@ -892,22 +894,21 @@ bool CookAssetsStep::Perform(CookingData& data) // Update build settings { - const auto settings = WindowsPlatformSettings::Instance(); + const auto settings = WindowsPlatformSettings::Get(); cache.Settings.Windows.SupportDX11 = settings->SupportDX11; cache.Settings.Windows.SupportDX10 = settings->SupportDX10; cache.Settings.Windows.SupportVulkan = settings->SupportVulkan; } { - const auto settings = UWPPlatformSettings::Instance(); + const auto settings = UWPPlatformSettings::Get(); cache.Settings.UWP.SupportDX11 = settings->SupportDX11; cache.Settings.UWP.SupportDX10 = settings->SupportDX10; } { - const auto settings = LinuxPlatformSettings::Instance(); + const auto settings = LinuxPlatformSettings::Get(); cache.Settings.Linux.SupportVulkan = settings->SupportVulkan; } { - const auto buildSettings = BuildSettings::Instance(); cache.Settings.Global.ShadersNoOptimize = buildSettings->ShadersNoOptimize; cache.Settings.Global.ShadersGenerateDebugData = buildSettings->ShadersGenerateDebugData; } @@ -1004,7 +1005,7 @@ bool CookAssetsStep::Perform(CookingData& data) // Create build game header { GameHeaderFlags gameFlags = GameHeaderFlags::None; - if (!GameSettings::NoSplashScreen) + if (!gameSettings->NoSplashScreen) gameFlags |= GameHeaderFlags::ShowSplashScreen; // Open file @@ -1022,17 +1023,17 @@ bool CookAssetsStep::Perform(CookingData& data) Array bytes; bytes.Resize(808 + sizeof(Guid)); Platform::MemoryClear(bytes.Get(), bytes.Count()); - int32 length = sizeof(Char) * GameSettings::ProductName.Length(); - Platform::MemoryCopy(bytes.Get() + 0, GameSettings::ProductName.Get(), length); + int32 length = sizeof(Char) * gameSettings->ProductName.Length(); + Platform::MemoryCopy(bytes.Get() + 0, gameSettings->ProductName.Get(), length); bytes[length] = 0; bytes[length + 1] = 0; - length = sizeof(Char) * GameSettings::CompanyName.Length(); - Platform::MemoryCopy(bytes.Get() + 400, GameSettings::CompanyName.Get(), length); + length = sizeof(Char) * gameSettings->CompanyName.Length(); + Platform::MemoryCopy(bytes.Get() + 400, gameSettings->CompanyName.Get(), length); bytes[length + 400] = 0; bytes[length + 401] = 0; *(int32*)(bytes.Get() + 800) = (int32)gameFlags; *(int32*)(bytes.Get() + 804) = contentKey; - *(Guid*)(bytes.Get() + 808) = GameSettings::SplashScreen; + *(Guid*)(bytes.Get() + 808) = gameSettings->SplashScreen; Encryption::EncryptBytes(bytes.Get(), bytes.Count()); stream->WriteArray(bytes); diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp index b5ea2d268..9ce3a8a5d 100644 --- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp +++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp @@ -12,6 +12,7 @@ bool DeployDataStep::Perform(CookingData& data) { data.StepProgress(TEXT("Deploying engine data"), 0); const String depsRoot = data.GetPlatformBinariesRoot(); + const auto gameSettings = GameSettings::Get(); // Setup output folders and copy required data const auto contentDir = data.OutputPath / TEXT("Content"); @@ -74,7 +75,7 @@ bool DeployDataStep::Perform(CookingData& data) data.AddRootEngineAsset(TEXT("Shaders/VolumetricFog")); data.AddRootEngineAsset(TEXT("Engine/DefaultMaterial")); data.AddRootEngineAsset(TEXT("Engine/DefaultTerrainMaterial")); - if (!GameSettings::NoSplashScreen && !GameSettings::SplashScreen.IsValid()) + if (!gameSettings->NoSplashScreen && !gameSettings->SplashScreen.IsValid()) data.AddRootEngineAsset(TEXT("Engine/Textures/Logo")); data.AddRootEngineAsset(TEXT("Engine/Textures/NormalTexture")); data.AddRootEngineAsset(TEXT("Engine/Textures/BlackTexture")); @@ -98,7 +99,7 @@ bool DeployDataStep::Perform(CookingData& data) // Register game assets data.StepProgress(TEXT("Deploying game data"), 50); - auto& buildSettings = *BuildSettings::Instance(); + auto& buildSettings = *BuildSettings::Get(); for (auto& e : buildSettings.AdditionalAssets) data.AddRootAsset(e.GetID()); Array files; diff --git a/Source/Editor/Cooker/Steps/ValidateStep.cpp b/Source/Editor/Cooker/Steps/ValidateStep.cpp index 219301544..625957fae 100644 --- a/Source/Editor/Cooker/Steps/ValidateStep.cpp +++ b/Source/Editor/Cooker/Steps/ValidateStep.cpp @@ -38,18 +38,23 @@ bool ValidateStep::Perform(CookingData& data) #endif // Load game settings (may be modified via editor) - GameSettings::Load(); + if (GameSettings::Load()) + { + data.Error(TEXT("Failed to load game settings.")); + return true; + } data.AddRootAsset(Globals::ProjectContentFolder / TEXT("GameSettings.json")); // Validate game settings + auto gameSettings = GameSettings::Get(); { - if (GameSettings::ProductName.IsEmpty()) + if (gameSettings->ProductName.IsEmpty()) { data.Error(TEXT("Missing product name.")); return true; } - if (GameSettings::CompanyName.IsEmpty()) + if (gameSettings->CompanyName.IsEmpty()) { data.Error(TEXT("Missing company name.")); return true; @@ -58,7 +63,7 @@ bool ValidateStep::Perform(CookingData& data) // TODO: validate version AssetInfo info; - if (!Content::GetAssetInfo(GameSettings::FirstScene, info)) + if (!Content::GetAssetInfo(gameSettings->FirstScene, info)) { data.Error(TEXT("Missing first scene.")); return true; diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs index a767ecac9..4d7d33d65 100644 --- a/Source/Editor/Editor.Build.cs +++ b/Source/Editor/Editor.Build.cs @@ -26,7 +26,7 @@ public class Editor : EditorModule // Platform Tools inside external platform implementation location options.PrivateDefinitions.Add(macro); options.SourcePaths.Add(externalPath); - options.SourceFiles.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", platform, "Engine", "Platform", platform + "PlatformSettings.cs")); + AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", platform, "Engine", "Platform", platform + "PlatformSettings.cs")); } } } diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index 4084c7f01..28d4df32a 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -24,7 +24,7 @@ #include "Engine/ContentImporters/ImportAudio.h" #include "Engine/ContentImporters/CreateCollisionData.h" #include "Engine/ContentImporters/CreateJson.h" -#include "Engine/Core/Config/LayersTagsSettings.h" +#include "Engine/Level/Level.h" #include "Engine/Core/Config/GameSettings.h" #include "Engine/Core/Cache.h" #include "Engine/CSG/CSGBuilder.h" @@ -302,22 +302,20 @@ namespace CustomEditorsUtilInternal } } -namespace LayersAndTagsSettingsInternal +namespace LayersAndTagsSettingsInternal1 { MonoArray* GetCurrentTags() { - auto settings = LayersAndTagsSettings::Instance(); - return MUtils::ToArray(settings->Tags); + return MUtils::ToArray(Level::Tags); } MonoArray* GetCurrentLayers() { - const auto settings = LayersAndTagsSettings::Instance(); - return MUtils::ToArray(Span(settings->Layers, Math::Max(1, settings->GetNonEmptyLayerNamesCount()))); + return MUtils::ToArray(Span(Level::Layers, Math::Max(1, Level::GetNonEmptyLayerNamesCount()))); } } -namespace GameSettingsInternal +namespace GameSettingsInternal1 { void Apply() { @@ -1054,9 +1052,9 @@ public: ADD_INTERNAL_CALL("FlaxEditor.Content.Import.TextureImportEntry::Internal_GetTextureImportOptions", &GetTextureImportOptions); ADD_INTERNAL_CALL("FlaxEditor.Content.Import.ModelImportEntry::Internal_GetModelImportOptions", &GetModelImportOptions); ADD_INTERNAL_CALL("FlaxEditor.Content.Import.AudioImportEntry::Internal_GetAudioImportOptions", &GetAudioImportOptions); - ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentTags", &LayersAndTagsSettingsInternal::GetCurrentTags); - ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers", &LayersAndTagsSettingsInternal::GetCurrentLayers); - ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.GameSettings::Apply", &GameSettingsInternal::Apply); + ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentTags", &LayersAndTagsSettingsInternal1::GetCurrentTags); + ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers", &LayersAndTagsSettingsInternal1::GetCurrentLayers); + ADD_INTERNAL_CALL("FlaxEditor.Content.Settings.GameSettings::Apply", &GameSettingsInternal1::Apply); ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CloseSplashScreen", &CloseSplashScreen); ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CreateAsset", &CreateAsset); ADD_INTERNAL_CALL("FlaxEditor.Editor::Internal_CreateVisualScript", &CreateVisualScript); diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index 1571c39d1..541c8892a 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -507,7 +507,11 @@ bool EditorUtilities::GetApplicationImage(const Guid& imageId, TextureData& imag AssetReference icon = Content::LoadAsync(imageId); if (icon == nullptr) { - icon = Content::LoadAsync(GameSettings::Icon); + const auto gameSettings = GameSettings::Get(); + if (gameSettings) + { + icon = Content::LoadAsync(gameSettings->Icon); + } } if (icon == nullptr) { diff --git a/Source/Engine/Audio/Audio.cpp b/Source/Engine/Audio/Audio.cpp index 1dd0393b4..51f674e73 100644 --- a/Source/Engine/Audio/Audio.cpp +++ b/Source/Engine/Audio/Audio.cpp @@ -44,9 +44,14 @@ Array Audio::Devices; Action Audio::DevicesChanged; Action Audio::ActiveDeviceChanged; AudioBackend* AudioBackend::Instance = nullptr; -float MasterVolume = 1.0f; -float Volume = 1.0f; -int32 ActiveDeviceIndex = -1; + +namespace +{ + float MasterVolume = 1.0f; + float Volume = 1.0f; + int32 ActiveDeviceIndex = -1; + bool MuteOnFocusLoss = true; +} class AudioService : public EngineService { @@ -77,6 +82,11 @@ namespace } } +void AudioSettings::Apply() +{ + ::MuteOnFocusLoss = MuteOnFocusLoss; +} + AudioDevice* Audio::GetActiveDevice() { return &Devices[ActiveDeviceIndex]; @@ -161,7 +171,7 @@ void Audio::OnRemoveSource(AudioSource* source) bool AudioService::Init() { - const auto settings = AudioSettings::Instance(); + const auto settings = AudioSettings::Get(); const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio; // Pick a backend to use @@ -225,11 +235,9 @@ void AudioService::Update() { PROFILE_CPU(); - const auto settings = AudioSettings::Instance(); - // Update the master volume float masterVolume = MasterVolume; - if (settings->MuteOnFocusLoss && !Engine::HasFocus) + if (MuteOnFocusLoss && !Engine::HasFocus) { // Mute audio if app has no user focus masterVolume = 0.0f; diff --git a/Source/Engine/Audio/AudioSettings.h b/Source/Engine/Audio/AudioSettings.h index e976c9953..b5efe9524 100644 --- a/Source/Engine/Audio/AudioSettings.h +++ b/Source/Engine/Audio/AudioSettings.h @@ -8,35 +8,38 @@ /// /// Audio settings container. /// -/// -class AudioSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AudioSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(AudioSettings); public: /// /// If checked, audio playback will be disabled in build game. Can be used if game uses custom audio playback engine. /// + API_FIELD(Attributes="EditorOrder(0), DefaultValue(false), EditorDisplay(\"General\")") bool DisableAudio = false; /// /// The doppler effect factor. Scale for source and listener velocities. Default is 1. /// + API_FIELD(Attributes="EditorOrder(100), DefaultValue(1.0f), EditorDisplay(\"General\")") float DopplerFactor = 1.0f; /// /// True if mute all audio playback when game has no use focus. /// + API_FIELD(Attributes="EditorOrder(200), DefaultValue(true), EditorDisplay(\"General\", \"Mute On Focus Loss\")") bool MuteOnFocusLoss = true; public: + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static AudioSettings* Get(); + // [SettingsBase] - void RestoreDefault() final override - { - DisableAudio = false; - DopplerFactor = 1.0f; - MuteOnFocusLoss = true; - } + void Apply() override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { diff --git a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp index e06ab35aa..89c8f3d02 100644 --- a/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp +++ b/Source/Engine/Audio/OpenAL/AudioBackendOAL.cpp @@ -878,7 +878,7 @@ bool AudioBackendOAL::Base_Init() // Init ALC::AL_EXT_float32 = ALC::IsExtensionSupported("AL_EXT_float32"); - SetDopplerFactor(AudioSettings::Instance()->DopplerFactor); + SetDopplerFactor(AudioSettings::Get()->DopplerFactor); ALC::RebuildContexts(true); Audio::SetActiveDeviceIndex(activeDeviceIndex); diff --git a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp index a201253ac..aac3979ce 100644 --- a/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp +++ b/Source/Engine/Audio/XAudio2/AudioBackendXAudio2.cpp @@ -794,7 +794,7 @@ void AudioBackendXAudio2::Base_Update() } // Update dirty voices - const float dopplerFactor = AudioSettings::Instance()->DopplerFactor; + const float dopplerFactor = AudioSettings::Get()->DopplerFactor; X3DAUDIO_DSP_SETTINGS dsp = { 0 }; dsp.DstChannelCount = XAudio2::Channels; dsp.pMatrixCoefficients = XAudio2::MatrixCoefficients; diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index 6618336d4..821c43f87 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -214,6 +214,7 @@ Asset::LoadResult JsonAsset::loadAsset() if (!instance) return LoadResult::Failed; Instance = instance; + InstanceType = typeHandle; _dtor = type.Class.Dtor; type.Class.Ctor(instance); @@ -238,6 +239,7 @@ void JsonAsset::unload(bool isReloading) if (Instance) { + InstanceType = ScriptingTypeHandle(); _dtor(Instance); Allocator::Free(Instance); Instance = nullptr; diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index c790bb6b9..b9edd67ea 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -74,7 +74,6 @@ protected: /// /// Generic type of Json-format asset. It provides the managed representation of this resource data so it can be accessed via C# API. /// -/// API_CLASS(NoSpawn) class JsonAsset : public JsonAssetBase { DECLARE_ASSET_HEADER(JsonAsset); @@ -83,6 +82,11 @@ private: public: + /// + /// The scripting type of the deserialized unmanaged object instance (e.g. PhysicalMaterial). + /// + ScriptingTypeHandle InstanceType; + /// /// The deserialized unmanaged object instance (e.g. PhysicalMaterial). /// diff --git a/Source/Engine/Core/Config/BuildSettings.h b/Source/Engine/Core/Config/BuildSettings.h index ac0a36f02..529171639 100644 --- a/Source/Engine/Core/Config/BuildSettings.h +++ b/Source/Engine/Core/Config/BuildSettings.h @@ -10,71 +10,73 @@ /// /// The game building rendering settings. /// -class BuildSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API BuildSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(BuildSettings); public: /// /// The maximum amount of assets to include into a single assets package. Asset packages will split into several packages if need to. /// - int32 MaxAssetsPerPackage = 1024; + API_FIELD(Attributes="EditorOrder(10), DefaultValue(4096), Limit(1, 32, ushort.MaxValue), EditorDisplay(\"General\", \"Max assets per package\")") + int32 MaxAssetsPerPackage = 4096; /// /// The maximum size of the single assets package (in megabytes). Asset packages will split into several packages if need to. /// + API_FIELD(Attributes="EditorOrder(20), DefaultValue(1024), Limit(1, 16, ushort.MaxValue), EditorDisplay(\"General\", \"Max package size (in MB)\")") int32 MaxPackageSizeMB = 1024; /// /// The game content cooking keycode. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building. /// + API_FIELD(Attributes="EditorOrder(30), DefaultValue(0), Limit(1, 16, ushort.MaxValue), EditorDisplay(\"General\")") int32 ContentKey = 0; /// /// If checked, the builds produced by the Game Cooker will be treated as for final game distribution (eg. for game store upload). Builds done this way cannot be tested on console devkits (eg. Xbox One, Xbox Scarlett). /// + API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"General\")") bool ForDistribution = false; /// /// If checked, the output build files won't be packaged for the destination platform. Useful when debugging build from local PC. /// + API_FIELD(Attributes="EditorOrder(50), DefaultValue(false), EditorDisplay(\"General\")") bool SkipPackaging = false; /// /// The list of additional assets to include into build (into root assets set). /// + API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Additional Data\")") Array> AdditionalAssets; /// /// The list of additional folders with assets to include into build (into root assets set). Paths relative to the project directory (or absolute). /// + API_FIELD(Attributes="EditorOrder(1010), EditorDisplay(\"Additional Data\")") Array AdditionalAssetFolders; /// /// Disables shaders compiler optimizations in cooked game. Can be used to debug shaders on a target platform or to speed up the shaders compilation time. /// + API_FIELD(Attributes="EditorOrder(2000), DefaultValue(false), EditorDisplay(\"Content\", \"Shaders No Optimize\")") bool ShadersNoOptimize = false; /// /// Enables shader debug data generation for shaders in cooked game (depends on the target platform rendering backend). /// + API_FIELD(Attributes="EditorOrder(2010), DefaultValue(false), EditorDisplay(\"Content\")") bool ShadersGenerateDebugData = false; public: - // [SettingsBase] - void RestoreDefault() final override - { - MaxAssetsPerPackage = 1024; - MaxPackageSizeMB = 1024; - ContentKey = 0; - ForDistribution = false; - SkipPackaging = false; - AdditionalAssets.Clear(); - AdditionalAssetFolders.Clear(); - ShadersNoOptimize = false; - ShadersGenerateDebugData = false; - } + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static BuildSettings* Get(); + // [SettingsBase] void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { DESERIALIZE(MaxAssetsPerPackage); diff --git a/Source/Engine/Core/Config/GameSettings.cpp b/Source/Engine/Core/Config/GameSettings.cpp index 9899ced75..5ea356d61 100644 --- a/Source/Engine/Core/Config/GameSettings.cpp +++ b/Source/Engine/Core/Config/GameSettings.cpp @@ -2,6 +2,7 @@ #include "GameSettings.h" #include "Engine/Serialization/JsonTools.h" +#include "Engine/Scripting/ScriptingType.h" #include "Engine/Physics/PhysicsSettings.h" #include "Engine/Core/Log.h" #include "LayersTagsSettings.h" @@ -17,30 +18,6 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Engine/EngineService.h" #include "Engine/Engine/Globals.h" -#include "Engine/Engine/Time.h" - -String GameSettings::ProductName; -String GameSettings::CompanyName; -String GameSettings::CopyrightNotice; -Guid GameSettings::Icon; -Guid GameSettings::FirstScene; -bool GameSettings::NoSplashScreen = false; -Guid GameSettings::SplashScreen; -Dictionary GameSettings::CustomSettings; - -Array Settings::Containers(32); - -#if USE_EDITOR -extern void LoadPlatformSettingsEditor(ISerializable::DeserializeStream& data); -#endif - -void TimeSettings::Apply() -{ - Time::UpdateFPS = UpdateFPS; - Time::PhysicsFPS = PhysicsFPS; - Time::DrawFPS = DrawFPS; - Time::TimeScale = TimeScale; -} class GameSettingsService : public EngineService { @@ -49,9 +26,6 @@ public: GameSettingsService() : EngineService(TEXT("GameSettings"), -70) { - GameSettings::Icon = Guid::Empty; - GameSettings::FirstScene = Guid::Empty; - GameSettings::SplashScreen = Guid::Empty; } bool Init() override @@ -60,62 +34,141 @@ public: } }; +IMPLEMENT_SETTINGS_GETTER(BuildSettings, GameCooking); +IMPLEMENT_SETTINGS_GETTER(GraphicsSettings, Graphics); +IMPLEMENT_SETTINGS_GETTER(LayersAndTagsSettings, LayersAndTags); +IMPLEMENT_SETTINGS_GETTER(TimeSettings, Time); +IMPLEMENT_SETTINGS_GETTER(AudioSettings, Audio); +IMPLEMENT_SETTINGS_GETTER(PhysicsSettings, Physics); +IMPLEMENT_SETTINGS_GETTER(InputSettings, Input); + +#if !USE_EDITOR +#if PLATFORM_WINDOWS +IMPLEMENT_SETTINGS_GETTER(WindowsPlatformSettings, WindowsPlatform); +#elif PLATFORM_UWP || PLATFORM_XBOX_ONE +IMPLEMENT_SETTINGS_GETTER(UWPPlatformSettings, UWPPlatform); +#elif PLATFORM_LINUX +IMPLEMENT_SETTINGS_GETTER(LinuxPlatformSettings, LinuxPlatform); +#elif PLATFORM_PS4 +IMPLEMENT_SETTINGS_GETTER(PS4PlatformSettings, PS4Platform); +#elif PLATFORM_XBOX_SCARLETT +IMPLEMENT_SETTINGS_GETTER(XboxScarlettPlatformSettings, XboxScarlettPlatform); +#elif PLATFORM_ANDROID +IMPLEMENT_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform); +#else +#error Unknown platform +#endif +#endif + GameSettingsService GameSettingsServiceInstance; +AssetReference GameSettingsAsset; + +GameSettings* GameSettings::Get() +{ + if (!GameSettingsAsset) + { + // Load root game settings asset. + // It may be missing in editor during dev but must be ready in the build game. + const auto assetPath = Globals::ProjectContentFolder / TEXT("GameSettings.json"); + GameSettingsAsset = Content::LoadAsync(assetPath); + if (GameSettingsAsset == nullptr) + { + LOG(Error, "Missing game settings asset."); + return nullptr; + } + if (GameSettingsAsset->WaitForLoaded()) + { + return nullptr; + } + if (GameSettingsAsset->InstanceType != GameSettings::TypeInitializer) + { + LOG(Error, "Invalid game settings asset data type."); + return nullptr; + } + } + auto asset = GameSettingsAsset.Get(); + if (asset && asset->WaitForLoaded()) + asset = nullptr; + return asset ? (GameSettings*)asset->Instance : nullptr; +} bool GameSettings::Load() { + // Load main settings asset + auto settings = Get(); + if (!settings) + { + return true; + } + + // Preload all settings assets +#define PRELOAD_SETTINGS(type) \ + { \ + if (settings->type) \ + { \ + Content::LoadAsync(settings->type); \ + } \ + else \ + { \ + LOG(Warning, "Missing {0} settings", TEXT(#type)); \ + } \ + } + PRELOAD_SETTINGS(Time); + PRELOAD_SETTINGS(Audio); + PRELOAD_SETTINGS(LayersAndTags); + PRELOAD_SETTINGS(Physics); + PRELOAD_SETTINGS(Input); + PRELOAD_SETTINGS(Graphics); + PRELOAD_SETTINGS(Navigation); + PRELOAD_SETTINGS(GameCooking); +#undef PRELOAD_SETTINGS + + // Apply the game settings to the engine + settings->Apply(); + + return false; +} + +void GameSettings::Apply() +{ + // TODO: impl this +#define APPLY_SETTINGS(type) \ + { \ + type* obj = type::Get(); \ + if (obj) \ + { \ + obj->Apply(); \ + } \ + else \ + { \ + LOG(Warning, "Missing {0} settings", TEXT(#type)); \ + } \ + } + APPLY_SETTINGS(TimeSettings); + APPLY_SETTINGS(AudioSettings); + APPLY_SETTINGS(LayersAndTagsSettings); + APPLY_SETTINGS(PhysicsSettings); + APPLY_SETTINGS(InputSettings); + APPLY_SETTINGS(GraphicsSettings); + APPLY_SETTINGS(NavigationSettings); + APPLY_SETTINGS(BuildSettings); + APPLY_SETTINGS(PlatformSettings); +#undef APPLY_SETTINGS +} + +void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + // Load properties + ProductName = JsonTools::GetString(stream, "ProductName"); + CompanyName = JsonTools::GetString(stream, "CompanyName"); + CopyrightNotice = JsonTools::GetString(stream, "CopyrightNotice"); + Icon = JsonTools::GetGuid(stream, "Icon"); + FirstScene = JsonTools::GetGuid(stream, "FirstScene"); + NoSplashScreen = JsonTools::GetBool(stream, "NoSplashScreen", NoSplashScreen); + SplashScreen = JsonTools::GetGuid(stream, "SplashScreen"); CustomSettings.Clear(); - -#if USE_EDITOR -#define END_POINT(msg) LOG(Warning, msg " Using default values."); return false -#else -#define END_POINT(msg) LOG(Fatal, msg); return true -#endif - -#define LOAD_SETTINGS(nodeName, settingsType) \ - { \ - Guid id = JsonTools::GetGuid(data, nodeName); \ - if (id.IsValid()) \ - { \ - AssetReference subAsset = Content::LoadAsync(id); \ - if (subAsset && !subAsset->WaitForLoaded()) \ - { \ - settingsType::Instance()->Deserialize(*subAsset->Data, nullptr); \ - settingsType::Instance()->Apply(); \ - } \ - else \ - { LOG(Warning, "Cannot load " nodeName " settings"); } \ - } \ - else \ - { LOG(Warning, "Missing " nodeName " settings"); } \ - } - - // Load root game settings asset. - // It may be missing in editor during dev but must be ready in the build game. - const auto assetPath = Globals::ProjectContentFolder / TEXT("GameSettings.json"); - AssetReference asset = Content::LoadAsync(assetPath); - if (asset == nullptr) - { - END_POINT("Missing game settings asset."); - } - if (asset->WaitForLoaded() - || asset->DataTypeName != TEXT("FlaxEditor.Content.Settings.GameSettings") - || asset->Data == nullptr) - { - END_POINT("Cannot load game settings asset."); - } - auto& data = *asset->Data; - - // Load settings - ProductName = JsonTools::GetString(data, "ProductName"); - CompanyName = JsonTools::GetString(data, "CompanyName"); - CopyrightNotice = JsonTools::GetString(data, "CopyrightNotice"); - Icon = JsonTools::GetGuid(data, "Icon"); - FirstScene = JsonTools::GetGuid(data, "FirstScene"); - NoSplashScreen = JsonTools::GetBool(data, "NoSplashScreen", NoSplashScreen); - SplashScreen = JsonTools::GetGuid(data, "SplashScreen"); - const auto customSettings = data.FindMember("CustomSettings"); - if (customSettings != data.MemberEnd()) + const auto customSettings = stream.FindMember("CustomSettings"); + if (customSettings != stream.MemberEnd()) { auto& items = customSettings->value; for (auto it = items.MemberBegin(); it != items.MemberEnd(); ++it) @@ -129,39 +182,21 @@ bool GameSettings::Load() } } - // Load child settings - LOAD_SETTINGS("Time", TimeSettings); - LOAD_SETTINGS("Physics", PhysicsSettings); - LOAD_SETTINGS("LayersAndTags", LayersAndTagsSettings); - LOAD_SETTINGS("Graphics", GraphicsSettings); - LOAD_SETTINGS("GameCooking", BuildSettings); - LOAD_SETTINGS("Input", InputSettings); - LOAD_SETTINGS("Audio", AudioSettings); - LOAD_SETTINGS("Navigation", NavigationSettings); + // Settings containers + DESERIALIZE(Time); + DESERIALIZE(Audio); + DESERIALIZE(LayersAndTags); + DESERIALIZE(Physics); + DESERIALIZE(Input); + DESERIALIZE(Graphics); + DESERIALIZE(Navigation); + DESERIALIZE(GameCooking); - // Load platform settings -#if PLATFORM_WINDOWS - LOAD_SETTINGS("WindowsPlatform", WindowsPlatformSettings); -#endif -#if PLATFORM_UWP - LOAD_SETTINGS("UWPPlatform", UWPPlatformSettings); -#endif -#if PLATFORM_LINUX - LOAD_SETTINGS("LinuxPlatform", LinuxPlatformSettings); -#endif -#if PLATFORM_PS4 - LOAD_SETTINGS("PS4Platform", PS4PlatformSettings); -#endif -#if PLATFORM_XBOX_SCARLETT - LOAD_SETTINGS("XboxScarlettPlatform", XboxScarlettPlatformSettings); -#endif -#if PLATFORM_ANDROID - LOAD_SETTINGS("AndroidPlatform", AndroidPlatformSettings); -#endif -#if USE_EDITOR - LoadPlatformSettingsEditor(data); -#endif - - return false; -#undef END_POINT + // Per-platform settings containers + DESERIALIZE(WindowsPlatform); + DESERIALIZE(UWPPlatform); + DESERIALIZE(LinuxPlatform); + DESERIALIZE(PS4Platform); + DESERIALIZE(XboxScarlettPlatform); + DESERIALIZE(AndroidPlatform); } diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index 6870d8c22..16e1fe7b5 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -2,60 +2,96 @@ #pragma once +#include "Settings.h" #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Collections/Dictionary.h" /// -/// Main engine configuration service. Loads and applies game configuration. +/// The main game engine configuration service. Loads and applies game configuration. /// -class GameSettings +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API GameSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(GameSettings); public: /// /// The product full name. /// - static String ProductName; + API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"General\")") + String ProductName; /// /// The company full name. /// - static String CompanyName; + API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"General\")") + String CompanyName; /// /// The copyright note used for content signing (eg. source code header). /// - static String CopyrightNotice; + API_FIELD(Attributes="EditorOrder(15), EditorDisplay(\"General\")") + String CopyrightNotice; /// /// The default application icon. /// - static Guid Icon; + Guid Icon = Guid::Empty; /// /// Reference to the first scene to load on a game startup. /// - static Guid FirstScene; + Guid FirstScene = Guid::Empty; /// /// True if skip showing splash screen image on the game startup. /// - static bool NoSplashScreen; + bool NoSplashScreen = false; /// /// Reference to the splash screen image to show on a game startup. /// - static Guid SplashScreen; + Guid SplashScreen = Guid::Empty; /// /// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair). /// - static Dictionary CustomSettings; + Dictionary CustomSettings; + +public: + + // Settings containers + Guid Time; + Guid Audio; + Guid LayersAndTags; + Guid Physics; + Guid Input; + Guid Graphics; + Guid Navigation; + Guid GameCooking; + + // Per-platform settings containers + Guid WindowsPlatform; + Guid UWPPlatform; + Guid LinuxPlatform; + Guid PS4Platform; + Guid XboxScarlettPlatform; + Guid AndroidPlatform; + +public: + + /// + /// Gets the instance of the game settings asset (null if missing). Object returned by this method is always loaded with valid data to use. + /// + static GameSettings* Get(); /// /// Loads the game settings (including other settings such as Physics, Input, etc.). /// /// True if failed, otherwise false. static bool Load(); + + // [SettingsBase] + void Apply() override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override; }; diff --git a/Source/Engine/Core/Config/GraphicsSettings.h b/Source/Engine/Core/Config/GraphicsSettings.h index 4e120fdff..17c342e89 100644 --- a/Source/Engine/Core/Config/GraphicsSettings.h +++ b/Source/Engine/Core/Config/GraphicsSettings.h @@ -9,67 +9,68 @@ /// /// Graphics rendering settings. /// -class GraphicsSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API GraphicsSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(GraphicsSettings); public: /// /// Enables rendering synchronization with the refresh rate of the display device to avoid "tearing" artifacts. /// + API_FIELD(Attributes="EditorOrder(20), DefaultValue(false), EditorDisplay(\"General\", \"Use V-Sync\")") bool UseVSync = false; /// /// Anti Aliasing quality setting. /// + API_FIELD(Attributes="EditorOrder(1000), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\", \"AA Quality\")") Quality AAQuality = Quality::Medium; /// /// Screen Space Reflections quality setting. /// + API_FIELD(Attributes="EditorOrder(1100), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\", \"SSR Quality\")") Quality SSRQuality = Quality::Medium; /// /// Screen Space Ambient Occlusion quality setting. /// + API_FIELD(Attributes="EditorOrder(1200), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\", \"SSAO Quality\")") Quality SSAOQuality = Quality::Medium; /// /// Volumetric Fog quality setting. /// + API_FIELD(Attributes="EditorOrder(1250), DefaultValue(Quality.High), EditorDisplay(\"Quality\")") Quality VolumetricFogQuality = Quality::High; /// /// The shadows quality. /// + API_FIELD(Attributes="EditorOrder(1300), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\")") Quality ShadowsQuality = Quality::Medium; /// /// The shadow maps quality (textures resolution). /// + API_FIELD(Attributes="EditorOrder(1310), DefaultValue(Quality.Medium), EditorDisplay(\"Quality\")") Quality ShadowMapsQuality = Quality::Medium; /// /// Enables cascades splits blending for directional light shadows. /// + API_FIELD(Attributes="EditorOrder(1320), DefaultValue(false), EditorDisplay(\"Quality\", \"Allow CSM Blending\")") bool AllowCSMBlending = false; public: + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static GraphicsSettings* Get(); + // [SettingsBase] void Apply() override; - - void RestoreDefault() final override - { - UseVSync = false; - AAQuality = Quality::Medium; - SSRQuality = Quality::Medium; - SSAOQuality = Quality::Medium; - VolumetricFogQuality = Quality::High; - ShadowsQuality = Quality::Medium; - ShadowMapsQuality = Quality::Medium; - AllowCSMBlending = false; - } - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { DESERIALIZE(UseVSync); diff --git a/Source/Engine/Core/Config/LayersTagsSettings.h b/Source/Engine/Core/Config/LayersTagsSettings.h index ee09d9250..44bca9231 100644 --- a/Source/Engine/Core/Config/LayersTagsSettings.h +++ b/Source/Engine/Core/Config/LayersTagsSettings.h @@ -7,8 +7,9 @@ /// /// Layers and objects tags settings. /// -class LayersAndTagsSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API LayersAndTagsSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(LayersAndTagsSettings); public: /// @@ -24,43 +25,11 @@ public: public: /// - /// Gets or adds the tag (returns the tag index). + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// - /// The tag. - /// The tag index. - int32 GetOrAddTag(const String& tag) - { - int32 index = Tags.Find(tag); - if (index == INVALID_INDEX) - { - index = Tags.Count(); - Tags.Add(tag); - } - return index; - } - - /// - /// Gets the amount of non empty layer names (from the beginning, trims the last ones). - /// - /// The layers count. - int32 GetNonEmptyLayerNamesCount() const - { - int32 result = 31; - while (result >= 0 && Layers[result].IsEmpty()) - result--; - return result + 1; - } - -public: + static LayersAndTagsSettings* Get(); // [SettingsBase] - void RestoreDefault() override - { - Tags.Clear(); - for (int32 i = 0; i < 32; i++) - Layers[i].Clear(); - } - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { const auto tags = stream.FindMember("Tags"); diff --git a/Source/Engine/Core/Config/Settings.h b/Source/Engine/Core/Config/Settings.h index fe594d5ae..2822f4a6a 100644 --- a/Source/Engine/Core/Config/Settings.h +++ b/Source/Engine/Core/Config/Settings.h @@ -2,81 +2,46 @@ #pragma once -#include "Engine/Core/Singleton.h" -#include "Engine/Core/Collections/Array.h" #include "Engine/Serialization/ISerializable.h" /// /// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to engine/game. /// -class FLAXENGINE_API Settings +API_CLASS(Abstract) class FLAXENGINE_API SettingsBase : public ISerializable { +DECLARE_SCRIPTING_TYPE_MINIMAL(SettingsBase); public: /// - /// The settings containers. - /// - static Array Containers; - - /// - /// Restores the default settings for all the registered containers. - /// - static void RestoreDefaultAll() - { - for (int32 i = 0; i < Containers.Count(); i++) - Containers[i]->RestoreDefault(); - } - -private: - - // Disable copy/move - Settings(const Settings&) = delete; - Settings& operator=(const Settings&) = delete; - -protected: - - Settings() - { - Containers.Add(this); - } - -public: - - virtual ~Settings() = default; - -public: - - typedef ISerializable::DeserializeStream DeserializeStream; - - /// - /// Applies the settings to the target services. + /// Applies the settings to the target system. /// virtual void Apply() { } - /// - /// Restores the default settings. - /// - virtual void RestoreDefault() = 0; +public: - /// - /// Deserializes the settings container. - /// - /// The input data stream. - /// The deserialization modifier object. Always valid. - virtual void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) = 0; -}; - -/// -/// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to engine/game. -/// -template -class SettingsBase : public Settings, public Singleton -{ -protected: - - SettingsBase() + // [ISerializable] + void Serialize(SerializeStream& stream, const void* otherObj) override { + // Not supported (Editor C# edits settings data) } }; + +// Helper utility define for settings getter implementation code +#define IMPLEMENT_SETTINGS_GETTER(type, field) \ + type* type::Get() \ + { \ + static type DefaultInstance; \ + type* result = &DefaultInstance; \ + const auto gameSettings = GameSettings::Get(); \ + if (gameSettings) \ + { \ + const auto asset = Content::Load(gameSettings->field); \ + if (asset && asset->Instance && asset->InstanceType == type::TypeInitializer) \ + { \ + result = static_cast(asset->Instance); \ + } \ + } \ + return result; \ + } diff --git a/Source/Engine/Core/Config/TimeSettings.h b/Source/Engine/Core/Config/TimeSettings.h index 4b4deaaf8..7e383380f 100644 --- a/Source/Engine/Core/Config/TimeSettings.h +++ b/Source/Engine/Core/Config/TimeSettings.h @@ -3,60 +3,53 @@ #pragma once #include "Engine/Core/Config/Settings.h" -#include "Engine/Serialization/Serialization.h" /// /// Time and game simulation settings container. /// -class TimeSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API TimeSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(TimeSettings); public: /// /// The target amount of the game logic updates per second (script updates frequency). /// + API_FIELD(Attributes="EditorOrder(1), DefaultValue(30.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Update FPS\")") float UpdateFPS = 30.0f; /// /// The target amount of the physics simulation updates per second (also fixed updates frequency). /// + API_FIELD(Attributes="EditorOrder(2), DefaultValue(60.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Physics FPS\")") float PhysicsFPS = 60.0f; /// /// The target amount of the frames rendered per second (actual game FPS). /// + API_FIELD(Attributes="EditorOrder(3), DefaultValue(60.0f), Limit(0, 1000), EditorDisplay(\"General\", \"Draw FPS\")") float DrawFPS = 60.0f; /// /// The game time scale factor. Default is 1. /// + API_FIELD(Attributes="EditorOrder(10), DefaultValue(1.0f), Limit(0, 1000.0f, 0.1f), EditorDisplay(\"General\")") float TimeScale = 1.0f; /// /// The maximum allowed delta time (in seconds) for the game logic update step. /// - float MaxUpdateDeltaTime = (1.0f / 10.0f); + API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.1f), Limit(0.1f, 1000.0f, 0.01f), EditorDisplay(\"General\")") + float MaxUpdateDeltaTime = 0.1f; public: + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static TimeSettings* Get(); + // [SettingsBase] void Apply() override; - - void RestoreDefault() override - { - UpdateFPS = 30.0f; - PhysicsFPS = 60.0f; - DrawFPS = 60.0f; - TimeScale = 1.0f; - MaxUpdateDeltaTime = 1.0f / 10.0f; - } - - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - DESERIALIZE(UpdateFPS); - DESERIALIZE(PhysicsFPS); - DESERIALIZE(DrawFPS); - DESERIALIZE(TimeScale); - DESERIALIZE(MaxUpdateDeltaTime); - } + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override; }; diff --git a/Source/Engine/Core/Math/Math.cpp b/Source/Engine/Core/Math/Math.cpp index 4075bcbda..d0642d7ca 100644 --- a/Source/Engine/Core/Math/Math.cpp +++ b/Source/Engine/Core/Math/Math.cpp @@ -5,8 +5,8 @@ void Math::SinCos(float angle, float& sine, float& cosine) { - sine = sin(angle); - cosine = cos(angle); + sine = Math::Sin(angle); + cosine = Math::Cos(angle); } uint32 Math::FloorLog2(uint32 value) diff --git a/Source/Engine/Engine/Base/GameBase.cpp b/Source/Engine/Engine/Base/GameBase.cpp index 6c02ce1d1..c313b0737 100644 --- a/Source/Engine/Engine/Base/GameBase.cpp +++ b/Source/Engine/Engine/Base/GameBase.cpp @@ -119,7 +119,10 @@ bool GameBase::Init() } // Preload first scene asset data - GameBaseImpl::FirstScene = GameSettings::FirstScene; + const auto gameSettings = GameSettings::Get(); + if (!gameSettings) + return true; + GameBaseImpl::FirstScene = gameSettings->FirstScene; return false; } @@ -261,8 +264,8 @@ void GameBaseImpl::OnSplashScreenEnd() // Load the first scene LOG(Info, "Loading the first scene"); + const auto sceneId = FirstScene ? FirstScene.GetID() : Guid::Empty; FirstScene.Unlink(); - const auto sceneId = GameSettings::FirstScene; if (Level::LoadSceneAsync(sceneId)) { LOG(Fatal, "Cannot load the first scene."); diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index ebf268957..f9313b1b4 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -51,6 +51,9 @@ namespace EngineImpl { bool IsReady = false; +#if !USE_EDITOR + bool RunInBackground = false; +#endif String CommandLine = nullptr; int32 Fps = 0, FpsAccumulatedFrames = 0; double FpsAccumulated = 0.0; @@ -133,6 +136,9 @@ int32 Engine::Main(const Char* cmdLine) Platform::BeforeRun(); EngineImpl::InitMainWindow(); Application::BeforeRun(); +#if !USE_EDITOR && (PLATFORM_WINDOWS || PLATFORM_LINUX) + EngineImpl::RunInBackground = PlatformSettings::Get()->RunInBackground; +#endif Log::Logger::WriteFloor(); LOG_FLUSH(); Time::OnBeforeRun(); @@ -279,11 +285,7 @@ void Engine::OnUpdate() bool isGameRunning = true; if (mainWindow && !mainWindow->IsFocused()) { -#if PLATFORM_WINDOWS || PLATFORM_LINUX - isGameRunning = PlatformSettings::Instance()->RunInBackground; -#else - isGameRunning = false; -#endif + isGameRunning = EngineImpl::RunInBackground; } Time::SetGamePaused(!isGameRunning); #endif @@ -393,8 +395,11 @@ const String& Engine::GetCommandLine() JsonAsset* Engine::GetCustomSettings(const StringView& key) { + const auto settings = GameSettings::Get(); + if (!settings) + return nullptr; Guid assetId = Guid::Empty; - GameSettings::CustomSettings.TryGet(key, assetId); + settings->CustomSettings.TryGet(key, assetId); return Content::LoadAsync(assetId); } diff --git a/Source/Engine/Engine/Linux/LinuxGame.cpp b/Source/Engine/Engine/Linux/LinuxGame.cpp index 43bfe4a6e..1f59a66a9 100644 --- a/Source/Engine/Engine/Linux/LinuxGame.cpp +++ b/Source/Engine/Engine/Linux/LinuxGame.cpp @@ -16,7 +16,7 @@ void LinuxGame::InitMainWindowSettings(CreateWindowSettings& settings) { // TODO: restore window size and fullscreen mode from the cached local settings saved after previous session - const auto platformSettings = LinuxPlatformSettings::Instance(); + const auto platformSettings = LinuxPlatformSettings::Get(); auto windowMode = platformSettings->WindowMode; // Use command line switches @@ -54,7 +54,7 @@ void LinuxGame::InitMainWindowSettings(CreateWindowSettings& settings) bool LinuxGame::Init() { - const auto platformSettings = LinuxPlatformSettings::Instance(); + const auto platformSettings = LinuxPlatformSettings::Get(); // Create mutex if need to if (platformSettings->ForceSingleInstance) diff --git a/Source/Engine/Engine/Time.cpp b/Source/Engine/Engine/Time.cpp index 1afe7e05a..f26f4f759 100644 --- a/Source/Engine/Engine/Time.cpp +++ b/Source/Engine/Engine/Time.cpp @@ -3,17 +3,19 @@ #include "Time.h" #include "EngineService.h" #include "Engine/Core/Math/Math.h" -#include "Engine/Core/Config/TimeSettings.h" #include "Engine/Platform/Platform.h" -#include "Engine/Physics/PhysicsSettings.h" +#include "Engine/Core/Config/TimeSettings.h" +#include "Engine/Serialization/Serialization.h" namespace { bool FixedDeltaTimeEnable; float FixedDeltaTimeValue; + float MaxUpdateDeltaTime = 0.1f; } bool Time::_gamePaused = false; +float Time::_physicsMaxDeltaTime = 0.1f; DateTime Time::StartupTime; float Time::UpdateFPS = 30.0f; float Time::PhysicsFPS = 60.0f; @@ -43,6 +45,24 @@ public: TimeService TimeServiceInstance; +void TimeSettings::Apply() +{ + Time::UpdateFPS = UpdateFPS; + Time::PhysicsFPS = PhysicsFPS; + Time::DrawFPS = DrawFPS; + Time::TimeScale = TimeScale; + ::MaxUpdateDeltaTime = MaxUpdateDeltaTime; +} + +void TimeSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + DESERIALIZE(UpdateFPS); + DESERIALIZE(PhysicsFPS); + DESERIALIZE(DrawFPS); + DESERIALIZE(TimeScale); + DESERIALIZE(MaxUpdateDeltaTime); +} + void Time::TickData::OnBeforeRun(float targetFps, double currentTime) { Time = UnscaledTime = TimeSpan::Zero(); @@ -219,7 +239,7 @@ void Time::OnBeforeRun() bool Time::OnBeginUpdate() { - if (Update.OnTickBegin(UpdateFPS, TimeSettings::Instance()->MaxUpdateDeltaTime)) + if (Update.OnTickBegin(UpdateFPS, MaxUpdateDeltaTime)) { Current = &Update; return true; @@ -229,7 +249,7 @@ bool Time::OnBeginUpdate() bool Time::OnBeginPhysics() { - if (Physics.OnTickBegin(PhysicsFPS, PhysicsSettings::Instance()->MaxDeltaTime)) + if (Physics.OnTickBegin(PhysicsFPS, _physicsMaxDeltaTime)) { Current = &Physics; return true; diff --git a/Source/Engine/Engine/Time.h b/Source/Engine/Engine/Time.h index 0e8c25806..0d44afa22 100644 --- a/Source/Engine/Engine/Time.h +++ b/Source/Engine/Engine/Time.h @@ -15,6 +15,7 @@ API_CLASS(Static) class FLAXENGINE_API Time DECLARE_SCRIPTING_TYPE_NO_SPAWN(Time); friend class Engine; friend class TimeService; + friend class PhysicsSettings; public: /// @@ -100,6 +101,7 @@ public: private: static bool _gamePaused; + static float _physicsMaxDeltaTime; public: diff --git a/Source/Engine/Engine/Windows/WindowsGame.cpp b/Source/Engine/Engine/Windows/WindowsGame.cpp index da82cc7fc..4b7496481 100644 --- a/Source/Engine/Engine/Windows/WindowsGame.cpp +++ b/Source/Engine/Engine/Windows/WindowsGame.cpp @@ -11,7 +11,7 @@ void WindowsGame::InitMainWindowSettings(CreateWindowSettings& settings) { // TODO: restore window size and fullscreen mode from the cached local settings saved after previous session - const auto platformSettings = WindowsPlatformSettings::Instance(); + const auto platformSettings = WindowsPlatformSettings::Get(); auto windowMode = platformSettings->WindowMode; // Use command line switches @@ -45,7 +45,7 @@ void WindowsGame::InitMainWindowSettings(CreateWindowSettings& settings) bool WindowsGame::Init() { - const auto platformSettings = WindowsPlatformSettings::Instance(); + const auto platformSettings = WindowsPlatformSettings::Get(); // Create mutex if need to if (platformSettings->ForceSingleInstance) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp index 64151749b..e9da74a99 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp @@ -91,7 +91,7 @@ GPUDevice* GPUDeviceDX11::Create() else if (CommandLine::Options.D3D11) maxAllowedFeatureLevel = D3D_FEATURE_LEVEL_11_0; #if !USE_EDITOR && PLATFORM_WINDOWS - auto winSettings = WindowsPlatformSettings::Instance(); + auto winSettings = WindowsPlatformSettings::Get(); if (!winSettings->SupportDX11 && !winSettings->SupportDX10) { // Skip if there is no support diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 29d417834..317a6e9aa 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -45,7 +45,7 @@ GPUDevice* GPUDeviceDX12::Create() selectedAdapter.Description.VendorId = GPU_VENDOR_ID_AMD; #else #if !USE_EDITOR && PLATFORM_WINDOWS - auto winSettings = WindowsPlatformSettings::Instance(); + auto winSettings = WindowsPlatformSettings::Get(); if (!winSettings->SupportDX12) { // Skip if there is no support diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 33df0db47..85fc3dcd0 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1076,7 +1076,7 @@ GPUDeviceVulkan::GPUDeviceVulkan(ShaderProfile shaderProfile, GPUAdapterVulkan* GPUDevice* GPUDeviceVulkan::Create() { #if !USE_EDITOR && (PLATFORM_WINDOWS || PLATFORM_LINUX) - auto settings = PlatformSettings::Instance(); + auto settings = PlatformSettings::Get(); if (!settings->SupportVulkan) { // Skip if there is no support diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 1dfa0e5fe..be11f091d 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -98,6 +98,12 @@ Delegate Input::ActionTriggered; Array Input::ActionMappings; Array Input::AxisMappings; +void InputSettings::Apply() +{ + Input::ActionMappings = ActionMappings; + Input::AxisMappings = AxisMappings; +} + int32 Input::GetGamepadsCount() { return Gamepads.Count(); diff --git a/Source/Engine/Input/Input.h b/Source/Engine/Input/Input.h index 33852f9ec..4ef7bfffb 100644 --- a/Source/Engine/Input/Input.h +++ b/Source/Engine/Input/Input.h @@ -21,7 +21,6 @@ class InputDevice; API_CLASS(Static) class FLAXENGINE_API Input { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Input); -public: /// /// Gets the mouse (null if platform does not support mouse or it is not connected). diff --git a/Source/Engine/Input/InputSettings.h b/Source/Engine/Input/InputSettings.h index df1f4d60c..7771028e8 100644 --- a/Source/Engine/Input/InputSettings.h +++ b/Source/Engine/Input/InputSettings.h @@ -5,14 +5,13 @@ #include "Engine/Core/Config/Settings.h" #include "Engine/Serialization/JsonTools.h" #include "VirtualInput.h" -#include "Input.h" /// /// Input settings container. /// -/// -class InputSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API InputSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(InputSettings); public: /// @@ -27,19 +26,14 @@ public: public: - // [SettingsBase] - void Apply() override - { - Input::ActionMappings = ActionMappings; - Input::AxisMappings = AxisMappings; - } + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static InputSettings* Get(); + + // [SettingsBase] + void Apply() override; - void RestoreDefault() override - { - ActionMappings.Resize(0); - AxisMappings.Resize(0); - } - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { const auto actionMappings = stream.FindMember("ActionMappings"); diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 81a5b3155..900d802ed 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -8,7 +8,6 @@ #include "Prefabs/Prefab.h" #include "Prefabs/PrefabManager.h" #include "Engine/Core/Log.h" -#include "Engine/Core/Config/LayersTagsSettings.h" #include "Engine/Scripting/Script.h" #include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Threading/Threading.h" @@ -386,21 +385,19 @@ Actor* Actor::GetChildByPrefabObjectId(const Guid& prefabObjectId) const bool Actor::HasTag(const StringView& tag) const { - return HasTag() && tag == LayersAndTagsSettings::Instance()->Tags[_tag]; + return HasTag() && tag == Level::Tags[_tag]; } const String& Actor::GetLayerName() const { - const auto settings = LayersAndTagsSettings::Instance(); - return settings->Layers[_layer]; + return Level::Layers[_layer]; } const String& Actor::GetTag() const { if (HasTag()) { - const auto settings = LayersAndTagsSettings::Instance(); - return settings->Tags[_tag]; + return Level::Tags[_tag]; } return String::Empty; } @@ -420,13 +417,13 @@ void Actor::SetTagIndex(int32 tagIndex) if (tagIndex == ACTOR_TAG_INVALID) { } - else if (LayersAndTagsSettings::Instance()->Tags.IsEmpty()) + else if (Level::Tags.IsEmpty()) { tagIndex = ACTOR_TAG_INVALID; } else { - tagIndex = tagIndex < 0 ? ACTOR_TAG_INVALID : Math::Min(tagIndex, LayersAndTagsSettings::Instance()->Tags.Count() - 1); + tagIndex = tagIndex < 0 ? ACTOR_TAG_INVALID : Math::Min(tagIndex, Level::Tags.Count() - 1); } if (tagIndex == _tag) return; @@ -444,7 +441,7 @@ void Actor::SetTag(const StringView& tagName) } else { - tagIndex = LayersAndTagsSettings::Instance()->Tags.Find(tagName); + tagIndex = Level::Tags.Find(tagName); if (tagIndex == -1) { LOG(Error, "Cannot change actor tag. Given value is invalid."); @@ -971,7 +968,7 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) if (tag->value.IsString() && tag->value.GetStringLength()) { const String tagName = tag->value.GetText(); - _tag = LayersAndTagsSettings::Instance()->GetOrAddTag(tagName); + _tag = Level::GetOrAddTag(tagName); } } diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 18d9d4a52..7d257e104 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -9,6 +9,7 @@ #include "Engine/Core/Cache.h" #include "Engine/Core/Collections/CollectionPoolCache.h" #include "Engine/Core/ObjectsRemovalService.h" +#include "Engine/Core/Config/LayersTagsSettings.h" #include "Engine/Debug/Exceptions/ArgumentException.h" #include "Engine/Debug/Exceptions/ArgumentNullException.h" #include "Engine/Debug/Exceptions/InvalidOperationException.h" @@ -97,6 +98,7 @@ public: { } + bool Init() override; void Update() override; void LateUpdate() override; void FixedUpdate() override; @@ -124,6 +126,8 @@ Delegate Level::SceneUnloaded; Action Level::ScriptsReloadStart; Action Level::ScriptsReload; Action Level::ScriptsReloadEnd; +Array Level::Tags; +String Level::Layers[32]; bool LevelImpl::spawnActor(Actor* actor, Actor* parent) { @@ -158,6 +162,15 @@ bool LevelImpl::deleteActor(Actor* actor) return false; } +bool LevelService::Init() +{ + auto& settings = *LayersAndTagsSettings::Get(); + Level::Tags = settings.Tags; + for (int32 i = 0; i < ARRAY_COUNT(Level::Layers); i++) + Level::Layers[i] = settings.Layers[i]; + return false; +} + void LevelService::Update() { PROFILE_CPU(); @@ -642,6 +655,25 @@ void LevelImpl::CallSceneEvent(SceneEventType eventType, Scene* scene, Guid scen } } +int32 Level::GetOrAddTag(const StringView& tag) +{ + int32 index = Tags.Find(tag); + if (index == INVALID_INDEX) + { + index = Tags.Count(); + Tags.AddOne() = tag; + } + return index; +} + +int32 Level::GetNonEmptyLayerNamesCount() +{ + int32 result = 31; + while (result >= 0 && Layers[result].IsEmpty()) + result--; + return result + 1; +} + void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b) { PROFILE_CPU(); diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index b1ee090f4..a1e1d73cd 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -420,6 +420,31 @@ public: /// Output array with only parents static void ConstructParentActorsTreeList(const Array& input, Array& output); +public: + + /// + /// The tags names. + /// + static Array Tags; + + /// + /// The layers names. + /// + static String Layers[32]; + + /// + /// Gets or adds the tag (returns the tag index). + /// + /// The tag. + /// The tag index. + static int32 GetOrAddTag(const StringView& tag); + + /// + /// Gets the amount of non empty layer names (from the beginning, trims the last ones). + /// + /// The layers count. + static int32 GetNonEmptyLayerNamesCount(); + private: // Actor API diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index b2679f861..caa2513d2 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -461,13 +461,13 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou float GetTileSize() { - auto& settings = *NavigationSettings::Instance(); + auto& settings = *NavigationSettings::Get(); return settings.CellSize * settings.TileSize; } void InitConfig(rcConfig& config) { - auto& settings = *NavigationSettings::Instance(); + auto& settings = *NavigationSettings::Get(); config.cs = settings.CellSize; config.ch = settings.CellHeight; diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 143449547..6decb4be7 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -1,8 +1,12 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Navigation.h" +#include "NavigationSettings.h" #include "NavMeshRuntime.h" #include "NavMeshBuilder.h" +#include "Engine/Core/Config/GameSettings.h" +#include "Engine/Content/Content.h" +#include "Engine/Content/JsonAsset.h" #include "Engine/Threading/Threading.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Engine/EngineService.h" @@ -55,6 +59,8 @@ void* rcAllocDefault(size_t size, rcAllocHint) return Allocator::Allocate(size); } +IMPLEMENT_SETTINGS_GETTER(NavigationSettings, Navigation); + bool NavigationService::Init() { // Link memory allocation calls to use engine default allocator diff --git a/Source/Engine/Navigation/NavigationSettings.cs b/Source/Engine/Navigation/NavigationSettings.cs new file mode 100644 index 000000000..ff6f2aa4a --- /dev/null +++ b/Source/Engine/Navigation/NavigationSettings.cs @@ -0,0 +1,8 @@ +// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. + +namespace FlaxEditor.Content.Settings +{ + partial class NavigationSettings + { + } +} diff --git a/Source/Engine/Navigation/NavigationSettings.h b/Source/Engine/Navigation/NavigationSettings.h index d7c3aa205..fe4189955 100644 --- a/Source/Engine/Navigation/NavigationSettings.h +++ b/Source/Engine/Navigation/NavigationSettings.h @@ -9,7 +9,7 @@ /// /// The navigation system settings container. /// -API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API NavigationSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API NavigationSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(NavigationSettings); public: @@ -94,24 +94,12 @@ public: public: - // [SettingsBase] - void RestoreDefault() final override - { - CellHeight = 10.0f; - CellSize = 30.0f; - TileSize = 64; - MinRegionArea = 0; - MergeRegionArea = 20; - MaxEdgeLen = 1200.0f; - MaxEdgeError = 1.3f; - DetailSamplingDist = 600.0f; - MaxDetailSamplingError = 1.0f; - WalkableRadius = 34.0f; - WalkableHeight = 144.0f; - WalkableMaxClimb = 35.0f; - WalkableMaxSlopeAngle = 60.0f; - } + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static NavigationSettings* Get(); + // [SettingsBase] void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { DESERIALIZE(CellHeight); diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index e73963c7c..2eeb266aa 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -232,7 +232,7 @@ void Collider::UpdateLayerBits() filterData.word0 = GetLayerMask(); // Own layer mask - filterData.word1 = PhysicsSettings::Instance()->LayerMasks[GetLayer()]; + filterData.word1 = Physics::LayerMasks[GetLayer()]; _shape->setSimulationFilterData(filterData); _shape->setQueryFilterData(filterData); diff --git a/Source/Engine/Physics/PhysicalMaterial.cpp b/Source/Engine/Physics/PhysicalMaterial.cpp deleted file mode 100644 index 9870bfc65..000000000 --- a/Source/Engine/Physics/PhysicalMaterial.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#include "PhysicalMaterial.h" -#include "PhysicsSettings.h" -#include "Physics.h" -#include -#include - -PhysicalMaterial::PhysicalMaterial() - : _material(nullptr) -{ -} - -PhysicalMaterial::~PhysicalMaterial() -{ - if (_material) - { - Physics::RemoveMaterial(_material); - } -} - -PxMaterial* PhysicalMaterial::GetPhysXMaterial() -{ - if (_material == nullptr && CPhysX) - { - _material = CPhysX->createMaterial(Friction, Friction, Restitution); - _material->userData = this; - - const PhysicsCombineMode useFrictionCombineMode = (OverrideFrictionCombineMode ? FrictionCombineMode : PhysicsSettings::Instance()->FrictionCombineMode); - _material->setFrictionCombineMode(static_cast(useFrictionCombineMode)); - - const PhysicsCombineMode useRestitutionCombineMode = (OverrideRestitutionCombineMode ? RestitutionCombineMode : PhysicsSettings::Instance()->RestitutionCombineMode); - _material->setRestitutionCombineMode(static_cast(useRestitutionCombineMode)); - } - - return _material; -} - -void PhysicalMaterial::UpdatePhysXMaterial() -{ - if (_material != nullptr) - { - _material->setStaticFriction(Friction); - _material->setDynamicFriction(Friction); - const PhysicsCombineMode useFrictionCombineMode = (OverrideFrictionCombineMode ? FrictionCombineMode : PhysicsSettings::Instance()->FrictionCombineMode); - _material->setFrictionCombineMode(static_cast(useFrictionCombineMode)); - - _material->setRestitution(Restitution); - const PhysicsCombineMode useRestitutionCombineMode = (OverrideRestitutionCombineMode ? RestitutionCombineMode : PhysicsSettings::Instance()->RestitutionCombineMode); - _material->setRestitutionCombineMode(static_cast(useRestitutionCombineMode)); - } -} diff --git a/Source/Engine/Physics/Physics.cpp b/Source/Engine/Physics/Physics.cpp index 2daa47010..98237948f 100644 --- a/Source/Engine/Physics/Physics.cpp +++ b/Source/Engine/Physics/Physics.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Physics.h" +#include "PhysicalMaterial.h" #include "Engine/Core/Log.h" #include "Engine/Threading/Threading.h" #include "Engine/Platform/CPUInfo.h" @@ -13,6 +14,8 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Core/Memory/Memory.h" #include "Engine/Engine/EngineService.h" +#include "Engine/Serialization/Serialization.h" +#include "Engine/Engine/Time.h" #include #include #if WITH_PVD @@ -110,34 +113,43 @@ struct ActionData }; PxPhysics* CPhysX = nullptr; -PhysXAllocator PhysXAllocatorCallback; -PhysXError PhysXErrorCallback; -PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader; #if WITH_PVD PxPvd* CPVD = nullptr; #endif -PxTolerancesScale ToleranceScale; -SimulationEventCallback EventsCallback; -void* ScratchMemory = nullptr; -FixedStepper* Stepper = nullptr; -CriticalSection FlushLocker; -Array NewActors; -Array Actions; -Array DeadActors; -Array DeadMaterials; -Array _deadObjects; -Array DeadColliders; -Array DeadJoints; -bool _isDuringSimulation = false; -PxFoundation* _foundation = nullptr; + +namespace +{ + PhysXAllocator PhysXAllocatorCallback; + PhysXError PhysXErrorCallback; + PxSimulationFilterShader PhysXDefaultFilterShader = PxDefaultSimulationFilterShader; + PxTolerancesScale ToleranceScale; + SimulationEventCallback EventsCallback; + void* ScratchMemory = nullptr; + FixedStepper* Stepper = nullptr; + CriticalSection FlushLocker; + Array NewActors; + Array Actions; + Array DeadActors; + Array DeadMaterials; + Array _deadObjects; + Array DeadColliders; + Array DeadJoints; + bool _queriesHitTriggers = true; + bool _isDuringSimulation = false; + PhysicsCombineMode _frictionCombineMode = PhysicsCombineMode::Average; + PhysicsCombineMode _restitutionCombineMode = PhysicsCombineMode::Average; + PxFoundation* _foundation = nullptr; #if COMPILE_WITH_PHYSICS_COOKING -PxCooking* Cooking = nullptr; + PxCooking* Cooking = nullptr; #endif -PxScene* PhysicsScene = nullptr; -PxMaterial* DefaultMaterial = nullptr; -PxControllerManager* ControllerManager = nullptr; -PxCpuDispatcher* CpuDispatcher = nullptr; + PxScene* PhysicsScene = nullptr; + PxMaterial* DefaultMaterial = nullptr; + PxControllerManager* ControllerManager = nullptr; + PxCpuDispatcher* CpuDispatcher = nullptr; +} + bool Physics::AutoSimulation = true; +uint32 Physics::LayerMasks[32]; class PhysicsService : public EngineService { @@ -146,6 +158,8 @@ public: PhysicsService() : EngineService(TEXT("Physics"), 0) { + for (int32 i = 0; i < 32; i++) + Physics::LayerMasks[i] = MAX_uint32; } bool Init() override; @@ -155,12 +169,146 @@ public: PhysicsService PhysicsServiceInstance; +PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled) +{ +#if WITH_PVD + PxShapeFlags flags = PxShapeFlag::eVISUALIZATION; +#else + PxShapeFlags flags = static_cast(0); +#endif + + if (isEnabled) + { + if (isTrigger) + { + flags |= PxShapeFlag::eTRIGGER_SHAPE; + if (_queriesHitTriggers) + flags |= PxShapeFlag::eSCENE_QUERY_SHAPE; + } + else + { + flags = PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eSCENE_QUERY_SHAPE; + } + } + + return flags; +} + +void PhysicsSettings::Apply() +{ + Time::_physicsMaxDeltaTime = MaxDeltaTime; + _queriesHitTriggers = QueriesHitTriggers; + _frictionCombineMode = FrictionCombineMode; + _restitutionCombineMode = RestitutionCombineMode; + Platform::MemoryCopy(Physics::LayerMasks, LayerMasks, sizeof(LayerMasks)); + Physics::SetGravity(DefaultGravity); + Physics::SetBounceThresholdVelocity(BounceThresholdVelocity); + Physics::SetEnableCCD(!DisableCCD); + + // TODO: setting eADAPTIVE_FORCE requires PxScene setup (physx docs: This flag is not mutable, and must be set in PxSceneDesc at scene creation.) + // TODO: update all shapes filter data + // TODO: update all shapes flags + + /* + { + get all actors and then: + + const PxU32 numShapes = actor->getNbShapes(); + PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*numShapes); + actor->getShapes(shapes, numShapes); + for (PxU32 i = 0; i < numShapes; i++) + { + .. + } + SAMPLE_FREE(shapes); + }*/ +} + +PhysicsSettings::PhysicsSettings() +{ + for (int32 i = 0; i < 32; i++) + LayerMasks[i] = MAX_uint32; +} + +void PhysicsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + DESERIALIZE(DefaultGravity); + DESERIALIZE(TriangleMeshTriangleMinAreaThreshold); + DESERIALIZE(BounceThresholdVelocity); + DESERIALIZE(FrictionCombineMode); + DESERIALIZE(RestitutionCombineMode); + DESERIALIZE(DisableCCD); + DESERIALIZE(EnableAdaptiveForce); + DESERIALIZE(MaxDeltaTime); + DESERIALIZE(EnableSubstepping); + DESERIALIZE(SubstepDeltaTime); + DESERIALIZE(MaxSubsteps); + DESERIALIZE(QueriesHitTriggers); + DESERIALIZE(SupportCookingAtRuntime); + + const auto layers = stream.FindMember("LayerMasks"); + if (layers != stream.MemberEnd()) + { + auto& layersArray = layers->value; + ASSERT(layersArray.IsArray()); + for (uint32 i = 0; i < layersArray.Size() && i < 32; i++) + { + LayerMasks[i] = layersArray[i].GetUint(); + } + } +} + +PhysicalMaterial::PhysicalMaterial() + : _material(nullptr) +{ +} + +PhysicalMaterial::~PhysicalMaterial() +{ + if (_material) + { + Physics::RemoveMaterial(_material); + } +} + +PxMaterial* PhysicalMaterial::GetPhysXMaterial() +{ + if (_material == nullptr && CPhysX) + { + _material = CPhysX->createMaterial(Friction, Friction, Restitution); + _material->userData = this; + + const PhysicsCombineMode useFrictionCombineMode = OverrideFrictionCombineMode ? FrictionCombineMode : _frictionCombineMode; + _material->setFrictionCombineMode(static_cast(useFrictionCombineMode)); + + const PhysicsCombineMode useRestitutionCombineMode = OverrideRestitutionCombineMode ? RestitutionCombineMode : _restitutionCombineMode; + _material->setRestitutionCombineMode(static_cast(useRestitutionCombineMode)); + } + + return _material; +} + +void PhysicalMaterial::UpdatePhysXMaterial() +{ + if (_material != nullptr) + { + _material->setStaticFriction(Friction); + _material->setDynamicFriction(Friction); + const PhysicsCombineMode useFrictionCombineMode = OverrideFrictionCombineMode ? FrictionCombineMode : _frictionCombineMode; + _material->setFrictionCombineMode(static_cast(useFrictionCombineMode)); + + _material->setRestitution(Restitution); + const PhysicsCombineMode useRestitutionCombineMode = OverrideRestitutionCombineMode ? RestitutionCombineMode : _restitutionCombineMode; + _material->setRestitutionCombineMode(static_cast(useRestitutionCombineMode)); + } +} + bool PhysicsService::Init() { #define CHECK_INIT(value, msg) if(!value) { LOG(Error, msg); return true; } auto cpuInfo = Platform::GetCPUInfo(); - auto settings = PhysicsSettings::Instance(); + auto& settings = *PhysicsSettings::Get(); // Send info LOG(Info, "Setup NVIDIA PhysX {0}.{1}.{2}", PX_PHYSICS_VERSION_MAJOR, PX_PHYSICS_VERSION_MINOR, PX_PHYSICS_VERSION_BUGFIX); @@ -220,7 +368,7 @@ bool PhysicsService::Init() #if COMPILE_WITH_PHYSICS_COOKING #if !USE_EDITOR - if (settings->SupportCookingAtRuntime) + if (settings.SupportCookingAtRuntime) #endif { // Init cooking @@ -235,16 +383,16 @@ bool PhysicsService::Init() // Create scene description PxSceneDesc sceneDesc(CPhysX->getTolerancesScale()); - sceneDesc.gravity = C2P(settings->DefaultGravity); + sceneDesc.gravity = C2P(settings.DefaultGravity); sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS; //sceneDesc.flags |= PxSceneFlag::eEXCLUDE_KINEMATICS_FROM_ACTIVE_ACTORS; // TODO: set it? - if (!settings->DisableCCD) + if (!settings.DisableCCD) sceneDesc.flags |= PxSceneFlag::eENABLE_CCD; - if (settings->EnableAdaptiveForce) + if (settings.EnableAdaptiveForce) sceneDesc.flags |= PxSceneFlag::eADAPTIVE_FORCE; sceneDesc.simulationEventCallback = &EventsCallback; sceneDesc.filterShader = PhysiXFilterShader; - sceneDesc.bounceThresholdVelocity = settings->BounceThresholdVelocity; + sceneDesc.bounceThresholdVelocity = settings.BounceThresholdVelocity; if (sceneDesc.cpuDispatcher == nullptr) { CpuDispatcher = PxDefaultCpuDispatcherCreate(Math::Clamp(cpuInfo.ProcessorCoreCount - 1, 1, 4)); @@ -366,7 +514,7 @@ void Physics::SetGravity(const Vector3& value) bool Physics::GetEnableCCD() { - return PhysicsScene ? (PhysicsScene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings_DisableCCD; + return PhysicsScene ? (PhysicsScene->getFlags() & PxSceneFlag::eENABLE_CCD) == PxSceneFlag::eENABLE_CCD : !PhysicsSettings::Get()->DisableCCD; } void Physics::SetEnableCCD(const bool value) @@ -377,7 +525,7 @@ void Physics::SetEnableCCD(const bool value) float Physics::GetBounceThresholdVelocity() { - return PhysicsScene ? PhysicsScene->getBounceThresholdVelocity() : PhysicsSettings_BounceThresholdVelocity; + return PhysicsScene ? PhysicsScene->getBounceThresholdVelocity() : PhysicsSettings::Get()->BounceThresholdVelocity; } void Physics::SetBounceThresholdVelocity(const float value) @@ -390,13 +538,13 @@ void Physics::Simulate(float dt) { ASSERT(IsInMainThread() && !_isDuringSimulation); ASSERT(CPhysX); - const auto settings = PhysicsSettings::Instance(); + const auto& settings = *PhysicsSettings::Get(); // Flush the old/new objects and the other requests before the simulation FlushRequests(); // Clamp delta - dt = Math::Clamp(dt, 0.0f, settings->MaxDeltaTime); + dt = Math::Clamp(dt, 0.0f, settings.MaxDeltaTime); // Prepare util objects if (ScratchMemory == nullptr) @@ -407,10 +555,10 @@ void Physics::Simulate(float dt) { Stepper = New(); } - if (settings->EnableSubstepping) + if (settings.EnableSubstepping) { // Use substeps - Stepper->Setup(settings->SubstepDeltaTime, settings->MaxSubsteps); + Stepper->Setup(settings.SubstepDeltaTime, settings.MaxSubsteps); } else { diff --git a/Source/Engine/Physics/Physics.h b/Source/Engine/Physics/Physics.h index bdc1326d3..05b64cb0f 100644 --- a/Source/Engine/Physics/Physics.h +++ b/Source/Engine/Physics/Physics.h @@ -155,6 +155,11 @@ public: /// API_PROPERTY() static void SetBounceThresholdVelocity(float value); + /// + /// The collision layers masks. Used to define layer-based collision detection. + /// + static uint32 LayerMasks[32]; + public: /// diff --git a/Source/Engine/Physics/PhysicsSettings.cpp b/Source/Engine/Physics/PhysicsSettings.cpp deleted file mode 100644 index be8bff331..000000000 --- a/Source/Engine/Physics/PhysicsSettings.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#include "PhysicsSettings.h" -#include "Engine/Serialization/Serialization.h" -#include "Physics.h" - -void PhysicsSettings::Apply() -{ - // Set simulation parameters - Physics::SetGravity(DefaultGravity); - Physics::SetBounceThresholdVelocity(BounceThresholdVelocity); - Physics::SetEnableCCD(!DisableCCD); - - // TODO: setting eADAPTIVE_FORCE requires PxScene setup (physx docs: This flag is not mutable, and must be set in PxSceneDesc at scene creation.) - - // Update all shapes - - // TODO: update all shapes filter data - // TODO: update all shapes flags - - /* - { - get all actors and then: - - const PxU32 numShapes = actor->getNbShapes(); - PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*numShapes); - actor->getShapes(shapes, numShapes); - for (PxU32 i = 0; i < numShapes; i++) - { - .. - } - SAMPLE_FREE(shapes); - }*/ -} - -void PhysicsSettings::RestoreDefault() -{ - DefaultGravity = PhysicsSettings_DefaultGravity; - TriangleMeshTriangleMinAreaThreshold = PhysicsSettings_TriangleMeshTriangleMinAreaThreshold; - BounceThresholdVelocity = PhysicsSettings_BounceThresholdVelocity; - FrictionCombineMode = PhysicsSettings_FrictionCombineMode; - RestitutionCombineMode = PhysicsSettings_RestitutionCombineMode; - DisableCCD = PhysicsSettings_DisableCCD; - EnableAdaptiveForce = PhysicsSettings_EnableAdaptiveForce; - MaxDeltaTime = PhysicsSettings_MaxDeltaTime; - EnableSubstepping = PhysicsSettings_EnableSubstepping; - SubstepDeltaTime = PhysicsSettings_SubstepDeltaTime; - MaxSubsteps = PhysicsSettings_MaxSubsteps; - QueriesHitTriggers = PhysicsSettings_QueriesHitTriggers; - SupportCookingAtRuntime = PhysicsSettings_SupportCookingAtRuntime; - - for (int32 i = 0; i < 32; i++) - LayerMasks[i] = MAX_uint32; -} - -void PhysicsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) -{ - DESERIALIZE(DefaultGravity); - DESERIALIZE(TriangleMeshTriangleMinAreaThreshold); - DESERIALIZE(BounceThresholdVelocity); - DESERIALIZE(FrictionCombineMode); - DESERIALIZE(RestitutionCombineMode); - DESERIALIZE(DisableCCD); - DESERIALIZE(EnableAdaptiveForce); - DESERIALIZE(MaxDeltaTime); - DESERIALIZE(EnableSubstepping); - DESERIALIZE(SubstepDeltaTime); - DESERIALIZE(MaxSubsteps); - DESERIALIZE(QueriesHitTriggers); - DESERIALIZE(SupportCookingAtRuntime); - - const auto layers = stream.FindMember("LayerMasks"); - if (layers != stream.MemberEnd()) - { - auto& layersArray = layers->value; - ASSERT(layersArray.IsArray()); - for (uint32 i = 0; i < layersArray.Size() && i < 32; i++) - { - LayerMasks[i] = layersArray[i].GetUint(); - } - } -} diff --git a/Source/Engine/Physics/PhysicsSettings.h b/Source/Engine/Physics/PhysicsSettings.h index 3bf19bcb8..214c86558 100644 --- a/Source/Engine/Physics/PhysicsSettings.h +++ b/Source/Engine/Physics/PhysicsSettings.h @@ -6,105 +6,91 @@ #include "Engine/Core/Math/Vector3.h" #include "Types.h" -// Default values for the physics settings - -#define PhysicsSettings_DefaultGravity Vector3(0, -981.0f, 0) -#define PhysicsSettings_TriangleMeshTriangleMinAreaThreshold (5.0f) -#define PhysicsSettings_BounceThresholdVelocity (200.0f) -#define PhysicsSettings_FrictionCombineMode PhysicsCombineMode::Average -#define PhysicsSettings_RestitutionCombineMode PhysicsCombineMode::Average -#define PhysicsSettings_DisableCCD false -#define PhysicsSettings_EnableAdaptiveForce false -#define PhysicsSettings_MaxDeltaTime (1.0f / 10.0f) -#define PhysicsSettings_EnableSubstepping false -#define PhysicsSettings_SubstepDeltaTime (1.0f / 120.0f) -#define PhysicsSettings_MaxSubsteps 5 -#define PhysicsSettings_QueriesHitTriggers true -#define PhysicsSettings_SupportCookingAtRuntime false - /// /// Physics simulation settings container. /// -/// -class PhysicsSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class FLAXENGINE_API PhysicsSettings : public SettingsBase { -public: - - /// - /// Initializes a new instance of the class. - /// - PhysicsSettings() - { - for (int32 i = 0; i < 32; i++) - LayerMasks[i] = MAX_uint32; - } - +DECLARE_SCRIPTING_TYPE_MINIMAL(PhysicsSettings); public: /// /// The default gravity force value (in cm^2/s). /// - Vector3 DefaultGravity = PhysicsSettings_DefaultGravity; - - /// - /// Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable. - /// - float TriangleMeshTriangleMinAreaThreshold = PhysicsSettings_TriangleMeshTriangleMinAreaThreshold; - - /// - /// Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity - /// - float BounceThresholdVelocity = PhysicsSettings_BounceThresholdVelocity; - - /// - /// Default friction combine mode, controls how friction is computed for multiple materials. - /// - PhysicsCombineMode FrictionCombineMode = PhysicsSettings_FrictionCombineMode; - - /// - /// Default restitution combine mode, controls how restitution is computed for multiple materials. - /// - PhysicsCombineMode RestitutionCombineMode = PhysicsSettings_RestitutionCombineMode; - - /// - /// If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for physx to check it internally. - /// - bool DisableCCD = PhysicsSettings_DisableCCD; - - /// - /// Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts. - /// - bool EnableAdaptiveForce = PhysicsSettings_EnableAdaptiveForce; - - /// - /// The maximum allowed delta time (in seconds) for the physics simulation step. - /// - float MaxDeltaTime = PhysicsSettings_MaxDeltaTime; - - /// - /// Whether to substep the physics simulation. - /// - bool EnableSubstepping = PhysicsSettings_EnableSubstepping; - - /// - /// Delta time (in seconds) for an individual simulation substep. - /// - float SubstepDeltaTime = PhysicsSettings_SubstepDeltaTime; - - /// - /// The maximum number of substeps for physics simulation. - /// - int32 MaxSubsteps = PhysicsSettings_MaxSubsteps; + API_FIELD(Attributes="EditorOrder(0), DefaultValue(typeof(Vector3), \"0,-981.0,0\"), EditorDisplay(\"Simulation\")") + Vector3 DefaultGravity = Vector3(0, -981.0f, 0); /// /// If enabled, any Raycast or other scene query that intersects with a Collider marked as a Trigger will returns with a hit. Individual raycasts can override this behavior. /// - bool QueriesHitTriggers = PhysicsSettings_QueriesHitTriggers; + API_FIELD(Attributes="EditorOrder(10), DefaultValue(true), EditorDisplay(\"Framerate\")") + bool QueriesHitTriggers = true; + + /// + /// Triangles from triangle meshes (CSG) with an area less than or equal to this value will be removed from physics collision data. Set to less than or equal 0 to disable. + /// + API_FIELD(Attributes="EditorOrder(20), DefaultValue(5.0f), EditorDisplay(\"Simulation\")") + float TriangleMeshTriangleMinAreaThreshold = 5.0f; + + /// + /// Minimum relative velocity required for an object to bounce. A typical value for simulation stability is about 0.2 * gravity + /// + API_FIELD(Attributes="EditorOrder(30), DefaultValue(200.0f), EditorDisplay(\"Simulation\")") + float BounceThresholdVelocity = 200.0f; + + /// + /// Default friction combine mode, controls how friction is computed for multiple materials. + /// + API_FIELD(Attributes="EditorOrder(40), DefaultValue(PhysicsCombineMode.Average), EditorDisplay(\"Simulation\")") + PhysicsCombineMode FrictionCombineMode = PhysicsCombineMode::Average; + + /// + /// Default restitution combine mode, controls how restitution is computed for multiple materials. + /// + API_FIELD(Attributes="EditorOrder(40), DefaultValue(PhysicsCombineMode.Average), EditorDisplay(\"Simulation\")") + PhysicsCombineMode RestitutionCombineMode = PhysicsCombineMode::Average; + + /// + /// If true CCD will be ignored. This is an optimization when CCD is never used which removes the need for physx to check it internally. + /// + API_FIELD(Attributes="EditorOrder(70), DefaultValue(false), EditorDisplay(\"Simulation\")") + bool DisableCCD = false; + + /// + /// Enables adaptive forces to accelerate convergence of the solver. Can improve physics simulation performance but lead to artifacts. + /// + API_FIELD(Attributes="EditorOrder(80), DefaultValue(false), EditorDisplay(\"Simulation\")") + bool EnableAdaptiveForce = false; + + /// + /// The maximum allowed delta time (in seconds) for the physics simulation step. + /// + API_FIELD(Attributes="EditorOrder(1000), DefaultValue(1.0f / 10.0f), EditorDisplay(\"Framerate\")") + float MaxDeltaTime = 1.0f / 10.0f; + + /// + /// Whether to substep the physics simulation. + /// + API_FIELD(Attributes="EditorOrder(1005), DefaultValue(false), EditorDisplay(\"Framerate\")") + bool EnableSubstepping = false; + + /// + /// Delta time (in seconds) for an individual simulation substep. + /// + API_FIELD(Attributes="EditorOrder(1010), DefaultValue(1.0f / 120.0f), EditorDisplay(\"Framerate\")") + float SubstepDeltaTime = 1.0f / 120.0f; + + /// + /// The maximum number of substeps for physics simulation. + /// + API_FIELD(Attributes="EditorOrder(1020), DefaultValue(5), EditorDisplay(\"Framerate\")") + int32 MaxSubsteps = 5; /// /// Enables support for cooking physical collision shapes geometry at runtime. Use it to enable generating runtime terrain collision or convex mesh colliders. /// - bool SupportCookingAtRuntime = PhysicsSettings_SupportCookingAtRuntime; + API_FIELD(Attributes="EditorOrder(1100), DefaultValue(false), EditorDisplay(\"Other\")") + bool SupportCookingAtRuntime = false; public: @@ -115,8 +101,17 @@ public: public: + /// + /// Initializes a new instance of the class. + /// + PhysicsSettings(); + + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static PhysicsSettings* Get(); + // [SettingsBase] void Apply() override; - void RestoreDefault() override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override; }; diff --git a/Source/Engine/Physics/Utilities.h b/Source/Engine/Physics/Utilities.h index 11914277f..34ee7f66d 100644 --- a/Source/Engine/Physics/Utilities.h +++ b/Source/Engine/Physics/Utilities.h @@ -14,9 +14,6 @@ #include #include #include -#include "PhysicsSettings.h" - -//////////////////////////// Conversion between PhysX and Flax types inline PxVec2& C2P(const Vector2& v) { @@ -43,8 +40,6 @@ inline PxBounds3& C2P(const BoundingBox& v) return *(PxBounds3*)&v; } -//////////////////////////// - inline Vector2& P2C(const PxVec2& v) { return *(Vector2*)&v; @@ -79,29 +74,4 @@ inline Vector3 P2C(const PxExtendedVec3& v) #endif } -//////////////////////////// - -inline PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled) -{ -#if WITH_PVD - PxShapeFlags flags = PxShapeFlag::eVISUALIZATION; -#else - PxShapeFlags flags = static_cast(0); -#endif - - if (isEnabled) - { - if (isTrigger) - { - flags |= PxShapeFlag::eTRIGGER_SHAPE; - if (PhysicsSettings::Instance()->QueriesHitTriggers) - flags |= PxShapeFlag::eSCENE_QUERY_SHAPE; - } - else - { - flags = PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eSCENE_QUERY_SHAPE; - } - } - - return flags; -} +extern PxShapeFlags GetShapeFlags(bool isTrigger, bool isEnabled); diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index 1c761259b..8805b2180 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -9,41 +9,37 @@ /// /// Android platform settings. /// -/// -class AndroidPlatformSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AndroidPlatformSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(AndroidPlatformSettings); public: /// /// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}. /// - String PackageName; + API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"General\")") + String PackageName = TEXT("com.${COMPANY_NAME}.${PROJECT_NAME}"); /// /// The application permissions list (eg. android.media.action.IMAGE_CAPTURE). Added to the generated manifest file. /// + API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"General\")") Array Permissions; /// /// Custom icon texture (asset id) to use for the application (overrides the default one). /// + API_FIELD(Attributes="EditorOrder(1030), AssetReference(typeof(Texture)), EditorDisplay(\"Other\")") Guid OverrideIcon; public: - AndroidPlatformSettings() - { - RestoreDefault(); - } + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static AndroidPlatformSettings* Get(); // [SettingsBase] - void RestoreDefault() final override - { - PackageName = TEXT("com.${COMPANY_NAME}.${PROJECT_NAME}"); - Permissions.Clear(); - OverrideIcon = Guid::Empty; - } - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { DESERIALIZE(PackageName); diff --git a/Source/Engine/Platform/Linux/LinuxPlatformSettings.h b/Source/Engine/Platform/Linux/LinuxPlatformSettings.h index 4c602224a..7da0ec406 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatformSettings.h +++ b/Source/Engine/Platform/Linux/LinuxPlatformSettings.h @@ -9,66 +9,67 @@ /// /// Linux platform settings. /// -/// -class LinuxPlatformSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API LinuxPlatformSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(LinuxPlatformSettings); public: /// /// The default game window mode. /// + API_FIELD(Attributes="EditorOrder(10), DefaultValue(GameWindowMode.Windowed), EditorDisplay(\"Window\")") GameWindowMode WindowMode = GameWindowMode::Windowed; /// /// The default game window width (in pixels). /// + API_FIELD(Attributes="EditorOrder(20), DefaultValue(1280), EditorDisplay(\"Window\")") int32 ScreenWidth = 1280; /// /// The default game window height (in pixels). /// + API_FIELD(Attributes="EditorOrder(30), DefaultValue(720), EditorDisplay(\"Window\")") int32 ScreenHeight = 720; - /// - /// Enables game running when application window loses focus. - /// - bool RunInBackground = false; - /// /// Enables resizing the game window by the user. /// + API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Window\")") bool ResizableWindow = false; + /// + /// Enables game running when application window loses focus. + /// + API_FIELD(Attributes="EditorOrder(1010), DefaultValue(false), EditorDisplay(\"Other\", \"Run In Background\")") + bool RunInBackground = false; + /// /// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once. /// + API_FIELD(Attributes="EditorOrder(1020), DefaultValue(false), EditorDisplay(\"Other\")") bool ForceSingleInstance = false; /// /// Custom icon texture (asset id) to use for the application (overrides the default one). /// - Guid OverrideIcon = Guid::Empty; + API_FIELD(Attributes="EditorOrder(1030), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.AssetRefEditor\"), AssetReference(typeof(Texture)), EditorDisplay(\"Other\")") + Guid OverrideIcon; /// /// Enables support for Vulkan. Disabling it reduces compiled shaders count. /// + API_FIELD(Attributes="EditorOrder(2000), DefaultValue(true), EditorDisplay(\"Graphics\")") bool SupportVulkan = true; public: - // [SettingsBase] - void RestoreDefault() final override - { - WindowMode = GameWindowMode::Windowed; - ScreenWidth = 1280; - ScreenHeight = 720; - RunInBackground = false; - ResizableWindow = false; - ForceSingleInstance = false; - OverrideIcon = Guid::Empty; - SupportVulkan = true; - } + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static LinuxPlatformSettings* Get(); + // [SettingsBase] void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { DESERIALIZE(WindowMode); diff --git a/Source/Engine/Platform/Platform.Build.cs b/Source/Engine/Platform/Platform.Build.cs index b7a2baa97..72a706685 100644 --- a/Source/Engine/Platform/Platform.Build.cs +++ b/Source/Engine/Platform/Platform.Build.cs @@ -62,5 +62,15 @@ public class Platform : EngineModule break; default: throw new InvalidPlatformException(options.Platform.Target); } + if (options.Target.IsEditor) + { + // Include platform settings headers + options.SourceFiles.Add(Path.Combine(FolderPath, "Windows", "WindowsPlatformSettings.h")); + options.SourceFiles.Add(Path.Combine(FolderPath, "UWP", "UWPPlatformSettings.h")); + options.SourceFiles.Add(Path.Combine(FolderPath, "Linux", "LinuxPlatformSettings.h")); + options.SourceFiles.Add(Path.Combine(FolderPath, "Android", "AndroidPlatformSettings.h")); + AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform", "XboxScarlettPlatformSettings.h")); + AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS4", "Engine", "Platform", "PS4PlatformSettings.h")); + } } } diff --git a/Source/Engine/Platform/UWP/UWPPlatformSettings.h b/Source/Engine/Platform/UWP/UWPPlatformSettings.h index 706cad89c..e600cead7 100644 --- a/Source/Engine/Platform/UWP/UWPPlatformSettings.h +++ b/Source/Engine/Platform/UWP/UWPPlatformSettings.h @@ -9,15 +9,15 @@ /// /// Universal Windows Platform settings. /// -/// -class UWPPlatformSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API UWPPlatformSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(UWPPlatformSettings); public: /// /// The preferred launch windowing mode. /// - enum class WindowMode + API_ENUM() enum class WindowMode { /// /// The full screen mode @@ -33,7 +33,7 @@ public: /// /// The display orientation modes. Can be combined as flags. /// - enum class DisplayOrientations + API_ENUM(Attributes="Flags") enum class DisplayOrientations { /// /// The none. @@ -71,40 +71,41 @@ public: /// /// The preferred launch windowing mode. Always fullscreen on Xbox. /// + API_FIELD(Attributes="EditorOrder(10), DefaultValue(WindowMode.FullScreen), EditorDisplay(\"Window\")") WindowMode PreferredLaunchWindowingMode = WindowMode::FullScreen; /// /// The display orientation modes. Can be combined as flags. /// + API_FIELD(Attributes="EditorOrder(20), DefaultValue(DisplayOrientations.All), EditorDisplay(\"Window\")") DisplayOrientations AutoRotationPreferences = DisplayOrientations::All; /// /// The location of the package certificate (relative to the project). /// + API_FIELD(Attributes="EditorOrder(1010), DefaultValue(\"\"), EditorDisplay(\"Other\")") String CertificateLocation; /// /// Enables support for DirectX 11. Disabling it reduces compiled shaders count. /// + API_FIELD(Attributes="EditorOrder(2000), DefaultValue(true), EditorDisplay(\"Graphics\", \"Support DirectX 11\")") bool SupportDX11 = true; /// /// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count. /// + API_FIELD(Attributes="EditorOrder(2010), DefaultValue(false), EditorDisplay(\"Graphics\", \"Support DirectX 10\")") bool SupportDX10 = false; public: - // [SettingsBase] - void RestoreDefault() final override - { - PreferredLaunchWindowingMode = WindowMode::FullScreen; - AutoRotationPreferences = DisplayOrientations::All; - CertificateLocation.Clear(); - SupportDX11 = true; - SupportDX10 = false; - } + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static UWPPlatformSettings* Get(); + // [SettingsBase] void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { DESERIALIZE(PreferredLaunchWindowingMode); diff --git a/Source/Engine/Platform/Windows/WindowsPlatformSettings.h b/Source/Engine/Platform/Windows/WindowsPlatformSettings.h index 3da8dc2b8..48a39444a 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatformSettings.h +++ b/Source/Engine/Platform/Windows/WindowsPlatformSettings.h @@ -9,84 +9,85 @@ /// /// Windows platform settings. /// -/// -class WindowsPlatformSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API WindowsPlatformSettings : public SettingsBase { +DECLARE_SCRIPTING_TYPE_MINIMAL(WindowsPlatformSettings); public: /// /// The default game window mode. /// + API_FIELD(Attributes="EditorOrder(10), DefaultValue(GameWindowMode.Windowed), EditorDisplay(\"Window\")") GameWindowMode WindowMode = GameWindowMode::Windowed; /// /// The default game window width (in pixels). /// + API_FIELD(Attributes="EditorOrder(20), DefaultValue(1280), EditorDisplay(\"Window\")") int32 ScreenWidth = 1280; /// /// The default game window height (in pixels). /// + API_FIELD(Attributes="EditorOrder(30), DefaultValue(720), EditorDisplay(\"Window\")") int32 ScreenHeight = 720; - /// - /// Enables game running when application window loses focus. - /// - bool RunInBackground = false; - /// /// Enables resizing the game window by the user. /// + API_FIELD(Attributes="EditorOrder(40), DefaultValue(false), EditorDisplay(\"Window\")") bool ResizableWindow = false; + /// + /// Enables game running when application window loses focus. + /// + API_FIELD(Attributes="EditorOrder(1010), DefaultValue(false), EditorDisplay(\"Other\", \"Run In Background\")") + bool RunInBackground = false; + /// /// Limits maximum amount of concurrent game instances running to one, otherwise user may launch application more than once. /// + API_FIELD(Attributes="EditorOrder(1020), DefaultValue(false), EditorDisplay(\"Other\")") bool ForceSingleInstance = false; /// /// Custom icon texture (asset id) to use for the application (overrides the default one). /// + API_FIELD(Attributes="EditorOrder(1030), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.AssetRefEditor\"), AssetReference(typeof(Texture)), EditorDisplay(\"Other\")") Guid OverrideIcon; /// /// Enables support for DirectX 12. Disabling it reduces compiled shaders count. /// + API_FIELD(Attributes="EditorOrder(2000), DefaultValue(false), EditorDisplay(\"Graphics\", \"Support DirectX 12\")") bool SupportDX12 = false; /// /// Enables support for DirectX 11. Disabling it reduces compiled shaders count. /// + API_FIELD(Attributes="EditorOrder(2010), DefaultValue(true), EditorDisplay(\"Graphics\", \"Support DirectX 11\")") bool SupportDX11 = true; /// /// Enables support for DirectX 10 and DirectX 10.1. Disabling it reduces compiled shaders count. /// + API_FIELD(Attributes="EditorOrder(2020), DefaultValue(false), EditorDisplay(\"Graphics\", \"Support DirectX 10\")") bool SupportDX10 = false; /// /// Enables support for Vulkan. Disabling it reduces compiled shaders count. /// + API_FIELD(Attributes="EditorOrder(2030), DefaultValue(false), EditorDisplay(\"Graphics\")") bool SupportVulkan = false; public: - // [SettingsBase] - void RestoreDefault() final override - { - WindowMode = GameWindowMode::Windowed; - ScreenWidth = 1280; - ScreenHeight = 720; - RunInBackground = false; - ResizableWindow = false; - ForceSingleInstance = false; - OverrideIcon = Guid::Empty; - SupportDX12 = false; - SupportDX11 = true; - SupportDX10 = false; - SupportVulkan = false; - } + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static WindowsPlatformSettings* Get(); + // [SettingsBase] void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override { DESERIALIZE(WindowMode); diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index d15dd4cfa..9dabe1af0 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -69,7 +69,7 @@ void Terrain::UpdateLayerBits() filterData.word0 = GetLayerMask(); // Own layer mask - filterData.word1 = PhysicsSettings::Instance()->LayerMasks[GetLayer()]; + filterData.word1 = Physics::LayerMasks[GetLayer()]; // Update the shapes layer bits for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 966737e95..2c3e1c837 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -66,6 +66,14 @@ namespace Flax.Build.Bindings return null; } + // Special case for Engine TEXT macro + if (value.Contains("TEXT(\"")) + return value.Replace("TEXT(\"", "(\""); + + // Special case for value constructors + if (value.Contains('(') && value.Contains(')')) + return "new " + value; + // Convert from C++ to C# switch (value) { @@ -611,6 +619,12 @@ namespace Flax.Build.Bindings contents.Append(returnValueType).Append(' ').Append(fieldInfo.Name); if (!useUnmanaged) { + if (fieldInfo.DefaultValue != null) + { + var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, classInfo); + if (!string.IsNullOrEmpty(defaultValue)) + contents.Append(" = ").Append(defaultValue); + } contents.AppendLine(";"); continue; } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index b9096b3bb..c8a6a55df 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1310,7 +1310,7 @@ namespace Flax.Build.Bindings contents.AppendLine(" }").AppendLine(); - if (!useScripting) + if (!useScripting && !classInfo.IsAbstract) { // Constructor contents.AppendLine(" static void Ctor(void* ptr)"); @@ -1350,7 +1350,14 @@ namespace Flax.Build.Bindings } else { - contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor, nullptr"); + if (classInfo.IsAbstract) + contents.Append("nullptr, nullptr, "); + else + contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor, "); + if (classInfo.BaseType != null) + contents.Append($"&{classInfo.BaseType}::TypeInitializer"); + else + contents.Append("nullptr"); } contents.Append(", ").Append(interfacesTable); contents.Append(");"); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index da337e746..916916995 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -1038,8 +1038,7 @@ namespace Flax.Build.Bindings var token = context.Tokenizer.ExpectAnyTokens(new[] { TokenType.SemiColon, TokenType.Equal, TokenType.LeftBracket, TokenType.Colon }); if (token.Type == TokenType.Equal) { - //context.Tokenizer.SkipUntil(TokenType.SemiColon, out var defaultValue); - context.Tokenizer.SkipUntil(TokenType.SemiColon); + context.Tokenizer.SkipUntil(TokenType.SemiColon, out desc.DefaultValue); } else if (token.Type == TokenType.LeftBracket) { diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index ea8cfb559..419820f56 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -87,6 +87,8 @@ namespace Flax.Build.Bindings if (headerFiles.Count == 0) return moduleInfo; + // TODO: use aynsc tasks or thread pool to load and parse multiple header files at once using multi-threading + // Find and load files with API tags string[] headerFilesContents = null; //using (new ProfileEventScope("LoadHeaderFiles")) @@ -94,9 +96,8 @@ namespace Flax.Build.Bindings var anyApi = false; for (int i = 0; i < headerFiles.Count; i++) { - // Skip scripting types definitions file - if (headerFiles[i].Replace('\\', '/').EndsWith("Engine/Core/Config.h", StringComparison.Ordinal) || - headerFiles[i].EndsWith("EditorContextAPI.h", StringComparison.Ordinal)) + // Skip scripting tags definitions file + if (headerFiles[i].Replace('\\', '/').EndsWith("Engine/Core/Config.h", StringComparison.Ordinal)) continue; // Check if file contains any valid API tag @@ -320,24 +321,36 @@ namespace Flax.Build.Bindings token = tokenizer.NextToken(true); while (token.Type != TokenType.Newline) { - condition += token.Value; + var tokenValue = token.Value.Trim(); + if (tokenValue.Length == 0) + { + token = tokenizer.NextToken(true); + continue; + } + + // Very simple defines processing + tokenValue = ReplacePreProcessorDefines(tokenValue, context.PreprocessorDefines.Keys); + tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PublicDefinitions); + tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PrivateDefinitions); + tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.CompileEnv.PreprocessorDefinitions); + tokenValue = tokenValue.Replace("false", "0"); + tokenValue = tokenValue.Replace("true", "1"); + tokenValue = tokenValue.Replace("||", "|"); + if (tokenValue.Length != 0 && tokenValue != "1" && tokenValue != "0" && tokenValue != "|") + tokenValue = "0"; + + condition += tokenValue; token = tokenizer.NextToken(true); } - // Replace contents with defines - condition = condition.Trim(); - condition = ReplacePreProcessorDefines(condition, context.PreprocessorDefines.Keys); - condition = ReplacePreProcessorDefines(condition, moduleOptions.PublicDefinitions); - condition = ReplacePreProcessorDefines(condition, moduleOptions.PrivateDefinitions); - condition = ReplacePreProcessorDefines(condition, moduleOptions.CompileEnv.PreprocessorDefinitions); - condition = condition.Replace("false", "0"); - condition = condition.Replace("true", "1"); + // Filter condition + condition = condition.Replace("1|1", "1"); + condition = condition.Replace("1|0", "1"); + condition = condition.Replace("0|1", "1"); - // Check condition - // TODO: support expressions in preprocessor defines in API headers? + // Skip chunk of code of condition fails if (condition != "1") { - // Skip chunk of code ParsePreprocessorIf(fileInfo, tokenizer, ref token); } @@ -404,7 +417,7 @@ namespace Flax.Build.Bindings { foreach (var define in defines) { - if (text.Contains(define)) + if (string.Equals(text, define, StringComparison.Ordinal)) text = text.Replace(define, "1"); } return text; diff --git a/Source/Tools/Flax.Build/Bindings/FieldInfo.cs b/Source/Tools/Flax.Build/Bindings/FieldInfo.cs index 509c1d6e6..b1488ed09 100644 --- a/Source/Tools/Flax.Build/Bindings/FieldInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FieldInfo.cs @@ -12,6 +12,9 @@ namespace Flax.Build.Bindings public bool NoArray; public FunctionInfo Getter; public FunctionInfo Setter; + public string DefaultValue; + + public bool HasDefaultValue => !string.IsNullOrEmpty(DefaultValue); public override string ToString() { @@ -19,6 +22,8 @@ namespace Flax.Build.Bindings if (IsStatic) result += "static "; result += Type + " " + Name; + if (HasDefaultValue) + result += " = " + DefaultValue; return result; } } diff --git a/Source/Tools/Flax.Build/Build/Module.cs b/Source/Tools/Flax.Build/Build/Module.cs index dae700728..726cbf946 100644 --- a/Source/Tools/Flax.Build/Build/Module.cs +++ b/Source/Tools/Flax.Build/Build/Module.cs @@ -111,5 +111,16 @@ namespace Flax.Build // By default deploy all C++ header files files.AddRange(Directory.GetFiles(FolderPath, "*.h", SearchOption.AllDirectories)); } + + /// + /// Adds the file to the build sources if exists. + /// + /// The options. + /// The source file path. + protected void AddSourceFileIfExists(BuildOptions options, string path) + { + if (File.Exists(path)) + options.SourceFiles.Add(path); + } } } From 199683897aeb016923bfc011a81981d50acec7bb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 5 Jan 2021 14:15:22 +0100 Subject: [PATCH 036/419] Update DirectXShaderCompiler to 1.6 --- .../DirectX/ShaderCompilerDX.cpp | 87 +-- .../Binaries/ThirdParty/x64/dxcompiler.dll | 4 +- .../Binaries/ThirdParty/x64/dxcompiler.lib | 4 +- .../Windows/Binaries/ThirdParty/x64/dxil.dll | 4 +- .../ThirdParty/DirectXShaderCompiler/dxcapi.h | 518 +++++++++++++----- 5 files changed, 448 insertions(+), 169 deletions(-) diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp index a7f2fc01c..1f58dce93 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp @@ -12,10 +12,6 @@ #include #include -#ifndef DXIL_FOURCC -#define DXIL_FOURCC(ch0, ch1, ch2, ch3) ((uint32)(uint8)(ch0) | (uint32)(uint8)(ch1) << 8 | (uint32)(uint8)(ch2) << 16 | (uint32)(uint8)(ch3) << 24) -#endif - /// /// Helper class to include source for DX shaders compiler. /// @@ -76,7 +72,7 @@ public: ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile) : ShaderCompiler(profile) { - IDxcCompiler2* compiler = nullptr; + IDxcCompiler3* compiler = nullptr; IDxcLibrary* library = nullptr; IDxcContainerReflection* containerReflection = nullptr; if (FAILED(DxcCreateInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast(&compiler))) || @@ -216,7 +212,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD // Prepare auto options = _context->Options; - auto compiler = (IDxcCompiler2*)_compiler; + auto compiler = (IDxcCompiler3*)_compiler; auto library = (IDxcLibrary*)_library; auto containerReflection = (IDxcContainerReflection*)_containerReflection; auto type = meta.GetStage(); @@ -248,18 +244,27 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD ComPtr textBlob; if (FAILED(library->CreateBlobWithEncodingFromPinned((LPBYTE)options->Source, options->SourceLength, CP_UTF8, &textBlob))) return true; + DxcBuffer textBuffer; + textBuffer.Ptr = textBlob->GetBufferPointer(); + textBuffer.Size = textBlob->GetBufferSize(); + textBuffer.Encoding = DXC_CP_ACP; const StringAsUTF16<> entryPoint(meta.Name.Get(), meta.Name.Length()); Array definesStrings; - Array defines; - Array> args; + Array> args; if (_context->Options->NoOptimize) - args.Add(TEXT("-Od")); + args.Add(DXC_ARG_SKIP_OPTIMIZATIONS); else - args.Add(TEXT("-O3")); + args.Add(DXC_ARG_OPTIMIZATION_LEVEL3); if (_context->Options->TreatWarningsAsErrors) - args.Add(TEXT("-WX")); + args.Add(DXC_ARG_WARNINGS_ARE_ERRORS); if (_context->Options->GenerateDebugData) - args.Add(TEXT("-Zi")); + args.Add(DXC_ARG_DEBUG); + args.Add(TEXT("-T")); + args.Add(targetProfile); + args.Add(TEXT("-E")); + args.Add(entryPoint.Get()); + args.Add(options->TargetName.Get()); + Array> argsFull; // Compile all shader function permutations for (int32 permutationIndex = 0; permutationIndex < meta.Permutations.Count(); permutationIndex++) @@ -278,33 +283,37 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD // Convert defines from char* to Char* const int32 macrosCount = _macros.Count() - 1; - definesStrings.Resize(macrosCount * 2); - defines.Resize(macrosCount); + definesStrings.Resize(macrosCount); for (int32 i = 0; i < macrosCount; i++) { auto& macro = _macros[i]; - auto& define = defines[i]; - auto& defineName = definesStrings[i * 2]; - auto& defineValue = definesStrings[i * 2 + 1]; - defineName = macro.Name; - defineValue = macro.Definition; - define.Name = defineName.GetText(); - define.Value = defineValue.Get(); + auto& define = definesStrings[i]; + define = macro.Name; + if (macro.Definition && *macro.Definition) + { + define += TEXT("="); + define += macro.Definition; + } + } + + // Build full list of arguments + argsFull.Clear(); + for (auto& e : args) + argsFull.Add(e); + for (auto& d : definesStrings) + { + argsFull.Add(TEXT("-D")); + argsFull.Add(*d); } // Compile - ComPtr results; + ComPtr results; HRESULT result = compiler->Compile( - textBlob.Get(), - options->TargetName.Get(), - entryPoint.Get(), - targetProfile, - (LPCWSTR*)args.Get(), - args.Count(), - defines.Get(), - defines.Count(), + &textBuffer, + (LPCWSTR*)argsFull.Get(), + argsFull.Count(), &include, - &results); + IID_PPV_ARGS(&results)); if (SUCCEEDED(result) && results) results->GetStatus(&result); if (FAILED(result)) @@ -338,11 +347,19 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD // Generate debug information { // Disassemble compiled shader - ComPtr disassembly; - if (FAILED(compiler->Disassemble(shaderBuffer, &disassembly))) + ComPtr disassembly; + DxcBuffer shaderDxcBuffer; + shaderDxcBuffer.Ptr = shaderBuffer->GetBufferPointer(); + shaderDxcBuffer.Size = shaderBuffer->GetBufferSize(); + shaderDxcBuffer.Encoding = DXC_CP_ACP; + if (FAILED(compiler->Disassemble(&shaderDxcBuffer, IID_PPV_ARGS(&disassembly)))) + return true; + ComPtr disassemblyBlob; + ComPtr disassemblyPath; + if (FAILED(disassembly->GetOutput(DXC_OUT_DISASSEMBLY, IID_PPV_ARGS(disassemblyBlob.GetAddressOf()), disassemblyPath.GetAddressOf()))) return true; ComPtr disassemblyUtf8; - if (FAILED(library->GetBlobAsUtf8(disassembly, &disassemblyUtf8))) + if (FAILED(library->GetBlobAsUtf8(disassemblyBlob, &disassemblyUtf8))) return true; // Extract debug info @@ -356,7 +373,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD LOG(Error, "IDxcContainerReflection::Load failed."); return true; } - const uint32 dxilPartKind = DXIL_FOURCC('D', 'X', 'I', 'L'); + const uint32 dxilPartKind = DXC_PART_DXIL; uint32 dxilPartIndex = ~0u; if (FAILED(containerReflection->FindFirstPartKind(dxilPartKind, &dxilPartIndex))) { diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll index bf7ec271d..0f4d9ef52 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5c8d027a25bc36830b6241b0d2031a74973a00d96b3db67745e45fcfbf1d910 -size 15377800 +oid sha256:b7490aa95b516e002b30d9cf7034f4fc796d087660d028cb076fdf15ac9efe5e +size 18515848 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib index 21852672f..748275336 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7bf3b219942ae175796d79ca63f83efabeca54fc6eab4f67703bc4946df0b2b -size 2344 +oid sha256:02d6f118c010e5a1a65c30d622871525d92e8f78a80e322d3a2d855bb502d0e2 +size 2002 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll index 9afd5f8bc..411d5e15f 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17df4ea0a53a18dcb80c924217a54d617c1b6166e7677501a8decf86d08b9de7 -size 1119176 +oid sha256:5c9bbaa764ec71e5087b13767a20086ccebc6127a46473a1076977b28bbc24c8 +size 1867776 diff --git a/Source/ThirdParty/DirectXShaderCompiler/dxcapi.h b/Source/ThirdParty/DirectXShaderCompiler/dxcapi.h index 342c2e782..4e4297e50 100644 --- a/Source/ThirdParty/DirectXShaderCompiler/dxcapi.h +++ b/Source/ThirdParty/DirectXShaderCompiler/dxcapi.h @@ -35,23 +35,6 @@ struct IMalloc; struct IDxcIncludeHandler; -/// -/// Creates a single uninitialized object of the class associated with a specified CLSID. -/// -/// -/// The CLSID associated with the data and code that will be used to create the object. -/// -/// -/// A reference to the identifier of the interface to be used to communicate -/// with the object. -/// -/// -/// Address of pointer variable that receives the interface pointer requested -/// in riid. Upon successful return, *ppv contains the requested interface -/// pointer. Upon failure, *ppv contains NULL. -/// -/// While this function is similar to CoCreateInstance, there is no COM involvement. -/// typedef HRESULT (__stdcall *DxcCreateInstanceProc)( _In_ REFCLSID rclsid, _In_ REFIID riid, @@ -102,6 +85,56 @@ DXC_API_IMPORT HRESULT __stdcall DxcCreateInstance2( _Out_ LPVOID* ppv ); +// For convenience, equivalent definitions to CP_UTF8 and CP_UTF16. +#define DXC_CP_UTF8 65001 +#define DXC_CP_UTF16 1200 +// Use DXC_CP_ACP for: Binary; ANSI Text; Autodetect UTF with BOM +#define DXC_CP_ACP 0 + +// This flag indicates that the shader hash was computed taking into account source information (-Zss) +#define DXC_HASHFLAG_INCLUDES_SOURCE 1 + +// Hash digest type for ShaderHash +typedef struct DxcShaderHash { + UINT32 Flags; // DXC_HASHFLAG_* + BYTE HashDigest[16]; +} DxcShaderHash; + +#define DXC_FOURCC(ch0, ch1, ch2, ch3) ( \ + (UINT32)(UINT8)(ch0) | (UINT32)(UINT8)(ch1) << 8 | \ + (UINT32)(UINT8)(ch2) << 16 | (UINT32)(UINT8)(ch3) << 24 \ + ) +#define DXC_PART_PDB DXC_FOURCC('I', 'L', 'D', 'B') +#define DXC_PART_PDB_NAME DXC_FOURCC('I', 'L', 'D', 'N') +#define DXC_PART_PRIVATE_DATA DXC_FOURCC('P', 'R', 'I', 'V') +#define DXC_PART_ROOT_SIGNATURE DXC_FOURCC('R', 'T', 'S', '0') +#define DXC_PART_DXIL DXC_FOURCC('D', 'X', 'I', 'L') +#define DXC_PART_REFLECTION_DATA DXC_FOURCC('S', 'T', 'A', 'T') +#define DXC_PART_SHADER_HASH DXC_FOURCC('H', 'A', 'S', 'H') +#define DXC_PART_INPUT_SIGNATURE DXC_FOURCC('I', 'S', 'G', '1') +#define DXC_PART_OUTPUT_SIGNATURE DXC_FOURCC('O', 'S', 'G', '1') +#define DXC_PART_PATCH_CONSTANT_SIGNATURE DXC_FOURCC('P', 'S', 'G', '1') + +// Some option arguments are defined here for continuity with D3DCompile interface +#define DXC_ARG_DEBUG L"-Zi" +#define DXC_ARG_SKIP_VALIDATION L"-Vd" +#define DXC_ARG_SKIP_OPTIMIZATIONS L"-Od" +#define DXC_ARG_PACK_MATRIX_ROW_MAJOR L"-Zpr" +#define DXC_ARG_PACK_MATRIX_COLUMN_MAJOR L"-Zpc" +#define DXC_ARG_AVOID_FLOW_CONTROL L"-Gfa" +#define DXC_ARG_PREFER_FLOW_CONTROL L"-Gfp" +#define DXC_ARG_ENABLE_STRICTNESS L"-Ges" +#define DXC_ARG_ENABLE_BACKWARDS_COMPATIBILITY L"-Gec" +#define DXC_ARG_IEEE_STRICTNESS L"-Gis" +#define DXC_ARG_OPTIMIZATION_LEVEL0 L"-O0" +#define DXC_ARG_OPTIMIZATION_LEVEL1 L"-O1" +#define DXC_ARG_OPTIMIZATION_LEVEL2 L"-O2" +#define DXC_ARG_OPTIMIZATION_LEVEL3 L"-O3" +#define DXC_ARG_WARNINGS_ARE_ERRORS L"-WX" +#define DXC_ARG_RESOURCES_MAY_ALIAS L"-res_may_alias" +#define DXC_ARG_ALL_RESOURCES_BOUND L"-all_resources_bound" +#define DXC_ARG_DEBUG_NAME_FOR_SOURCE L"-Zss" +#define DXC_ARG_DEBUG_NAME_FOR_BINARY L"-Zsb" // IDxcBlob is an alias of ID3D10Blob and ID3DBlob struct __declspec(uuid("8BA5FB08-5195-40e2-AC58-0D989C3A0102")) @@ -122,70 +155,144 @@ public: DECLARE_CROSS_PLATFORM_UUIDOF(IDxcBlobEncoding) }; -struct __declspec(uuid("e5204dc7-d18c-4c3c-bdfb-851673980fe7")) -IDxcLibrary : public IUnknown { - virtual HRESULT STDMETHODCALLTYPE SetMalloc(_In_opt_ IMalloc *pMalloc) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateBlobFromBlob( - _In_ IDxcBlob *pBlob, UINT32 offset, UINT32 length, _COM_Outptr_ IDxcBlob **ppResult) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateBlobFromFile( - LPCWSTR pFileName, _In_opt_ UINT32* codePage, - _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingFromPinned( - _In_bytecount_(size) LPCVOID pText, UINT32 size, UINT32 codePage, - _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingOnHeapCopy( - _In_bytecount_(size) LPCVOID pText, UINT32 size, UINT32 codePage, - _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingOnMalloc( - _In_bytecount_(size) LPCVOID pText, IMalloc *pIMalloc, UINT32 size, UINT32 codePage, - _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateIncludeHandler( - _COM_Outptr_ IDxcIncludeHandler **ppResult) = 0; - virtual HRESULT STDMETHODCALLTYPE CreateStreamFromBlobReadOnly( - _In_ IDxcBlob *pBlob, _COM_Outptr_ IStream **ppStream) = 0; - virtual HRESULT STDMETHODCALLTYPE GetBlobAsUtf8( - _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; - virtual HRESULT STDMETHODCALLTYPE GetBlobAsUtf16( - _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; +// Notes on IDxcBlobUtf16 and IDxcBlobUtf8 +// These guarantee null-terminated text and the stated encoding. +// GetBufferSize() will return the size in bytes, including null-terminator +// GetStringLength() will return the length in characters, excluding the null-terminator +// Name strings will use IDxcBlobUtf16, while other string output blobs, +// such as errors/warnings, preprocessed HLSL, or other text will be based +// on the -encoding option. - DECLARE_CROSS_PLATFORM_UUIDOF(IDxcLibrary) +// The API will use this interface for output name strings +struct __declspec(uuid("A3F84EAB-0FAA-497E-A39C-EE6ED60B2D84")) +IDxcBlobUtf16 : public IDxcBlobEncoding { +public: + virtual LPCWSTR STDMETHODCALLTYPE GetStringPointer(void) = 0; + virtual SIZE_T STDMETHODCALLTYPE GetStringLength(void) = 0; + + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcBlobUtf16) }; +struct __declspec(uuid("3DA636C9-BA71-4024-A301-30CBF125305B")) +IDxcBlobUtf8 : public IDxcBlobEncoding { +public: + virtual LPCSTR STDMETHODCALLTYPE GetStringPointer(void) = 0; + virtual SIZE_T STDMETHODCALLTYPE GetStringLength(void) = 0; -struct __declspec(uuid("CEDB484A-D4E9-445A-B991-CA21CA157DC2")) -IDxcOperationResult : public IUnknown { - virtual HRESULT STDMETHODCALLTYPE GetStatus(_Out_ HRESULT *pStatus) = 0; - virtual HRESULT STDMETHODCALLTYPE GetResult(_COM_Outptr_result_maybenull_ IDxcBlob **pResult) = 0; - virtual HRESULT STDMETHODCALLTYPE GetErrorBuffer(_COM_Outptr_result_maybenull_ IDxcBlobEncoding **pErrors) = 0; - - DECLARE_CROSS_PLATFORM_UUIDOF(IDxcOperationResult) + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcBlobUtf8) }; struct __declspec(uuid("7f61fc7d-950d-467f-b3e3-3c02fb49187c")) IDxcIncludeHandler : public IUnknown { virtual HRESULT STDMETHODCALLTYPE LoadSource( - _In_ LPCWSTR pFilename, // Candidate filename. + _In_z_ LPCWSTR pFilename, // Candidate filename. _COM_Outptr_result_maybenull_ IDxcBlob **ppIncludeSource // Resultant source object for included file, nullptr if not found. ) = 0; DECLARE_CROSS_PLATFORM_UUIDOF(IDxcIncludeHandler) }; +// Structure for supplying bytes or text input to Dxc APIs. +// Use Encoding = 0 for non-text bytes, ANSI text, or unknown with BOM. +typedef struct DxcBuffer { + LPCVOID Ptr; + SIZE_T Size; + UINT Encoding; +} DxcText; + struct DxcDefine { LPCWSTR Name; _Maybenull_ LPCWSTR Value; }; +struct __declspec(uuid("73EFFE2A-70DC-45F8-9690-EFF64C02429D")) +IDxcCompilerArgs : public IUnknown { + // Pass GetArguments() and GetCount() to Compile + virtual LPCWSTR* STDMETHODCALLTYPE GetArguments() = 0; + virtual UINT32 STDMETHODCALLTYPE GetCount() = 0; + + // Add additional arguments or defines here, if desired. + virtual HRESULT STDMETHODCALLTYPE AddArguments( + _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments to add + _In_ UINT32 argCount // Number of arguments to add + ) = 0; + virtual HRESULT STDMETHODCALLTYPE AddArgumentsUTF8( + _In_opt_count_(argCount)LPCSTR *pArguments, // Array of pointers to UTF-8 arguments to add + _In_ UINT32 argCount // Number of arguments to add + ) = 0; + virtual HRESULT STDMETHODCALLTYPE AddDefines( + _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines + _In_ UINT32 defineCount // Number of defines + ) = 0; + + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcCompilerArgs) +}; + +////////////////////////// +// Legacy Interfaces +///////////////////////// + +// NOTE: IDxcUtils replaces IDxcLibrary +struct __declspec(uuid("e5204dc7-d18c-4c3c-bdfb-851673980fe7")) +IDxcLibrary : public IUnknown { + virtual HRESULT STDMETHODCALLTYPE SetMalloc(_In_opt_ IMalloc *pMalloc) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateBlobFromBlob( + _In_ IDxcBlob *pBlob, UINT32 offset, UINT32 length, _COM_Outptr_ IDxcBlob **ppResult) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateBlobFromFile( + _In_z_ LPCWSTR pFileName, _In_opt_ UINT32* codePage, + _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingFromPinned( + _In_bytecount_(size) LPCVOID pText, UINT32 size, UINT32 codePage, + _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingOnHeapCopy( + _In_bytecount_(size) LPCVOID pText, UINT32 size, UINT32 codePage, + _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateBlobWithEncodingOnMalloc( + _In_bytecount_(size) LPCVOID pText, IMalloc *pIMalloc, UINT32 size, UINT32 codePage, + _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateIncludeHandler( + _COM_Outptr_ IDxcIncludeHandler **ppResult) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateStreamFromBlobReadOnly( + _In_ IDxcBlob *pBlob, _COM_Outptr_ IStream **ppStream) = 0; + virtual HRESULT STDMETHODCALLTYPE GetBlobAsUtf8( + _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + virtual HRESULT STDMETHODCALLTYPE GetBlobAsUtf16( + _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcLibrary) +}; + +// NOTE: IDxcResult replaces IDxcOperationResult +struct __declspec(uuid("CEDB484A-D4E9-445A-B991-CA21CA157DC2")) +IDxcOperationResult : public IUnknown { + virtual HRESULT STDMETHODCALLTYPE GetStatus(_Out_ HRESULT *pStatus) = 0; + + // GetResult returns the main result of the operation. + // This corresponds to: + // DXC_OUT_OBJECT - Compile() with shader or library target + // DXC_OUT_DISASSEMBLY - Disassemble() + // DXC_OUT_HLSL - Compile() with -P + // DXC_OUT_ROOT_SIGNATURE - Compile() with rootsig_* target + virtual HRESULT STDMETHODCALLTYPE GetResult(_COM_Outptr_result_maybenull_ IDxcBlob **ppResult) = 0; + + // GetErrorBuffer Corresponds to DXC_OUT_ERRORS. + virtual HRESULT STDMETHODCALLTYPE GetErrorBuffer(_COM_Outptr_result_maybenull_ IDxcBlobEncoding **ppErrors) = 0; + + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcOperationResult) +}; + +// NOTE: IDxcCompiler3 replaces IDxcCompiler and IDxcCompiler2 struct __declspec(uuid("8c210bf3-011f-4422-8d70-6f9acb8db617")) IDxcCompiler : public IUnknown { // Compile a single entry point to the target shader model virtual HRESULT STDMETHODCALLTYPE Compile( _In_ IDxcBlob *pSource, // Source text to compile - _In_opt_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. - _In_ LPCWSTR pEntryPoint, // entry point name - _In_ LPCWSTR pTargetProfile, // shader profile to compile - _In_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments + _In_opt_z_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. + _In_opt_z_ LPCWSTR pEntryPoint, // entry point name + _In_z_ LPCWSTR pTargetProfile, // shader profile to compile + _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments - _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines + _In_count_(defineCount) + const DxcDefine *pDefines, // Array of defines _In_ UINT32 defineCount, // Number of defines _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional) _COM_Outptr_ IDxcOperationResult **ppResult // Compiler output status, buffer, and errors @@ -194,10 +301,11 @@ IDxcCompiler : public IUnknown { // Preprocess source text virtual HRESULT STDMETHODCALLTYPE Preprocess( _In_ IDxcBlob *pSource, // Source text to preprocess - _In_opt_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. - _In_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments + _In_opt_z_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. + _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments - _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines + _In_count_(defineCount) + const DxcDefine *pDefines, // Array of defines _In_ UINT32 defineCount, // Number of defines _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional) _COM_Outptr_ IDxcOperationResult **ppResult // Preprocessor output status, buffer, and errors @@ -212,21 +320,23 @@ IDxcCompiler : public IUnknown { DECLARE_CROSS_PLATFORM_UUIDOF(IDxcCompiler) }; +// NOTE: IDxcCompiler3 replaces IDxcCompiler and IDxcCompiler2 struct __declspec(uuid("A005A9D9-B8BB-4594-B5C9-0E633BEC4D37")) IDxcCompiler2 : public IDxcCompiler { // Compile a single entry point to the target shader model with debug information. virtual HRESULT STDMETHODCALLTYPE CompileWithDebug( _In_ IDxcBlob *pSource, // Source text to compile - _In_opt_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. - _In_ LPCWSTR pEntryPoint, // Entry point name - _In_ LPCWSTR pTargetProfile, // Shader profile to compile - _In_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments + _In_opt_z_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. + _In_opt_z_ LPCWSTR pEntryPoint, // Entry point name + _In_z_ LPCWSTR pTargetProfile, // Shader profile to compile + _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments _In_ UINT32 argCount, // Number of arguments - _In_count_(defineCount) const DxcDefine *pDefines, // Array of defines + _In_count_(defineCount) + const DxcDefine *pDefines, // Array of defines _In_ UINT32 defineCount, // Number of defines _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional) _COM_Outptr_ IDxcOperationResult **ppResult, // Compiler output status, buffer, and errors - _Outptr_opt_result_z_ LPWSTR *ppDebugBlobName,// Suggested file name for debug blob. + _Outptr_opt_result_z_ LPWSTR *ppDebugBlobName,// Suggested file name for debug blob. (Must be HeapFree()'d!) _COM_Outptr_opt_ IDxcBlob **ppDebugBlob // Debug blob ) = 0; @@ -238,28 +348,181 @@ IDxcLinker : public IUnknown { public: // Register a library with name to ref it later. virtual HRESULT RegisterLibrary( - _In_opt_ LPCWSTR pLibName, // Name of the library. - _In_ IDxcBlob *pLib // Library blob. + _In_opt_ LPCWSTR pLibName, // Name of the library. + _In_ IDxcBlob *pLib // Library blob. ) = 0; // Links the shader and produces a shader blob that the Direct3D runtime can // use. virtual HRESULT STDMETHODCALLTYPE Link( - _In_opt_ LPCWSTR pEntryName, // Entry point name - _In_ LPCWSTR pTargetProfile, // shader profile to link - _In_count_(libCount) - const LPCWSTR *pLibNames, // Array of library names to link - UINT32 libCount, // Number of libraries to link - _In_count_(argCount) - const LPCWSTR *pArguments, // Array of pointers to arguments - _In_ UINT32 argCount, // Number of arguments - _COM_Outptr_ IDxcOperationResult * - *ppResult // Linker output status, buffer, and errors + _In_opt_ LPCWSTR pEntryName, // Entry point name + _In_ LPCWSTR pTargetProfile, // shader profile to link + _In_count_(libCount) + const LPCWSTR *pLibNames, // Array of library names to link + _In_ UINT32 libCount, // Number of libraries to link + _In_opt_count_(argCount) const LPCWSTR *pArguments, // Array of pointers to arguments + _In_ UINT32 argCount, // Number of arguments + _COM_Outptr_ + IDxcOperationResult **ppResult // Linker output status, buffer, and errors ) = 0; DECLARE_CROSS_PLATFORM_UUIDOF(IDxcLinker) }; +///////////////////////// +// Latest interfaces. Please use these +//////////////////////// + +// NOTE: IDxcUtils replaces IDxcLibrary +struct __declspec(uuid("4605C4CB-2019-492A-ADA4-65F20BB7D67F")) +IDxcUtils : public IUnknown { + // Create a sub-blob that holds a reference to the outer blob and points to its memory. + virtual HRESULT STDMETHODCALLTYPE CreateBlobFromBlob( + _In_ IDxcBlob *pBlob, UINT32 offset, UINT32 length, _COM_Outptr_ IDxcBlob **ppResult) = 0; + + // For codePage, use 0 (or DXC_CP_ACP) for raw binary or ANSI code page + + // Creates a blob referencing existing memory, with no copy. + // User must manage the memory lifetime separately. + // (was: CreateBlobWithEncodingFromPinned) + virtual HRESULT STDMETHODCALLTYPE CreateBlobFromPinned( + _In_bytecount_(size) LPCVOID pData, UINT32 size, UINT32 codePage, + _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + + // Create blob, taking ownership of memory allocated with supplied allocator. + // (was: CreateBlobWithEncodingOnMalloc) + virtual HRESULT STDMETHODCALLTYPE MoveToBlob( + _In_bytecount_(size) LPCVOID pData, IMalloc *pIMalloc, UINT32 size, UINT32 codePage, + _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + + //// + // New blobs and copied contents are allocated with the current allocator + + // Copy blob contents to memory owned by the new blob. + // (was: CreateBlobWithEncodingOnHeapCopy) + virtual HRESULT STDMETHODCALLTYPE CreateBlob( + _In_bytecount_(size) LPCVOID pData, UINT32 size, UINT32 codePage, + _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + + // (was: CreateBlobFromFile) + virtual HRESULT STDMETHODCALLTYPE LoadFile( + _In_z_ LPCWSTR pFileName, _In_opt_ UINT32* pCodePage, + _COM_Outptr_ IDxcBlobEncoding **pBlobEncoding) = 0; + + virtual HRESULT STDMETHODCALLTYPE CreateReadOnlyStreamFromBlob( + _In_ IDxcBlob *pBlob, _COM_Outptr_ IStream **ppStream) = 0; + + // Create default file-based include handler + virtual HRESULT STDMETHODCALLTYPE CreateDefaultIncludeHandler( + _COM_Outptr_ IDxcIncludeHandler **ppResult) = 0; + + // Convert or return matching encoded text blobs + virtual HRESULT STDMETHODCALLTYPE GetBlobAsUtf8( + _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobUtf8 **pBlobEncoding) = 0; + virtual HRESULT STDMETHODCALLTYPE GetBlobAsUtf16( + _In_ IDxcBlob *pBlob, _COM_Outptr_ IDxcBlobUtf16 **pBlobEncoding) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDxilContainerPart( + _In_ const DxcBuffer *pShader, + _In_ UINT32 DxcPart, + _Outptr_result_nullonfailure_ void **ppPartData, + _Out_ UINT32 *pPartSizeInBytes) = 0; + + // Create reflection interface from serialized Dxil container, or DXC_PART_REFLECTION_DATA. + // TBD: Require part header for RDAT? (leaning towards yes) + virtual HRESULT STDMETHODCALLTYPE CreateReflection( + _In_ const DxcBuffer *pData, REFIID iid, void **ppvReflection) = 0; + + virtual HRESULT STDMETHODCALLTYPE BuildArguments( + _In_opt_z_ LPCWSTR pSourceName, // Optional file name for pSource. Used in errors and include handlers. + _In_opt_z_ LPCWSTR pEntryPoint, // Entry point name. (-E) + _In_z_ LPCWSTR pTargetProfile, // Shader profile to compile. (-T) + _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments + _In_ UINT32 argCount, // Number of arguments + _In_count_(defineCount) + const DxcDefine *pDefines, // Array of defines + _In_ UINT32 defineCount, // Number of defines + _COM_Outptr_ IDxcCompilerArgs **ppArgs // Arguments you can use with Compile() method + ) = 0; + + // Takes the shader PDB and returns the hash and the container inside it + virtual HRESULT STDMETHODCALLTYPE GetPDBContents( + _In_ IDxcBlob *pPDBBlob, _COM_Outptr_ IDxcBlob **ppHash, _COM_Outptr_ IDxcBlob **ppContainer) = 0; + + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcUtils) +}; + +// For use with IDxcResult::[Has|Get]Output dxcOutKind argument +// Note: text outputs returned from version 2 APIs are UTF-8 or UTF-16 based on -encoding option +typedef enum DXC_OUT_KIND { + DXC_OUT_NONE = 0, + DXC_OUT_OBJECT = 1, // IDxcBlob - Shader or library object + DXC_OUT_ERRORS = 2, // IDxcBlobUtf8 or IDxcBlobUtf16 + DXC_OUT_PDB = 3, // IDxcBlob + DXC_OUT_SHADER_HASH = 4, // IDxcBlob - DxcShaderHash of shader or shader with source info (-Zsb/-Zss) + DXC_OUT_DISASSEMBLY = 5, // IDxcBlobUtf8 or IDxcBlobUtf16 - from Disassemble + DXC_OUT_HLSL = 6, // IDxcBlobUtf8 or IDxcBlobUtf16 - from Preprocessor or Rewriter + DXC_OUT_TEXT = 7, // IDxcBlobUtf8 or IDxcBlobUtf16 - other text, such as -ast-dump or -Odump + DXC_OUT_REFLECTION = 8, // IDxcBlob - RDAT part with reflection data + DXC_OUT_ROOT_SIGNATURE = 9, // IDxcBlob - Serialized root signature output + DXC_OUT_EXTRA_OUTPUTS = 10,// IDxcExtraResults - Extra outputs + + DXC_OUT_FORCE_DWORD = 0xFFFFFFFF +} DXC_OUT_KIND; + +struct __declspec(uuid("58346CDA-DDE7-4497-9461-6F87AF5E0659")) +IDxcResult : public IDxcOperationResult { + virtual BOOL STDMETHODCALLTYPE HasOutput(_In_ DXC_OUT_KIND dxcOutKind) = 0; + virtual HRESULT STDMETHODCALLTYPE GetOutput(_In_ DXC_OUT_KIND dxcOutKind, + _In_ REFIID iid, _COM_Outptr_opt_result_maybenull_ void **ppvObject, + _COM_Outptr_ IDxcBlobUtf16 **ppOutputName) = 0; + + virtual UINT32 GetNumOutputs() = 0; + virtual DXC_OUT_KIND GetOutputByIndex(UINT32 Index) = 0; + virtual DXC_OUT_KIND PrimaryOutput() = 0; + + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcResult) +}; + +// Special names for extra output that should get written to specific streams +#define DXC_EXTRA_OUTPUT_NAME_STDOUT L"*stdout*" +#define DXC_EXTRA_OUTPUT_NAME_STDERR L"*stderr*" + +struct __declspec(uuid("319b37a2-a5c2-494a-a5de-4801b2faf989")) +IDxcExtraOutputs : public IUnknown { + + virtual UINT32 STDMETHODCALLTYPE GetOutputCount() = 0; + virtual HRESULT STDMETHODCALLTYPE GetOutput(_In_ UINT32 uIndex, + _In_ REFIID iid, _COM_Outptr_opt_result_maybenull_ void **ppvObject, + _COM_Outptr_opt_result_maybenull_ IDxcBlobUtf16 **ppOutputType, + _COM_Outptr_opt_result_maybenull_ IDxcBlobUtf16 **ppOutputName) = 0; + + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcExtraOutputs) +}; + +struct __declspec(uuid("228B4687-5A6A-4730-900C-9702B2203F54")) +IDxcCompiler3 : public IUnknown { + // Compile a single entry point to the target shader model, + // Compile a library to a library target (-T lib_*), + // Compile a root signature (-T rootsig_*), or + // Preprocess HLSL source (-P) + virtual HRESULT STDMETHODCALLTYPE Compile( + _In_ const DxcBuffer *pSource, // Source text to compile + _In_opt_count_(argCount) LPCWSTR *pArguments, // Array of pointers to arguments + _In_ UINT32 argCount, // Number of arguments + _In_opt_ IDxcIncludeHandler *pIncludeHandler, // user-provided interface to handle #include directives (optional) + _In_ REFIID riid, _Out_ LPVOID *ppResult // IDxcResult: status, buffer, and errors + ) = 0; + + // Disassemble a program. + virtual HRESULT STDMETHODCALLTYPE Disassemble( + _In_ const DxcBuffer *pObject, // Program to disassemble: dxil container or bitcode. + _In_ REFIID riid, _Out_ LPVOID *ppResult // IDxcResult: status, disassembly text, and errors + ) = 0; + + DECLARE_CROSS_PLATFORM_UUIDOF(IDxcCompiler3) +}; + static const UINT32 DxcValidatorFlags_Default = 0; static const UINT32 DxcValidatorFlags_InPlaceEdit = 1; // Validator is allowed to update shader blob in-place. static const UINT32 DxcValidatorFlags_RootSignatureOnly = 2; @@ -356,80 +619,79 @@ IDxcVersionInfo2 : public IDxcVersionInfo { // Note: __declspec(selectany) requires 'extern' // On Linux __declspec(selectany) is removed and using 'extern' results in link error. #ifdef _MSC_VER -#define EXTERN extern +#define CLSID_SCOPE __declspec(selectany) extern #else -#define EXTERN +#define CLSID_SCOPE #endif -// {73e22d93-e6ce-47f3-b5bf-f0664f39c1b0} -__declspec(selectany) EXTERN const CLSID CLSID_DxcCompiler = { - 0x73e22d93, - 0xe6ce, - 0x47f3, - { 0xb5, 0xbf, 0xf0, 0x66, 0x4f, 0x39, 0xc1, 0xb0 } -}; +CLSID_SCOPE const CLSID CLSID_DxcCompiler = { + 0x73e22d93, + 0xe6ce, + 0x47f3, + {0xb5, 0xbf, 0xf0, 0x66, 0x4f, 0x39, 0xc1, 0xb0}}; // {EF6A8087-B0EA-4D56-9E45-D07E1A8B7806} -__declspec(selectany) EXTERN const GUID CLSID_DxcLinker = { +CLSID_SCOPE const GUID CLSID_DxcLinker = { 0xef6a8087, 0xb0ea, 0x4d56, - {0x9e, 0x45, 0xd0, 0x7e, 0x1a, 0x8b, 0x78, 0x6} -}; + {0x9e, 0x45, 0xd0, 0x7e, 0x1a, 0x8b, 0x78, 0x6}}; // {CD1F6B73-2AB0-484D-8EDC-EBE7A43CA09F} -__declspec(selectany) EXTERN const CLSID CLSID_DxcDiaDataSource = { - 0xcd1f6b73, - 0x2ab0, - 0x484d, - { 0x8e, 0xdc, 0xeb, 0xe7, 0xa4, 0x3c, 0xa0, 0x9f } -}; +CLSID_SCOPE const CLSID CLSID_DxcDiaDataSource = { + 0xcd1f6b73, + 0x2ab0, + 0x484d, + {0x8e, 0xdc, 0xeb, 0xe7, 0xa4, 0x3c, 0xa0, 0x9f}}; + +// {3E56AE82-224D-470F-A1A1-FE3016EE9F9D} +CLSID_SCOPE const CLSID CLSID_DxcCompilerArgs = { + 0x3e56ae82, + 0x224d, + 0x470f, + {0xa1, 0xa1, 0xfe, 0x30, 0x16, 0xee, 0x9f, 0x9d}}; // {6245D6AF-66E0-48FD-80B4-4D271796748C} -__declspec(selectany) EXTERN const GUID CLSID_DxcLibrary = { - 0x6245d6af, - 0x66e0, - 0x48fd, - { 0x80, 0xb4, 0x4d, 0x27, 0x17, 0x96, 0x74, 0x8c } -}; +CLSID_SCOPE const GUID CLSID_DxcLibrary = { + 0x6245d6af, + 0x66e0, + 0x48fd, + {0x80, 0xb4, 0x4d, 0x27, 0x17, 0x96, 0x74, 0x8c}}; + +CLSID_SCOPE const GUID CLSID_DxcUtils = CLSID_DxcLibrary; // {8CA3E215-F728-4CF3-8CDD-88AF917587A1} -__declspec(selectany) EXTERN const GUID CLSID_DxcValidator = { - 0x8ca3e215, - 0xf728, - 0x4cf3, - { 0x8c, 0xdd, 0x88, 0xaf, 0x91, 0x75, 0x87, 0xa1 } -}; +CLSID_SCOPE const GUID CLSID_DxcValidator = { + 0x8ca3e215, + 0xf728, + 0x4cf3, + {0x8c, 0xdd, 0x88, 0xaf, 0x91, 0x75, 0x87, 0xa1}}; // {D728DB68-F903-4F80-94CD-DCCF76EC7151} -__declspec(selectany) EXTERN const GUID CLSID_DxcAssembler = { - 0xd728db68, - 0xf903, - 0x4f80, - { 0x94, 0xcd, 0xdc, 0xcf, 0x76, 0xec, 0x71, 0x51 } -}; +CLSID_SCOPE const GUID CLSID_DxcAssembler = { + 0xd728db68, + 0xf903, + 0x4f80, + {0x94, 0xcd, 0xdc, 0xcf, 0x76, 0xec, 0x71, 0x51}}; // {b9f54489-55b8-400c-ba3a-1675e4728b91} -__declspec(selectany) EXTERN const GUID CLSID_DxcContainerReflection = { - 0xb9f54489, - 0x55b8, - 0x400c, - { 0xba, 0x3a, 0x16, 0x75, 0xe4, 0x72, 0x8b, 0x91 } -}; +CLSID_SCOPE const GUID CLSID_DxcContainerReflection = { + 0xb9f54489, + 0x55b8, + 0x400c, + {0xba, 0x3a, 0x16, 0x75, 0xe4, 0x72, 0x8b, 0x91}}; // {AE2CD79F-CC22-453F-9B6B-B124E7A5204C} -__declspec(selectany) EXTERN const GUID CLSID_DxcOptimizer = { +CLSID_SCOPE const GUID CLSID_DxcOptimizer = { 0xae2cd79f, 0xcc22, 0x453f, - {0x9b, 0x6b, 0xb1, 0x24, 0xe7, 0xa5, 0x20, 0x4c} -}; + {0x9b, 0x6b, 0xb1, 0x24, 0xe7, 0xa5, 0x20, 0x4c}}; // {94134294-411f-4574-b4d0-8741e25240d2} -__declspec(selectany) EXTERN const GUID CLSID_DxcContainerBuilder = { - 0x94134294, - 0x411f, - 0x4574, - { 0xb4, 0xd0, 0x87, 0x41, 0xe2, 0x52, 0x40, 0xd2 } -}; +CLSID_SCOPE const GUID CLSID_DxcContainerBuilder = { + 0x94134294, + 0x411f, + 0x4574, + {0xb4, 0xd0, 0x87, 0x41, 0xe2, 0x52, 0x40, 0xd2}}; #endif From f5b9a95522524eb381d05bc8e8e0c6f57ca0c7fc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 5 Jan 2021 14:15:32 +0100 Subject: [PATCH 037/419] Fix null character at shader sources at during game cooking --- Source/Editor/Cooker/Steps/CookAssetsStep.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index af6f003b8..7f2824c42 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -323,6 +323,8 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass auto sourceLength = sourceChunk->Size(); Encryption::DecryptBytes((byte*)source, sourceLength); source[sourceLength - 1] = 0; + while (sourceLength > 2 && source[sourceLength - 1] == 0) + sourceLength--; // Init shader cache output stream // TODO: reuse MemoryWriteStream per cooking process to reduce dynamic memory allocations From 1e383fde5ced50105966b9eb031d80bec502449d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 5 Jan 2021 14:15:42 +0100 Subject: [PATCH 038/419] Fix using AssetRefEditor for editing asset reference on Guid property --- .../CustomEditors/Editors/AssetRefEditor.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 49950dae8..7a0e5819c 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -34,7 +34,7 @@ namespace FlaxEditor.CustomEditors.Editors public class AssetRefEditor : CustomEditor { private CustomElement _element; - private ScriptType _type; + private ScriptType _valueType; /// public override DisplayStyle Style => DisplayStyle.Inline; @@ -44,7 +44,8 @@ namespace FlaxEditor.CustomEditors.Editors { if (!HasDifferentTypes) { - _type = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]); + _valueType = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]); + var assetType = _valueType; float height = 48; var attributes = Values.GetAttributes(); @@ -58,14 +59,14 @@ namespace FlaxEditor.CustomEditors.Editors { var customType = TypeUtils.GetType(assetReference.TypeName); if (customType != ScriptType.Null) - _type = customType; + assetType = customType; else Debug.LogWarning(string.Format("Unknown asset type '{0}' to use for asset picker filter.", assetReference.TypeName)); } } _element = layout.Custom(); - _element.CustomControl.AssetType = _type; + _element.CustomControl.AssetType = assetType; _element.CustomControl.Height = height; _element.CustomControl.SelectedItemChanged += OnSelectedItemChanged; } @@ -73,11 +74,11 @@ namespace FlaxEditor.CustomEditors.Editors private void OnSelectedItemChanged() { - if (typeof(AssetItem).IsAssignableFrom(_type.Type)) + if (typeof(AssetItem).IsAssignableFrom(_valueType.Type)) SetValue(_element.CustomControl.SelectedItem); - else if (_type.Type == typeof(Guid)) + else if (_valueType.Type == typeof(Guid)) SetValue(_element.CustomControl.SelectedID); - else if (_type.Type == typeof(SceneReference)) + else if (_valueType.Type == typeof(SceneReference)) SetValue(new SceneReference(_element.CustomControl.SelectedID)); else SetValue(_element.CustomControl.SelectedAsset); From 31bab88a5bdddc75e4bb0cd6ea3ad2427eb4e914 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 5 Jan 2021 14:15:49 +0100 Subject: [PATCH 039/419] Fix PlatformSettings typedef on Android --- Source/Engine/Platform/Android/AndroidPlatformSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index 8805b2180..0526849a9 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -48,7 +48,7 @@ public: } }; -#if PLATFORM_LINUX +#if PLATFORM_ANDROID typedef AndroidPlatformSettings PlatformSettings; #endif From d7224670c5af688a1c0df5950a1a47e2a4b1c6ac Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 5 Jan 2021 14:16:17 +0100 Subject: [PATCH 040/419] Fix PCF shadow uvs vector trunc warning --- Source/Shaders/ShadowsSampling.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Shaders/ShadowsSampling.hlsl b/Source/Shaders/ShadowsSampling.hlsl index ffd757e42..6bea0678c 100644 --- a/Source/Shaders/ShadowsSampling.hlsl +++ b/Source/Shaders/ShadowsSampling.hlsl @@ -518,7 +518,7 @@ float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, UNROLL for(int i = 0; i < FilterSizeCube; i++) { - float2 samplePos = shadowMapUVs + sideVector * PCFDiscSamples[i].x + upVector * PCFDiscSamples[i].y; + float2 samplePos = shadowMapUVs + sideVector.xy * PCFDiscSamples[i].x + upVector.xy * PCFDiscSamples[i].y; result += shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, samplePos, shadowPosition.z); } result *= (1.0f / FilterSizeCube); From 6da5c704b12592d9bfbd5fe699b1d1e49f3cf43d Mon Sep 17 00:00:00 2001 From: VNC <52937757+VNNCC@users.noreply.github.com> Date: Wed, 6 Jan 2021 01:03:19 +0100 Subject: [PATCH 041/419] Change 'for' to 'to' --- Source/Editor/Surface/Archetypes/Animation.StateMachine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 63faacb6d..2faa9feef 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -1046,7 +1046,7 @@ namespace FlaxEditor.Surface.Archetypes startPos += nrm; endPos += nrm; - // Swap for the other arrow + // Swap to the other arrow if (!diff) { var tmp = startPos; From 6f727aa48355ad117bddafbff72e2c1a200bae82 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Wed, 6 Jan 2021 22:20:16 +0100 Subject: [PATCH 042/419] Corrected additional typos --- Source/Editor/Gizmo/GizmosCollection.cs | 2 +- Source/Editor/Gizmo/TransformGizmoBase.Draw.cs | 2 +- Source/Editor/Gizmo/TransformGizmoBase.Settings.cs | 2 +- Source/Editor/Gizmo/TransformGizmoBase.cs | 4 ++-- Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Gizmo/GizmosCollection.cs b/Source/Editor/Gizmo/GizmosCollection.cs index a246aeeb0..5530a41a3 100644 --- a/Source/Editor/Gizmo/GizmosCollection.cs +++ b/Source/Editor/Gizmo/GizmosCollection.cs @@ -7,7 +7,7 @@ using FlaxEngine; namespace FlaxEditor.Gizmo { /// - /// Represents collection of Gizmo tools where one is active and in use. + /// Represents collection of gizmo tools where one is active and in use. /// /// [HideInEditor] diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs index 8f871c635..0a11ecd77 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs @@ -45,7 +45,7 @@ namespace FlaxEditor.Gizmo ) { // Error - Platform.Fatal("Failed to load Transform Gizmo resources."); + Platform.Fatal("Failed to load transform gizmo resources."); } } diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs index 786e001e6..2955fe6d7 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Settings.cs @@ -104,7 +104,7 @@ namespace FlaxEditor.Gizmo public Axis ActiveAxis => _activeAxis; /// - /// Gets or sts the current gizmo mode. + /// Gets or sets the current gizmo mode. /// public Mode ActiveMode { diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 87dcb6fa8..93dbeddaa 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -151,7 +151,7 @@ namespace FlaxEditor.Gizmo // Set positions of the gizmo UpdateGizmoPosition(); - // Scale Gizmo to fit on-screen + // Scale gizmo to fit on-screen Vector3 vLength = Owner.ViewPosition - Position; float gizmoSize = Editor.Instance.Options.Options.Visual.GizmoSize; _screenScale = vLength.Length / GizmoScaleFactor * gizmoSize; @@ -318,7 +318,7 @@ namespace FlaxEditor.Gizmo } else if (_activeMode == Mode.Scale) { - // Apply Scale + // Apply scale _scaleDelta = delta; } } diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h index cc1b5b6b2..42aca7dbb 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h @@ -100,7 +100,7 @@ public: public: /// - /// Determinate whenever this emitter uses lights rendering. + /// Determines whenever this emitter uses lights rendering. /// /// True if emitter uses lights rendering, otherwise false. FORCE_INLINE bool UsesLightRendering() const From 6deb64e587751d0e9fc5ce056fb98aadc98070fd Mon Sep 17 00:00:00 2001 From: VNC <52937757+VNNCC@users.noreply.github.com> Date: Thu, 7 Jan 2021 00:02:04 +0100 Subject: [PATCH 043/419] Fix to disable terrain and foliage buttons if no scene is present This will fix the issues described in #99. --- Source/Editor/Tools/Foliage/FoliageTab.cs | 23 +++++++++++++++++-- Source/Editor/Tools/Terrain/CarveTab.cs | 28 ++++++++++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index 9dbb17e88..bbad9018b 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -21,6 +21,7 @@ namespace FlaxEditor.Tools.Foliage private readonly Tabs _modes; private readonly ContainerControl _noFoliagePanel; private int _selectedFoliageTypeIndex = -1; + private Button _createNewFoliage; /// /// The editor instance. @@ -99,6 +100,7 @@ namespace FlaxEditor.Tools.Foliage public FoliageTab(SpriteHandle icon, Editor editor) : base(string.Empty, icon) { + Level.SceneLoaded += this.OnSceneLoaded; Editor = editor; Editor.SceneEditing.SelectionChanged += OnSelectionChanged; @@ -135,14 +137,31 @@ namespace FlaxEditor.Tools.Foliage Offsets = Margin.Zero, Parent = _noFoliagePanel }; - var noFoliageButton = new Button + _createNewFoliage = new Button { Text = "Create new foliage", AnchorPreset = AnchorPresets.MiddleCenter, Offsets = new Margin(-60, 120, -12, 24), Parent = _noFoliagePanel, + Enabled = false }; - noFoliageButton.Clicked += OnCreateNewFoliageClicked; + _createNewFoliage.Clicked += OnCreateNewFoliageClicked; + } + + private void OnSceneLoaded(Scene arg1, Guid arg2) + { + _createNewFoliage.Enabled = true; + + Level.SceneUnloaded += this.OnSceneUnloaded; + Level.SceneLoaded -= OnSceneLoaded; + } + + private void OnSceneUnloaded(Scene arg1, Guid arg2) + { + _createNewFoliage.Enabled = false; + + Level.SceneLoaded += OnSceneLoaded; + Level.SceneUnloaded -= this.OnSceneUnloaded; } private void OnSelected(Tab tab) diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index a3a65c52a..c8029c82a 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -18,6 +18,7 @@ namespace FlaxEditor.Tools.Terrain { private readonly Tabs _modes; private readonly ContainerControl _noTerrainPanel; + private readonly Button _createTerrainButton; /// /// The editor instance. @@ -57,6 +58,7 @@ namespace FlaxEditor.Tools.Terrain public CarveTab(SpriteHandle icon, Editor editor) : base(string.Empty, icon) { + Level.SceneLoaded += this.OnSceneLoaded; Editor = editor; Editor.SceneEditing.SelectionChanged += OnSelectionChanged; @@ -93,14 +95,31 @@ namespace FlaxEditor.Tools.Terrain Offsets = Margin.Zero, Parent = _noTerrainPanel }; - var noTerrainButton = new Button + _createTerrainButton = new Button { Text = "Create new terrain", AnchorPreset = AnchorPresets.MiddleCenter, Offsets = new Margin(-60, 120, -12, 24), - Parent = _noTerrainPanel + Parent = _noTerrainPanel, + Enabled = false }; - noTerrainButton.Clicked += OnCreateNewTerrainClicked; + _createTerrainButton.Clicked += OnCreateNewTerrainClicked; + } + + private void OnSceneLoaded(Scene arg1, Guid arg2) + { + _createTerrainButton.Enabled = true; + + Level.SceneUnloaded += this.OnSceneUnloaded; + Level.SceneLoaded -= OnSceneLoaded; + } + + private void OnSceneUnloaded(Scene arg1, Guid arg2) + { + _createTerrainButton.Enabled = false; + + Level.SceneLoaded += OnSceneLoaded; + Level.SceneUnloaded -= this.OnSceneUnloaded; } private void OnSelected(Tab tab) @@ -117,6 +136,9 @@ namespace FlaxEditor.Tools.Terrain private void OnCreateNewTerrainClicked() { + if (!Level.IsAnySceneLoaded) + return; + Editor.UI.CreateTerrain(); } From 2252cd73f98b2b1dac6ea895aad640a970ec63d8 Mon Sep 17 00:00:00 2001 From: VNC <52937757+VNNCC@users.noreply.github.com> Date: Thu, 7 Jan 2021 00:06:43 +0100 Subject: [PATCH 044/419] Removed unnecessary check --- Source/Editor/Tools/Terrain/CarveTab.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index c8029c82a..37ce82c1f 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -136,9 +136,6 @@ namespace FlaxEditor.Tools.Terrain private void OnCreateNewTerrainClicked() { - if (!Level.IsAnySceneLoaded) - return; - Editor.UI.CreateTerrain(); } From 0d0340e11adaa39aea34083e7ff535474d905f96 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Fri, 8 Jan 2021 12:06:17 +0100 Subject: [PATCH 045/419] Added golden ratio value --- Source/Engine/Core/Math/Mathf.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Engine/Core/Math/Mathf.cs b/Source/Engine/Core/Math/Mathf.cs index 7286573bf..8240b02cc 100644 --- a/Source/Engine/Core/Math/Mathf.cs +++ b/Source/Engine/Core/Math/Mathf.cs @@ -36,6 +36,11 @@ namespace FlaxEngine /// public const float PiOverFour = (float)(Math.PI / 4); + /// + /// A value specifying the golden mean + /// + public const float GoldenRatio = 1.61803f; + /// /// Returns the absolute value of f. /// From 2d2d45f56848ed34c75cb2d2eae76241cb4e5b41 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Fri, 8 Jan 2021 23:14:09 +0100 Subject: [PATCH 046/419] Increased precision by 5 digits --- Source/Engine/Core/Math/Mathf.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Math/Mathf.cs b/Source/Engine/Core/Math/Mathf.cs index 8240b02cc..529bd6ab0 100644 --- a/Source/Engine/Core/Math/Mathf.cs +++ b/Source/Engine/Core/Math/Mathf.cs @@ -39,7 +39,7 @@ namespace FlaxEngine /// /// A value specifying the golden mean /// - public const float GoldenRatio = 1.61803f; + public const float GoldenRatio = 1.6180339887f; /// /// Returns the absolute value of f. From ef2dbb7818e8fc38b6a49a1f512be07a00e37c1a Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 9 Jan 2021 11:26:29 +0100 Subject: [PATCH 047/419] Fix double context menu invokation SurfaceNode.cs already takes care of opening the context menu --- Source/Editor/Surface/VisjectSurface.Input.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 68966370c..d1f394f3b 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -464,15 +464,7 @@ namespace FlaxEditor.Surface { // Check if any control is under the mouse _cmStartPos = location; - if (controlUnderMouse != null) - { - if (!HasNodesSelection) - Select(controlUnderMouse); - - // Show secondary context menu - ShowSecondaryCM(_cmStartPos, controlUnderMouse); - } - else + if (controlUnderMouse == null) { // Show primary context menu ShowPrimaryMenu(_cmStartPos); From cd5db384a47ee29602baa1d29c08cbf03115134c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 9 Jan 2021 19:40:06 +0100 Subject: [PATCH 048/419] Adding Camera Orientation to editor view menu. --- Source/Editor/Viewport/EditorViewport.cs | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 02696f743..99cb8cb92 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -510,6 +510,17 @@ namespace FlaxEditor.Viewport debugView.VisibleChanged += WidgetViewModeShowHide; } + // Camera Orientation + { + var cameraView = ViewWidgetButtonMenu.AddChildMenu("Camera Orientation").ContextMenu; + for (int i = 0; i < EditorViewportCameraOrientationValues.Length; i++) + { + var co = EditorViewportCameraOrientationValues[i]; + var button = cameraView.AddButton(co.Name); + button.Tag = co.Orientation; + } + cameraView.ButtonClicked += button => ViewOrientation = Quaternion.Euler((Vector3)button.Tag); + } ViewWidgetButtonMenu.AddSeparator(); // Orthographic @@ -1193,6 +1204,28 @@ namespace FlaxEditor.Viewport base.OnDestroy(); } + private struct CameraOrientation + { + public readonly string Name; + public readonly Vector3 Orientation; + + public CameraOrientation(string name, Vector3 orientation) + { + Name = name; + Orientation = orientation; + } + } + + private readonly CameraOrientation[] EditorViewportCameraOrientationValues = + { + new CameraOrientation("Front", new Vector3(0,0,0)), + new CameraOrientation("Back", new Vector3(0,180,0)), + new CameraOrientation("Left", new Vector3(0,90,0)), + new CameraOrientation("Right", new Vector3(0,-90,0)), + new CameraOrientation("Top", new Vector3(-90,0,0)), + new CameraOrientation("Bottom", new Vector3(90,0,0)) + }; + private readonly float[] EditorViewportCameraSpeedValues = { 0.1f, From 9e1c1ecb9a5387f84fe6739a233edc08fed499b1 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 9 Jan 2021 21:46:41 +0100 Subject: [PATCH 049/419] Implement basic node formatter --- .../Surface/VisjectSurface.ContextMenu.cs | 8 + .../Surface/VisjectSurface.Formatting.cs | 264 ++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 Source/Editor/Surface/VisjectSurface.Formatting.cs diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index 6b38ffdfc..c2b0d1887 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -15,6 +15,7 @@ namespace FlaxEditor.Surface { private ContextMenuButton _cmCopyButton; private ContextMenuButton _cmDuplicateButton; + private ContextMenuButton _cmFormatNodesConnectionButton; private ContextMenuButton _cmRemoveNodeConnectionsButton; private ContextMenuButton _cmRemoveBoxConnectionsButton; private readonly Vector2 ContextMenuOffset = new Vector2(5); @@ -216,6 +217,13 @@ namespace FlaxEditor.Surface } }).Enabled = Nodes.Any(x => x.Breakpoint.Set && x.Breakpoint.Enabled); } + menu.AddSeparator(); + + _cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () => + { + FormatGraph(SelectedNodes); + }); + _cmFormatNodesConnectionButton.Enabled = HasNodesSelection; menu.AddSeparator(); _cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () => diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs new file mode 100644 index 000000000..723f8555f --- /dev/null +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FlaxEngine; +using FlaxEditor.Surface.Elements; +using FlaxEditor.Surface.Undo; + +namespace FlaxEditor.Surface +{ + public partial class VisjectSurface + { + // Reference https://github.com/stefnotch/xnode-graph-formatter/blob/812e08e71c7b9b7eb0810dbdfb0a9a1034da6941/Assets/Examples/MathGraph/Editor/MathGraphEditor.cs + + private class NodeFormattingData + { + /// + /// Starting from 0 at the main nodes + /// + public int Layer; + + /// + /// Position in the layer + /// + public int Offset; + + /// + /// How far the subtree needs to be moved additionally + /// + public int SubtreeOffset; + } + + /// + /// Formats a graph where the nodes can be disjointed. + /// Uses the Sugiyama method + /// + /// List of nodes + protected void FormatGraph(List nodes) + { + if (nodes.Count <= 1 || !CanEdit) return; + + var nodesToVisit = new HashSet(nodes); + + // While we haven't formatted every node + while (nodesToVisit.Count > 0) + { + // Run a search in both directions + var connectedNodes = new List(); + var queue = new Queue(); + + var startNode = nodesToVisit.First(); + nodesToVisit.Remove(startNode); + queue.Enqueue(startNode); + + while (queue.Count > 0) + { + var node = queue.Dequeue(); + connectedNodes.Add(node); + + for (int i = 0; i < node.Elements.Count; i++) + { + if (node.Elements[i] is Box box) + { + for (int j = 0; j < box.Connections.Count; j++) + { + if (nodesToVisit.Contains(box.Connections[j].ParentNode)) + { + nodesToVisit.Remove(box.Connections[j].ParentNode); + queue.Enqueue(box.Connections[j].ParentNode); + } + } + + } + } + } + + FormatConnectedGraph(connectedNodes); + } + } + + /// + /// Formats a graph where all nodes are connected + /// + /// List of connected nodes + protected void FormatConnectedGraph(List nodes) + { + if (nodes.Count <= 1 || !CanEdit) return; + + var boundingBox = GetNodesBounds(nodes); + + var nodeData = nodes.ToDictionary(n => n, n => new NodeFormattingData { }); + + // Rightmost nodes with none of our nodes to the right of them + var endNodes = nodes + .Where(n => !n.GetBoxes().Any(b => b.IsOutput && b.Connections.Any(c => nodeData.ContainsKey(c.ParentNode)))) + .OrderBy(n => n.Top) // Keep their relative order + .ToList(); + + // Longest path layering + int maxLayer = SetLayers(nodeData, endNodes); + + // Set the vertical offsets + int maxOffset = SetOffsets(nodeData, endNodes, maxLayer); + + // Get the largest nodes in the Y and X direction + float[] widths = new float[maxLayer + 1]; + float[] heights = new float[maxOffset + 1]; + for (int i = 0; i < nodes.Count; i++) + { + if (nodeData.TryGetValue(nodes[i], out var data)) + { + if (nodes[i].Width > widths[data.Layer]) + { + widths[data.Layer] = nodes[i].Width; + } + if (nodes[i].Height > heights[data.Offset]) + { + heights[data.Offset] = nodes[i].Height; + } + } + } + + // Layout the nodes + Vector2 minSize = new Vector2(180, 180); + + var undoActions = new List(); + var topRightPosition = endNodes[0].Location; + for (int i = 0; i < nodes.Count; i++) + { + if (nodeData.TryGetValue(nodes[i], out var data)) + { + Vector2 size = Vector2.Max(minSize, new Vector2(widths[data.Layer], heights[data.Offset])); + Vector2 newLocation = (new Vector2(-data.Layer, data.Offset) * size) + topRightPosition; + Vector2 locationDelta = newLocation - nodes[i].Location; + nodes[i].Location = newLocation; + + if (Undo != null) + undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta)); + } + } + + MarkAsEdited(false); + Undo?.AddAction(new MultiUndoAction(undoActions, "Format nodes")); + } + + /// + /// Assigns a layer to every node + /// + /// The exta node data + /// The end nodes + /// The number of the maximum layer + private int SetLayers(Dictionary nodeData, List endNodes) + { + int maxLayer = 0; + var stack = new Stack(endNodes); + + while (stack.Count > 0) + { + var node = stack.Pop(); + int layer = nodeData[node].Layer; + + for (int i = 0; i < node.Elements.Count; i++) + { + if (node.Elements[i] is InputBox box && box.HasAnyConnection) + { + var childNode = box.Connections[0].ParentNode; + + if (nodeData.TryGetValue(childNode, out var data)) + { + int nodeLayer = Math.Max(data.Layer, layer + 1); + data.Layer = nodeLayer; + if (nodeLayer > maxLayer) + { + maxLayer = nodeLayer; + } + + stack.Push(childNode); + } + } + } + } + return maxLayer; + } + + + /// + /// Sets the node offsets + /// + /// The exta node data + /// The end nodes + /// The number of the maximum layer + /// The number of the maximum offset + private int SetOffsets(Dictionary nodeData, List endNodes, int maxLayer) + { + // This piece of code should be explained a bit better, since it does some fairly fancy stuff + int maxOffset = 0; + int[] offsets = new int[maxLayer + 1]; + + var visitedNodes = new HashSet(); + + void SetOffsets(SurfaceNode node, NodeFormattingData straightParentData) + { + if (!nodeData.TryGetValue(node, out var data)) return; + + if (data.Layer >= 0 && offsets[data.Layer] > data.Offset) + { + straightParentData.SubtreeOffset = Math.Max(straightParentData.SubtreeOffset, offsets[data.Layer] - data.Offset); + } + + int childOffset = data.Offset; + bool straightChild = true; + + for (int i = 0; i < node.Elements.Count; i++) + { + if (node.Elements[i] is InputBox box && box.HasAnyConnection) + { + var childNode = box.Connections[0].ParentNode; + if (!visitedNodes.Contains(childNode) && nodeData.TryGetValue(childNode, out var childData)) + { + visitedNodes.Add(childNode); + childData.Offset = childOffset; + SetOffsets(childNode, straightChild ? straightParentData : childData); + childOffset = childData.Offset + 1; + straightChild = false; + } + } + } + + if (data.Layer >= 0) + { + data.Offset += straightParentData.SubtreeOffset; + if (data.Offset > maxOffset) + { + maxOffset = data.Offset; + } + offsets[data.Layer] = data.Offset + 1; + } + } + + { + // An imaginary final node + var endNodeData = new NodeFormattingData { Layer = -1 }; + int childOffset = 0; + bool straightChild = true; + + for (int i = 0; i < endNodes.Count; i++) + { + if (nodeData.TryGetValue(endNodes[i], out var childData)) + { + visitedNodes.Add(endNodes[i]); + childData.Offset = childOffset; + SetOffsets(endNodes[i], straightChild ? endNodeData : childData); + childOffset = childData.Offset + 1; + straightChild = false; + } + } + } + + return maxOffset; + } + + } +} From 01e904e7021982958314b72f915c8c365bda863a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 10 Jan 2021 00:17:47 +0100 Subject: [PATCH 050/419] Fixing Windows position (on startup) being always on the top left corner. --- Source/Editor/Modules/WindowsModule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 9b80b664c..0a19e7380 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -276,8 +276,8 @@ namespace FlaxEditor.Modules // Get metadata int version = int.Parse(root.Attributes["Version"].Value, CultureInfo.InvariantCulture); var virtualDesktopBounds = Platform.VirtualDesktopBounds; - var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location + new Vector2(0, 23); // 23 is a window strip size - var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight - new Vector2(50, 50); // apply some safe area + var virtualDesktopSafeLeftCorner = virtualDesktopBounds.Location; + var virtualDesktopSafeRightCorner = virtualDesktopBounds.BottomRight; switch (version) { From 419ca4e6303ff2fd65381bd6889300add5b24473 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sun, 10 Jan 2021 09:56:10 +0100 Subject: [PATCH 051/419] Reduce width of mask nodes --- Source/Editor/Surface/Archetypes/Packing.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs index 04f3819ea..f5b466ab3 100644 --- a/Source/Editor/Surface/Archetypes/Packing.cs +++ b/Source/Editor/Surface/Archetypes/Packing.cs @@ -486,7 +486,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack X component from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -500,7 +500,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack Y component from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -514,7 +514,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack Z component from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -528,7 +528,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack W component from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -544,7 +544,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack XY components from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -558,7 +558,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack XZ components from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -572,7 +572,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack YZ components from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), @@ -588,7 +588,7 @@ namespace FlaxEditor.Surface.Archetypes Description = "Unpack XYZ components from Vector", Flags = NodeFlags.AllGraphs, ConnectionsHints = ConnectionsHint.Vector, - Size = new Vector2(160, 30), + Size = new Vector2(110, 30), Elements = new[] { NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), From 7112160de509982dcb1cc729e17c293da57432a7 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Mon, 11 Jan 2021 13:00:24 +0100 Subject: [PATCH 052/419] Fix dragging existing connection Undo stack --- Source/Editor/Surface/Elements/Box.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 9be0dfd97..b8d88741f 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -97,7 +97,7 @@ namespace FlaxEditor.Surface.Elements var connections = Connections.ToArray(); for (int i = 0; i < connections.Length; i++) { - var targetBox = Connections[i]; + var targetBox = connections[i]; // Break connection Connections.Remove(targetBox); @@ -568,9 +568,19 @@ namespace FlaxEditor.Surface.Elements { if (!IsOutput && HasSingleConnection) { - var inputBox = Connections[0]; - BreakConnection(inputBox); - Surface.ConnectingStart(inputBox); + var connectedBox = Connections[0]; + if (Surface.Undo != null) + { + var action = new ConnectBoxesAction((InputBox)this, (OutputBox)connectedBox, false); + BreakConnection(connectedBox); + action.End(); + Surface.Undo.AddAction(action); + } + else + { + BreakConnection(connectedBox); + } + Surface.ConnectingStart(connectedBox); } else { From f0f18631240e6b2a12dc9fa1f843534f3fb9c711 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Jan 2021 14:58:46 +0100 Subject: [PATCH 053/419] Fix code style and move Camera Orientation widget below --- Source/Editor/Viewport/EditorViewport.cs | 39 ++++++++++++------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 99cb8cb92..0b810e891 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -510,17 +510,6 @@ namespace FlaxEditor.Viewport debugView.VisibleChanged += WidgetViewModeShowHide; } - // Camera Orientation - { - var cameraView = ViewWidgetButtonMenu.AddChildMenu("Camera Orientation").ContextMenu; - for (int i = 0; i < EditorViewportCameraOrientationValues.Length; i++) - { - var co = EditorViewportCameraOrientationValues[i]; - var button = cameraView.AddButton(co.Name); - button.Tag = co.Orientation; - } - cameraView.ButtonClicked += button => ViewOrientation = Quaternion.Euler((Vector3)button.Tag); - } ViewWidgetButtonMenu.AddSeparator(); // Orthographic @@ -539,6 +528,18 @@ namespace FlaxEditor.Viewport ViewWidgetButtonMenu.VisibleChanged += control => orthoValue.Checked = _isOrtho; } + // Cara Orientation + { + var cameraView = ViewWidgetButtonMenu.AddChildMenu("Orientation").ContextMenu; + for (int i = 0; i < EditorViewportCameraOrientationValues.Length; i++) + { + var co = EditorViewportCameraOrientationValues[i]; + var button = cameraView.AddButton(co.Name); + button.Tag = co.Orientation; + } + cameraView.ButtonClicked += button => ViewOrientation = Quaternion.Euler((Vector3)button.Tag); + } + // Field of View { var fov = ViewWidgetButtonMenu.AddButton("Field Of View"); @@ -1215,17 +1216,17 @@ namespace FlaxEditor.Viewport Orientation = orientation; } } - + private readonly CameraOrientation[] EditorViewportCameraOrientationValues = { - new CameraOrientation("Front", new Vector3(0,0,0)), - new CameraOrientation("Back", new Vector3(0,180,0)), - new CameraOrientation("Left", new Vector3(0,90,0)), - new CameraOrientation("Right", new Vector3(0,-90,0)), - new CameraOrientation("Top", new Vector3(-90,0,0)), - new CameraOrientation("Bottom", new Vector3(90,0,0)) + new CameraOrientation("Front", new Vector3(0, 0, 0)), + new CameraOrientation("Back", new Vector3(0, 180, 0)), + new CameraOrientation("Left", new Vector3(0, 90, 0)), + new CameraOrientation("Right", new Vector3(0, -90, 0)), + new CameraOrientation("Top", new Vector3(-90, 0, 0)), + new CameraOrientation("Bottom", new Vector3(90, 0, 0)) }; - + private readonly float[] EditorViewportCameraSpeedValues = { 0.1f, From 1ca31224e2dfb28038671e8e8bbe95e9737f976a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Jan 2021 15:03:23 +0100 Subject: [PATCH 054/419] Fix code style --- Source/Editor/Modules/WindowsModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 0a19e7380..a8058b9d8 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -971,7 +971,7 @@ namespace FlaxEditor.Modules } #region Window Events - + private void OnEditorStateChanged() { for (int i = 0; i < Windows.Count; i++) From a664e277724e942046d58dbb8f86d844fc97b41c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Jan 2021 15:13:47 +0100 Subject: [PATCH 055/419] Add missing event unregister for foliage/terrain tabs --- Source/Editor/Tools/Foliage/FoliageTab.cs | 17 ++++++++++++++--- Source/Editor/Tools/Terrain/CarveTab.cs | 19 +++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index bbad9018b..fe4120035 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -100,7 +100,7 @@ namespace FlaxEditor.Tools.Foliage public FoliageTab(SpriteHandle icon, Editor editor) : base(string.Empty, icon) { - Level.SceneLoaded += this.OnSceneLoaded; + Level.SceneLoaded += OnSceneLoaded; Editor = editor; Editor.SceneEditing.SelectionChanged += OnSelectionChanged; @@ -152,7 +152,7 @@ namespace FlaxEditor.Tools.Foliage { _createNewFoliage.Enabled = true; - Level.SceneUnloaded += this.OnSceneUnloaded; + Level.SceneUnloaded += OnSceneUnloaded; Level.SceneLoaded -= OnSceneLoaded; } @@ -161,7 +161,7 @@ namespace FlaxEditor.Tools.Foliage _createNewFoliage.Enabled = false; Level.SceneLoaded += OnSceneLoaded; - Level.SceneUnloaded -= this.OnSceneUnloaded; + Level.SceneUnloaded -= OnSceneUnloaded; } private void OnSelected(Tab tab) @@ -267,5 +267,16 @@ namespace FlaxEditor.Tools.Foliage { SelectedFoliageTypesChanged?.Invoke(); } + + /// + public override void OnDestroy() + { + if (_createNewFoliage.Enabled) + Level.SceneUnloaded -= OnSceneUnloaded; + else + Level.SceneLoaded -= OnSceneLoaded; + + base.OnDestroy(); + } } } diff --git a/Source/Editor/Tools/Terrain/CarveTab.cs b/Source/Editor/Tools/Terrain/CarveTab.cs index 37ce82c1f..e7b880414 100644 --- a/Source/Editor/Tools/Terrain/CarveTab.cs +++ b/Source/Editor/Tools/Terrain/CarveTab.cs @@ -58,7 +58,7 @@ namespace FlaxEditor.Tools.Terrain public CarveTab(SpriteHandle icon, Editor editor) : base(string.Empty, icon) { - Level.SceneLoaded += this.OnSceneLoaded; + Level.SceneLoaded += OnSceneLoaded; Editor = editor; Editor.SceneEditing.SelectionChanged += OnSelectionChanged; @@ -105,12 +105,12 @@ namespace FlaxEditor.Tools.Terrain }; _createTerrainButton.Clicked += OnCreateNewTerrainClicked; } - + private void OnSceneLoaded(Scene arg1, Guid arg2) { _createTerrainButton.Enabled = true; - Level.SceneUnloaded += this.OnSceneUnloaded; + Level.SceneUnloaded += OnSceneUnloaded; Level.SceneLoaded -= OnSceneLoaded; } @@ -119,7 +119,7 @@ namespace FlaxEditor.Tools.Terrain _createTerrainButton.Enabled = false; Level.SceneLoaded += OnSceneLoaded; - Level.SceneUnloaded -= this.OnSceneUnloaded; + Level.SceneUnloaded -= OnSceneUnloaded; } private void OnSelected(Tab tab) @@ -202,5 +202,16 @@ namespace FlaxEditor.Tools.Terrain default: throw new IndexOutOfRangeException("Invalid carve tab mode."); } } + + /// + public override void OnDestroy() + { + if (_createTerrainButton.Enabled) + Level.SceneUnloaded -= OnSceneUnloaded; + else + Level.SceneLoaded -= OnSceneLoaded; + + base.OnDestroy(); + } } } From b446790438120368328609105c3c586b4a1977c3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Jan 2021 15:38:22 +0100 Subject: [PATCH 056/419] Update shaders --- Content/Shaders/BakeLightmap.flax | 2 +- Content/Shaders/BitonicSort.flax | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content/Shaders/BakeLightmap.flax b/Content/Shaders/BakeLightmap.flax index 6e813205f..fac992469 100644 --- a/Content/Shaders/BakeLightmap.flax +++ b/Content/Shaders/BakeLightmap.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17295767d488dc2b64a1794ceda8b4d68f20dd2c5f1a8fdbe6f7940a070f9724 +oid sha256:cc4b141137661d995ff571191150c5997fa6f6576572b5c2281c395a12772d7c size 16095 diff --git a/Content/Shaders/BitonicSort.flax b/Content/Shaders/BitonicSort.flax index c8a9e901c..d5964fca9 100644 --- a/Content/Shaders/BitonicSort.flax +++ b/Content/Shaders/BitonicSort.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8371a1f4e0631e69f6dc6dbb8e11c8618351a7083293078270d60599f739e9f -size 6725 +oid sha256:df704c63770ee2b641f000726a3ec93c189685ffa149e484e961c9dba522baaf +size 6721 From ab4195ab6cc7d47613127f73b1250b7e3522c8fa Mon Sep 17 00:00:00 2001 From: stefnotch Date: Mon, 11 Jan 2021 16:23:02 +0100 Subject: [PATCH 057/419] Fix right clicking on Visject boxes Previously, the primary context menu would get triggered when right clicking on a box, causing some issues --- Source/Editor/Surface/VisjectSurface.Input.cs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index d1f394f3b..510ac1939 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -99,18 +99,6 @@ namespace FlaxEditor.Surface /// public event Window.MouseWheelDelegate CustomMouseWheel; - /// - /// Gets the node under the mouse location. - /// - /// The node or null if no intersection. - public SurfaceNode GetNodeUnderMouse() - { - var pos = _rootControl.PointFromParent(ref _mousePos); - if (_rootControl.GetChildAt(pos) is SurfaceNode node) - return node; - return null; - } - /// /// Gets the control under the mouse location. /// @@ -118,9 +106,7 @@ namespace FlaxEditor.Surface public SurfaceControl GetControlUnderMouse() { var pos = _rootControl.PointFromParent(ref _mousePos); - if (_rootControl.GetChildAtRecursive(pos) is SurfaceControl control) - return control; - return null; + return _rootControl.GetChildAt(pos) as SurfaceControl; } private void UpdateSelectionRectangle() From 6252c111b42cb6564b207d25f22b296dbb574f8c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 11 Jan 2021 16:38:12 +0100 Subject: [PATCH 058/419] Tweak BatteryInfo --- Source/Engine/Audio/AudioSource.h | 2 +- Source/Engine/Platform/BatteryInfo.h | 58 ++++++++++--------- .../Engine/Platform/Win32/Win32Platform.cpp | 10 +++- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/Source/Engine/Audio/AudioSource.h b/Source/Engine/Audio/AudioSource.h index ac15b5a4b..2b5b7e9d8 100644 --- a/Source/Engine/Audio/AudioSource.h +++ b/Source/Engine/Audio/AudioSource.h @@ -199,7 +199,7 @@ public: /// Gets the current state of the audio playback (playing/paused/stopped). /// /// The value. - API_PROPERTY() FORCE_INLINE States GetState() const + API_PROPERTY() FORCE_INLINE AudioSource::States GetState() const { return _state; } diff --git a/Source/Engine/Platform/BatteryInfo.h b/Source/Engine/Platform/BatteryInfo.h index 146f3f562..2ed6b5cac 100644 --- a/Source/Engine/Platform/BatteryInfo.h +++ b/Source/Engine/Platform/BatteryInfo.h @@ -5,43 +5,45 @@ #include "Engine/Core/Types/BaseTypes.h" /// -/// Power supply status. -/// -API_ENUM() enum class ACLineStatus : byte -{ - /// - /// Power supply is not connected. - /// - Offline = 0, - /// - /// Power supply is connected. - /// - Online = 1, - /// - /// Unknown status. - /// - Unknown = 255 -}; - -/// -/// Contains information about power supply (Battery). +/// Contains information about power supply (battery). /// API_STRUCT() struct BatteryInfo { DECLARE_SCRIPTING_TYPE_MINIMAL(BatteryInfo); - /// - /// Power supply status. - /// - API_FIELD() ACLineStatus ACLineStatus; + /// + /// Power supply status. + /// + API_ENUM() enum class States + { + /// + /// Unknown status. + /// + Unknown, + + /// + /// Power supply is connected and battery is charging. + /// + BatteryCharging, + + /// + /// Device is running on a battery. + /// + BatteryDischarging, + + /// + /// Device is connected to the stable power supply (AC). + /// + Connected, + }; /// - /// Battery percentage left. + /// Power supply state. /// - API_FIELD() byte BatteryLifePercent; + API_FIELD() BatteryInfo::States State = BatteryInfo::States::Unknown; /// - /// Remaining battery life time in second. + /// Battery percentage left (normalized to 0-1 range). /// - API_FIELD() uint32 BatteryLifeTime; + API_FIELD() float BatteryLifePercent = 1.0f; }; diff --git a/Source/Engine/Platform/Win32/Win32Platform.cpp b/Source/Engine/Platform/Win32/Win32Platform.cpp index 5e2aa4598..129ff4249 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.cpp +++ b/Source/Engine/Platform/Win32/Win32Platform.cpp @@ -315,9 +315,13 @@ BatteryInfo Win32Platform::GetBatteryInfo() BatteryInfo info; SYSTEM_POWER_STATUS status; GetSystemPowerStatus(&status); - info.ACLineStatus = (ACLineStatus)status.ACLineStatus; - info.BatteryLifePercent = status.BatteryLifePercent; - info.BatteryLifeTime = status.BatteryLifeTime; + info.BatteryLifePercent = (float)status.BatteryLifePercent / 255.0f; + if (status.BatteryFlag & 8) + info.State = BatteryInfo::States::BatteryCharging; + else if (status.BatteryFlag & 1 || status.BatteryFlag & 2 || status.BatteryFlag & 4) + info.State = BatteryInfo::States::BatteryDischarging; + else if (status.ACLineStatus == 1 || status.BatteryFlag & 128) + info.State = BatteryInfo::States::Connected; return info; } From 5679d86683ecd6de16b0d97717d1e953ab49d41e Mon Sep 17 00:00:00 2001 From: stefnotch Date: Tue, 12 Jan 2021 12:04:30 +0100 Subject: [PATCH 059/419] Fix tracking mouse offset on high DPI screens --- Source/Engine/UI/GUI/WindowRootControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/WindowRootControl.cs b/Source/Engine/UI/GUI/WindowRootControl.cs index 23330ffae..02c996b68 100644 --- a/Source/Engine/UI/GUI/WindowRootControl.cs +++ b/Source/Engine/UI/GUI/WindowRootControl.cs @@ -151,7 +151,7 @@ namespace FlaxEngine.GUI } /// - public override Vector2 TrackingMouseOffset => _window.TrackingMouseOffset; + public override Vector2 TrackingMouseOffset => _window.TrackingMouseOffset / _window._dpiScale; /// public override WindowRootControl RootWindow => this; From d20cbf434fb732182c936bc557e8dec618f6a437 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 13:49:26 +0100 Subject: [PATCH 060/419] Add default values initialization for structures and new array entries when resizing --- .../CustomEditors/Editors/ArrayEditor.cs | 10 +++++ .../CustomEditors/Editors/ListEditor.cs | 2 +- Source/Editor/Scripting/TypeUtils.cs | 12 +++++- Source/Editor/Utilities/Utils.cs | 40 +++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ArrayEditor.cs b/Source/Editor/CustomEditors/Editors/ArrayEditor.cs index f12dadde1..335fd64a7 100644 --- a/Source/Editor/CustomEditors/Editors/ArrayEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ArrayEditor.cs @@ -2,6 +2,7 @@ using System; using System.Collections; +using FlaxEditor.Scripting; using FlaxEngine; namespace FlaxEditor.CustomEditors.Editors @@ -48,6 +49,15 @@ namespace FlaxEditor.CustomEditors.Editors Array.Copy(array, oldSize - 1, newValues, i, 1); } } + else if (newSize > 0) + { + // Initialize new entries with default values + var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType)); + for (int i = 0; i < newSize; i++) + { + newValues.SetValue(defaultValue, i); + } + } SetValue(newValues); } diff --git a/Source/Editor/CustomEditors/Editors/ListEditor.cs b/Source/Editor/CustomEditors/Editors/ListEditor.cs index 305d2e2c9..19ed9675b 100644 --- a/Source/Editor/CustomEditors/Editors/ListEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ListEditor.cs @@ -58,7 +58,7 @@ namespace FlaxEditor.CustomEditors.Editors } else if (newSize > 0) { - // Fill new entries + // Fill new entries with default value var defaultValue = Scripting.TypeUtils.GetDefaultValue(ElementType); for (int i = oldSize; i < newSize; i++) { diff --git a/Source/Editor/Scripting/TypeUtils.cs b/Source/Editor/Scripting/TypeUtils.cs index 70108f06f..60f64ab8e 100644 --- a/Source/Editor/Scripting/TypeUtils.cs +++ b/Source/Editor/Scripting/TypeUtils.cs @@ -59,11 +59,19 @@ namespace FlaxEditor.Scripting if (type.Type == typeof(MaterialSceneTextures)) return MaterialSceneTextures.BaseColor; if (type.IsValueType) - return type.CreateInstance(); + { + var value = type.CreateInstance(); + Utilities.Utils.InitDefaultValues(value); + return value; + } if (new ScriptType(typeof(object)).IsAssignableFrom(type)) return null; if (type.CanCreateInstance) - return type.CreateInstance(); + { + var value = type.CreateInstance(); + Utilities.Utils.InitDefaultValues(value); + return value; + } throw new NotSupportedException("Cannot create default value for type " + type); } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 5f1636239..cd9935da3 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Reflection; using System.Runtime.InteropServices; using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; @@ -1732,5 +1733,44 @@ namespace FlaxEditor.Utilities distance = 0; return false; } + + /// + /// Initializes the object fields and properties with their default values based on . + /// + /// The object. + public static void InitDefaultValues(object obj) + { + var scriptType = TypeUtils.GetObjectType(obj); + if (!scriptType) + return; + var isStructure = scriptType.IsStructure; + + var fields = scriptType.GetFields(BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public); + for (var i = 0; i < fields.Length; i++) + { + var field = fields[i]; + var attr = field.GetAttribute(); + if (attr != null) + { + field.SetValue(obj, attr.Value); + } + else if (isStructure) + { + // C# doesn't support default values for structure members so initialize them + field.SetValue(obj, TypeUtils.GetDefaultValue(field.ValueType)); + } + } + + var properties = scriptType.GetProperties(BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public); + for (var i = 0; i < properties.Length; i++) + { + var property = properties[i]; + var attr = property.GetAttribute(); + if (attr != null) + { + property.SetValue(obj, attr.Value); + } + } + } } } From a6d5fb318afd453a01257b1463f84a86c5a7e561 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 14:14:08 +0100 Subject: [PATCH 061/419] Fix default field value parsing to skip whitespaces --- .../Bindings/BindingsGenerator.Parsing.cs | 2 +- Source/Tools/Flax.Build/Utilities/Tokenizer.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 916916995..1c4cadb4a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -1038,7 +1038,7 @@ namespace Flax.Build.Bindings var token = context.Tokenizer.ExpectAnyTokens(new[] { TokenType.SemiColon, TokenType.Equal, TokenType.LeftBracket, TokenType.Colon }); if (token.Type == TokenType.Equal) { - context.Tokenizer.SkipUntil(TokenType.SemiColon, out desc.DefaultValue); + context.Tokenizer.SkipUntil(TokenType.SemiColon, out desc.DefaultValue, false); } else if (token.Type == TokenType.LeftBracket) { diff --git a/Source/Tools/Flax.Build/Utilities/Tokenizer.cs b/Source/Tools/Flax.Build/Utilities/Tokenizer.cs index 6913ec31a..e73ddcbca 100644 --- a/Source/Tools/Flax.Build/Utilities/Tokenizer.cs +++ b/Source/Tools/Flax.Build/Utilities/Tokenizer.cs @@ -460,6 +460,24 @@ namespace Flax.Build } } + /// + /// Skips all tokens until the tokenizer steps into token of given type (and it is also skipped, so, NextToken will give the next token). + /// + /// The expected token type. + /// The output contents of the skipped tokens. + /// When false, all white-space tokens will be ignored. + public void SkipUntil(TokenType tokenType, out string context, bool includeWhitespaces) + { + context = string.Empty; + while (NextToken(true).Type != tokenType) + { + var token = CurrentToken; + if (!includeWhitespaces && (token.Type == TokenType.Newline || token.Type == TokenType.Whitespace)) + continue; + context += token.Value; + } + } + /// /// Disposes the . /// From 40c7fe3f778584ffbbddb7d3916fac038ddd7248 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 14:14:35 +0100 Subject: [PATCH 062/419] Add support for automatic DefaultValue attributes generation for fields in scripting API --- .../Bindings/BindingsGenerator.CSharp.cs | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 2c3e1c837..a25e00fbb 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -394,14 +394,36 @@ namespace Flax.Build.Bindings } } - private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, string attributes, string[] comment, bool canUseTooltip, bool useUnmanaged) + private static bool IsDefaultValueSupported(string value) + { + // TEXT macro (eg. TEXT("text")) + // TODO: support string for default value attribute + if (value.Contains("TEXT(\"")) + return false; + + // Value constructors (eg. Vector2(1, 2)) + // TODO: support value constructores for default value attribute + if (value.Contains('(') && value.Contains(')')) + return false; + + // Constants (eg. Vector2::Zero) + // TODO: support constants for default value attribute + if (value.Contains("::")) + return false; + + return true; + } + + private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, string attributes, string[] comment, bool canUseTooltip, bool useUnmanaged, string defaultValue = null) { var writeTooltip = true; + var writeDefaultValue = true; if (!string.IsNullOrEmpty(attributes)) { // Write attributes contents.Append(indent).Append('[').Append(attributes).Append(']').AppendLine(); writeTooltip = !attributes.Contains("Tooltip(") && !attributes.Contains("HideInEditor"); + writeDefaultValue = !attributes.Contains("DefaultValue("); } if (useUnmanaged) @@ -425,16 +447,22 @@ namespace Flax.Build.Bindings contents.Append(indent).Append("[Tooltip(\"").Append(tooltip).Append("\")]").AppendLine(); } } + if (!string.IsNullOrEmpty(defaultValue) && writeDefaultValue && IsDefaultValueSupported(defaultValue)) + { + // Write default value attribute + defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, defaultValue, apiTypeInfo); + contents.Append(indent).Append("[DefaultValue(").Append(defaultValue).Append(")]").AppendLine(); + } } - private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, bool useUnmanaged) + private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, bool useUnmanaged, string defaultValue = null) { - GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo.Attributes, apiTypeInfo.Comment, true, useUnmanaged); + GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo, apiTypeInfo.Attributes, apiTypeInfo.Comment, true, useUnmanaged, defaultValue); } - private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, MemberInfo memberInfo, bool useUnmanaged) + private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, MemberInfo memberInfo, bool useUnmanaged, string defaultValue = null) { - GenerateCSharpAttributes(buildData, contents, indent, memberInfo.Attributes, memberInfo.Comment, true, useUnmanaged); + GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo, memberInfo.Attributes, memberInfo.Comment, true, useUnmanaged, defaultValue); } private static void GenerateCSharpClass(BuildData buildData, StringBuilder contents, string indent, ClassInfo classInfo) @@ -506,7 +534,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); } - GenerateCSharpAttributes(buildData, contents, indent, eventInfo, useUnmanaged); + GenerateCSharpAttributes(buildData, contents, indent, classInfo, eventInfo, useUnmanaged); contents.Append(indent); if (eventInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -605,7 +633,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); } - GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, useUnmanaged); + GenerateCSharpAttributes(buildData, contents, indent, classInfo, fieldInfo, useUnmanaged, fieldInfo.DefaultValue); contents.Append(indent); if (fieldInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -672,7 +700,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); } - GenerateCSharpAttributes(buildData, contents, indent, propertyInfo, useUnmanaged); + GenerateCSharpAttributes(buildData, contents, indent, classInfo, propertyInfo, useUnmanaged); contents.Append(indent); if (propertyInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -725,7 +753,7 @@ namespace Flax.Build.Bindings { contents.AppendLine(); GenerateCSharpComment(contents, indent, functionInfo.Comment); - GenerateCSharpAttributes(buildData, contents, indent, functionInfo, true); + GenerateCSharpAttributes(buildData, contents, indent, classInfo, functionInfo, true); contents.Append(indent); if (functionInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -837,7 +865,7 @@ namespace Flax.Build.Bindings foreach (var comment in fieldInfo.Comment) contents.Append(indent).Append(comment).AppendLine(); - GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, fieldInfo.IsStatic); + GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic, fieldInfo.DefaultValue); contents.Append(indent); if (fieldInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -861,7 +889,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); foreach (var comment in fieldInfo.Comment) contents.Append(indent).Append(comment).AppendLine(); - GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, fieldInfo.IsStatic); + GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic); contents.Append(indent); if (fieldInfo.Access == AccessLevel.Public) contents.Append("public "); @@ -967,7 +995,7 @@ namespace Flax.Build.Bindings foreach (var comment in entryInfo.Comment) contents.Append(indent).Append(comment).AppendLine(); - GenerateCSharpAttributes(buildData, contents, indent, entryInfo.Attributes, entryInfo.Comment, true, false); + GenerateCSharpAttributes(buildData, contents, indent, enumInfo, entryInfo.Attributes, entryInfo.Comment, true, false); contents.Append(indent).Append(entryInfo.Name); if (!string.IsNullOrEmpty(entryInfo.Value)) contents.Append(" = ").Append(entryInfo.Value); From dba43c4e9fbb5f6007f6ad82cb45d5c8af7fc03b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 15:33:17 +0100 Subject: [PATCH 063/419] Add support for using `ObsoleteAttribute` to upgrade old C# asset/script data format after refactor --- .../JsonCustomSerializers/ExtendedDefaultContractResolver.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs index f6a62dd54..cc07597e8 100644 --- a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs +++ b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs @@ -118,9 +118,11 @@ namespace FlaxEngine.Json.JsonCustomSerializers if (noSerialize) continue; + var isObsolete = attributes.Any(x => x is ObsoleteAttribute); + var jsonProperty = CreateProperty(p, memberSerialization); jsonProperty.Writable = true; - jsonProperty.Readable = true; + jsonProperty.Readable = !isObsolete; if (_flaxType.IsAssignableFrom(p.PropertyType)) { From fd268f4e154b60d5e0d0be6759f344bf562eaa93 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 19:00:54 +0100 Subject: [PATCH 064/419] Add missing FLAXENGINE_API expose macro to engine math types --- Source/Engine/Core/Math/AABB.h | 2 +- Source/Engine/Core/Math/BoundingFrustum.h | 2 +- Source/Engine/Core/Math/Half.h | 12 ++++-------- Source/Engine/Core/Math/Matrix3x3.h | 2 +- Source/Engine/Core/Math/OrientedBoundingBox.h | 2 +- Source/Engine/Core/Math/Packed.h | 8 ++++---- Source/Engine/Core/Math/Plane.h | 2 +- Source/Engine/Core/Math/Ray.h | 2 +- Source/Engine/Core/Math/Rectangle.h | 1 - Source/Engine/Core/Math/Transform.h | 3 +-- Source/Engine/Core/Math/Triangle.h | 2 +- Source/Engine/Core/Math/VectorInt.h | 6 +++--- Source/Engine/Core/Math/Viewport.h | 2 +- 13 files changed, 20 insertions(+), 26 deletions(-) diff --git a/Source/Engine/Core/Math/AABB.h b/Source/Engine/Core/Math/AABB.h index 39e8a5bf6..5853d6597 100644 --- a/Source/Engine/Core/Math/AABB.h +++ b/Source/Engine/Core/Math/AABB.h @@ -8,7 +8,7 @@ /// /// Integer axis aligned bounding box /// -struct AABB +struct FLAXENGINE_API AABB { public: diff --git a/Source/Engine/Core/Math/BoundingFrustum.h b/Source/Engine/Core/Math/BoundingFrustum.h index b8b16c412..9052521fc 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.h +++ b/Source/Engine/Core/Math/BoundingFrustum.h @@ -10,7 +10,7 @@ /// /// Defines a frustum which can be used in frustum culling, zoom to Extents (zoom to fit) operations, (matrix, frustum, camera) interchange, and many kind of intersection testing. /// -API_STRUCT(InBuild) struct BoundingFrustum +API_STRUCT(InBuild) struct FLAXENGINE_API BoundingFrustum { private: diff --git a/Source/Engine/Core/Math/Half.h b/Source/Engine/Core/Math/Half.h index 19059ad0c..7b86fd7a7 100644 --- a/Source/Engine/Core/Math/Half.h +++ b/Source/Engine/Core/Math/Half.h @@ -11,7 +11,7 @@ typedef uint16 Half; #define USE_SSE_HALF_CONVERSION 0 -class Float16Compressor +class FLAXENGINE_API Float16Compressor { union Bits { @@ -108,7 +108,7 @@ inline Half ConvertFloatToHalf(const float value) /// /// Defines a two component vector, using half precision floating point coordinates. /// -struct Half2 +struct FLAXENGINE_API Half2 { public: @@ -167,7 +167,7 @@ public: /// /// Defines a three component vector, using half precision floating point coordinates. /// -struct Half3 +struct FLAXENGINE_API Half3 { public: @@ -216,7 +216,7 @@ public: /// /// Defines a four component vector, using half precision floating point coordinates. /// -struct Half4 +struct FLAXENGINE_API Half4 { public: @@ -270,16 +270,12 @@ public: } explicit Half4(const Vector4& v); - explicit Half4(const Color& c); - explicit Half4(const Rectangle& rect); public: Vector2 ToVector2() const; - Vector3 ToVector3() const; - Vector4 ToVector4() const; }; diff --git a/Source/Engine/Core/Math/Matrix3x3.h b/Source/Engine/Core/Math/Matrix3x3.h index 3cf690259..b28be5437 100644 --- a/Source/Engine/Core/Math/Matrix3x3.h +++ b/Source/Engine/Core/Math/Matrix3x3.h @@ -9,7 +9,7 @@ /// /// Represents a 3x3 mathematical matrix. /// -API_STRUCT(InBuild) struct Matrix3x3 +API_STRUCT(InBuild) struct FLAXENGINE_API Matrix3x3 { public: diff --git a/Source/Engine/Core/Math/OrientedBoundingBox.h b/Source/Engine/Core/Math/OrientedBoundingBox.h index 03256633d..d19108f84 100644 --- a/Source/Engine/Core/Math/OrientedBoundingBox.h +++ b/Source/Engine/Core/Math/OrientedBoundingBox.h @@ -8,7 +8,7 @@ #include "CollisionsHelper.h" // Oriented Bounding Box (OBB) is a rectangular block, much like an AABB (Bounding Box) but with an arbitrary orientation in 3D space. -API_STRUCT(InBuild) struct OrientedBoundingBox +API_STRUCT(InBuild) struct FLAXENGINE_API OrientedBoundingBox { public: diff --git a/Source/Engine/Core/Math/Packed.h b/Source/Engine/Core/Math/Packed.h index ec666bf61..4f41dac9c 100644 --- a/Source/Engine/Core/Math/Packed.h +++ b/Source/Engine/Core/Math/Packed.h @@ -13,7 +13,7 @@ typedef Half Float16; /// /// Packed vector, layout: R:10 bytes, G:10 bytes, B:10 bytes, A:2 bytes, all values are stored as floats in range [0;1]. /// -struct Float1010102 +struct FLAXENGINE_API Float1010102 { union { @@ -64,7 +64,7 @@ public: }; // The 3D vector is packed into 32 bits with 11/11/10 bits per floating-point component. -struct FloatR11G11B10 +struct FLAXENGINE_API FloatR11G11B10 { union { @@ -118,7 +118,7 @@ public: Vector3 ToVector3() const; }; -struct RG16UNorm +struct FLAXENGINE_API RG16UNorm { uint16 X, Y; @@ -131,7 +131,7 @@ struct RG16UNorm Vector2 ToVector2() const; }; -struct RGBA16UNorm +struct FLAXENGINE_API RGBA16UNorm { uint16 X, Y, Z, W; diff --git a/Source/Engine/Core/Math/Plane.h b/Source/Engine/Core/Math/Plane.h index 2ca34258f..2fbe7438c 100644 --- a/Source/Engine/Core/Math/Plane.h +++ b/Source/Engine/Core/Math/Plane.h @@ -8,7 +8,7 @@ /// /// Represents a plane in three dimensional space. /// -API_STRUCT() struct Plane +API_STRUCT() struct FLAXENGINE_API Plane { DECLARE_SCRIPTING_TYPE_MINIMAL(Plane); public: diff --git a/Source/Engine/Core/Math/Ray.h b/Source/Engine/Core/Math/Ray.h index 56d06a21e..f125cba97 100644 --- a/Source/Engine/Core/Math/Ray.h +++ b/Source/Engine/Core/Math/Ray.h @@ -11,7 +11,7 @@ struct Viewport; /// /// Represents a three dimensional line based on a point in space and a direction. /// -API_STRUCT() struct Ray +API_STRUCT() struct FLAXENGINE_API Ray { DECLARE_SCRIPTING_TYPE_MINIMAL(Ray); public: diff --git a/Source/Engine/Core/Math/Rectangle.h b/Source/Engine/Core/Math/Rectangle.h index 305ae70e1..f2dcbd20a 100644 --- a/Source/Engine/Core/Math/Rectangle.h +++ b/Source/Engine/Core/Math/Rectangle.h @@ -10,7 +10,6 @@ API_STRUCT() struct FLAXENGINE_API Rectangle { DECLARE_SCRIPTING_TYPE_MINIMAL(Rectangle); -public: /// /// The empty rectangle. diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index 3b8cf71b7..ed19efeae 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -11,10 +11,9 @@ struct Matrix; /// /// Describes transformation in a 3D space. /// -API_STRUCT() struct Transform +API_STRUCT() struct FLAXENGINE_API Transform { DECLARE_SCRIPTING_TYPE_MINIMAL(Transform); -public: /// /// The translation vector of the transform. diff --git a/Source/Engine/Core/Math/Triangle.h b/Source/Engine/Core/Math/Triangle.h index 03f91ece3..b0f7ea09d 100644 --- a/Source/Engine/Core/Math/Triangle.h +++ b/Source/Engine/Core/Math/Triangle.h @@ -8,7 +8,7 @@ /// /// Represents a three dimensional triangle. /// -struct Triangle +struct FLAXENGINE_API Triangle { public: diff --git a/Source/Engine/Core/Math/VectorInt.h b/Source/Engine/Core/Math/VectorInt.h index 80804c39a..9b5eeb866 100644 --- a/Source/Engine/Core/Math/VectorInt.h +++ b/Source/Engine/Core/Math/VectorInt.h @@ -13,7 +13,7 @@ struct Vector4; /// /// Two-components vector (32 bit integer type). /// -API_STRUCT(InBuild) struct Int2 +API_STRUCT(InBuild) struct FLAXENGINE_API Int2 { public: @@ -273,7 +273,7 @@ public: /// /// Three-components vector (32 bit integer type). /// -API_STRUCT(InBuild) struct Int3 +API_STRUCT(InBuild) struct FLAXENGINE_API Int3 { public: @@ -382,7 +382,7 @@ public: /// /// Four-components vector (32 bit integer type). /// -API_STRUCT(InBuild) struct Int4 +API_STRUCT(InBuild) struct FLAXENGINE_API Int4 { public: diff --git a/Source/Engine/Core/Math/Viewport.h b/Source/Engine/Core/Math/Viewport.h index 0b5871f2e..e30d085d3 100644 --- a/Source/Engine/Core/Math/Viewport.h +++ b/Source/Engine/Core/Math/Viewport.h @@ -10,7 +10,7 @@ struct Matrix; struct Rectangle; // Describes the viewport dimensions. -API_STRUCT(InBuild) struct Viewport +API_STRUCT(InBuild) struct FLAXENGINE_API Viewport { public: From 89f25516fc318b225dad491ed83b7507a9a66f38 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 19:05:17 +0100 Subject: [PATCH 065/419] Add inline for float16 compression and add code reference note --- Source/Engine/Content/Utilities/IESLoader.cpp | 2 +- .../Engine/ContentExporters/ExportModel.cpp | 4 +- Source/Engine/Core/Math/Half.cpp | 62 +++++++++---------- Source/Engine/Core/Math/Half.h | 57 +++++++---------- .../Engine/Tools/TextureTool/TextureTool.cpp | 4 +- 5 files changed, 60 insertions(+), 69 deletions(-) diff --git a/Source/Engine/Content/Utilities/IESLoader.cpp b/Source/Engine/Content/Utilities/IESLoader.cpp index 0ef7c320a..271ab3d5e 100644 --- a/Source/Engine/Content/Utilities/IESLoader.cpp +++ b/Source/Engine/Content/Utilities/IESLoader.cpp @@ -240,7 +240,7 @@ float IESLoader::ExtractInR16(Array& output) float result = 0.0f; for (uint32 i = 0; i < hAnglesCount; i++) result += InterpolateBilinear(static_cast(i), v); - *out++ = ConvertFloatToHalf(invMaxValue * result / (float)hAnglesCount); + *out++ = Float16Compressor::Compress(invMaxValue * result / (float)hAnglesCount); } } diff --git a/Source/Engine/ContentExporters/ExportModel.cpp b/Source/Engine/ContentExporters/ExportModel.cpp index efc13419b..7fdd011f9 100644 --- a/Source/Engine/ContentExporters/ExportModel.cpp +++ b/Source/Engine/ContentExporters/ExportModel.cpp @@ -76,7 +76,7 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context) for (uint32 i = 0; i < vertices; i++) { auto v = vb1[i].TexCoord; - output->WriteTextFormatted("vt {0} {1}\n", ConvertHalfToFloat(v.X), ConvertHalfToFloat(v.Y)); + output->WriteTextFormatted("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y)); } output->WriteChar('\n'); @@ -181,7 +181,7 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context for (uint32 i = 0; i < vertices; i++) { auto v = vb0[i].TexCoord; - output->WriteTextFormatted("vt {0} {1}\n", ConvertHalfToFloat(v.X), ConvertHalfToFloat(v.Y)); + output->WriteTextFormatted("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y)); } output->WriteChar('\n'); diff --git a/Source/Engine/Core/Math/Half.cpp b/Source/Engine/Core/Math/Half.cpp index 47bf66802..0de29c030 100644 --- a/Source/Engine/Core/Math/Half.cpp +++ b/Source/Engine/Core/Math/Half.cpp @@ -18,81 +18,81 @@ Half4 Half4::Zero(0, 0, 0, 0); Half2::Half2(const Vector2& v) { - X = ConvertFloatToHalf(v.X); - Y = ConvertFloatToHalf(v.Y); + X = Float16Compressor::Compress(v.X); + Y = Float16Compressor::Compress(v.Y); } Vector2 Half2::ToVector2() const { return Vector2( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y) ); } Half3::Half3(const Vector3& v) { - X = ConvertFloatToHalf(v.X); - Y = ConvertFloatToHalf(v.Y); - Z = ConvertFloatToHalf(v.Z); + X = Float16Compressor::Compress(v.X); + Y = Float16Compressor::Compress(v.Y); + Z = Float16Compressor::Compress(v.Z); } Vector3 Half3::ToVector3() const { return Vector3( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y), - ConvertHalfToFloat(Z) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y), + Float16Compressor::Decompress(Z) ); } Half4::Half4(const Vector4& v) { - X = ConvertFloatToHalf(v.X); - Y = ConvertFloatToHalf(v.Y); - Z = ConvertFloatToHalf(v.Z); - W = ConvertFloatToHalf(v.W); + X = Float16Compressor::Compress(v.X); + Y = Float16Compressor::Compress(v.Y); + Z = Float16Compressor::Compress(v.Z); + W = Float16Compressor::Compress(v.W); } Half4::Half4(const Color& c) { - X = ConvertFloatToHalf(c.R); - Y = ConvertFloatToHalf(c.G); - Z = ConvertFloatToHalf(c.B); - W = ConvertFloatToHalf(c.A); + X = Float16Compressor::Compress(c.R); + Y = Float16Compressor::Compress(c.G); + Z = Float16Compressor::Compress(c.B); + W = Float16Compressor::Compress(c.A); } Half4::Half4(const Rectangle& rect) { - X = ConvertFloatToHalf(rect.Location.X); - Y = ConvertFloatToHalf(rect.Location.Y); - Z = ConvertFloatToHalf(rect.Size.X); - W = ConvertFloatToHalf(rect.Size.Y); + X = Float16Compressor::Compress(rect.Location.X); + Y = Float16Compressor::Compress(rect.Location.Y); + Z = Float16Compressor::Compress(rect.Size.X); + W = Float16Compressor::Compress(rect.Size.Y); } Vector2 Half4::ToVector2() const { return Vector2( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y) ); } Vector3 Half4::ToVector3() const { return Vector3( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y), - ConvertHalfToFloat(Z) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y), + Float16Compressor::Decompress(Z) ); } Vector4 Half4::ToVector4() const { return Vector4( - ConvertHalfToFloat(X), - ConvertHalfToFloat(Y), - ConvertHalfToFloat(Z), - ConvertHalfToFloat(W) + Float16Compressor::Decompress(X), + Float16Compressor::Decompress(Y), + Float16Compressor::Decompress(Z), + Float16Compressor::Decompress(W) ); } diff --git a/Source/Engine/Core/Math/Half.h b/Source/Engine/Core/Math/Half.h index 7b86fd7a7..e0773b1d4 100644 --- a/Source/Engine/Core/Math/Half.h +++ b/Source/Engine/Core/Math/Half.h @@ -11,8 +11,14 @@ typedef uint16 Half; #define USE_SSE_HALF_CONVERSION 0 +/// +/// Utility for packing/unpacking floating point value from single precision (32 bit) to half precision (16 bit). +/// class FLAXENGINE_API Float16Compressor { + // Reference: + // http://www.cs.cmu.edu/~jinlianw/third_party/float16_compressor.hpp + union Bits { float f; @@ -22,24 +28,19 @@ class FLAXENGINE_API Float16Compressor static const int shift = 13; static const int shiftSign = 16; - static const int32 infN = 0x7F800000; // flt32 infinity static const int32 maxN = 0x477FE000; // max flt16 normal as a flt32 static const int32 minN = 0x38800000; // min flt16 normal as a flt32 static const int32 signN = 0x80000000; // flt32 sign bit - static const int32 infC = infN >> shift; static const int32 nanN = (infC + 1) << shift; // minimum flt16 nan as a flt32 static const int32 maxC = maxN >> shift; static const int32 minC = minN >> shift; static const int32 signC = signN >> shiftSign; // flt16 sign bit - static const int32 mulN = 0x52000000; // (1 << 23) / minN static const int32 mulC = 0x33800000; // minN / (1 << (23 - shift)) - static const int32 subC = 0x003FF; // max flt32 subnormal down shifted static const int32 norC = 0x00400; // min flt32 normal down shifted - static const int32 maxD = infC - maxC - 1; static const int32 minD = minC - subC - 1; @@ -48,9 +49,9 @@ public: static Half Compress(const float value) { #if USE_SSE_HALF_CONVERSION - __m128 V1 = _mm_set_ss(value); - __m128i V2 = _mm_cvtps_ph(V1, 0); - return static_cast(_mm_cvtsi128_si32(V2)); + __m128 value1 = _mm_set_ss(value); + __m128i value2 = _mm_cvtps_ph(value1, 0); + return static_cast(_mm_cvtsi128_si32(value2)); #else Bits v, s; v.f = value; @@ -72,9 +73,9 @@ public: static float Decompress(const Half value) { #if USE_SSE_HALF_CONVERSION - __m128i V1 = _mm_cvtsi32_si128(static_cast(value)); - __m128 V2 = _mm_cvtph_ps(V1); - return _mm_cvtss_f32(V2); + __m128i value1 = _mm_cvtsi32_si128(static_cast(value)); + __m128 value2 = _mm_cvtph_ps(value1); + return _mm_cvtss_f32(value2); #else Bits v; v.ui = value; @@ -95,16 +96,6 @@ public: } }; -inline float ConvertHalfToFloat(const Half value) -{ - return Float16Compressor::Decompress(value); -} - -inline Half ConvertFloatToHalf(const float value) -{ - return Float16Compressor::Compress(value); -} - /// /// Defines a two component vector, using half precision floating point coordinates. /// @@ -145,8 +136,8 @@ public: /// Y component Half2(float x, float y) { - X = ConvertFloatToHalf(x); - Y = ConvertFloatToHalf(y); + X = Float16Compressor::Compress(x); + Y = Float16Compressor::Compress(y); } /// @@ -201,9 +192,9 @@ public: Half3(const float x, const float y, const float z) { - X = ConvertFloatToHalf(x); - Y = ConvertFloatToHalf(y); - Z = ConvertFloatToHalf(z); + X = Float16Compressor::Compress(x); + Y = Float16Compressor::Compress(y); + Z = Float16Compressor::Compress(z); } Half3(const Vector3& v); @@ -255,18 +246,18 @@ public: Half4(const float x, const float y, const float z) { - X = ConvertFloatToHalf(x); - Y = ConvertFloatToHalf(y); - Z = ConvertFloatToHalf(z); + X = Float16Compressor::Compress(x); + Y = Float16Compressor::Compress(y); + Z = Float16Compressor::Compress(z); W = 0; } Half4(const float x, const float y, const float z, const float w) { - X = ConvertFloatToHalf(x); - Y = ConvertFloatToHalf(y); - Z = ConvertFloatToHalf(z); - W = ConvertFloatToHalf(w); + X = Float16Compressor::Compress(x); + Y = Float16Compressor::Compress(y); + Z = Float16Compressor::Compress(z); + W = Float16Compressor::Compress(w); } explicit Half4(const Vector4& v); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index 97e4df253..e6502bb6f 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -489,11 +489,11 @@ TextureTool::PixelFormatSampler PixelFormatSamplers[] = sizeof(Half), [](const void* ptr) { - return Color(ConvertHalfToFloat(*(Half*)ptr), 0, 0, 1); + return Color(Float16Compressor::Decompress(*(Half*)ptr), 0, 0, 1); }, [](const void* ptr, const Color& color) { - *(Half*)ptr = ConvertFloatToHalf(color.R); + *(Half*)ptr = Float16Compressor::Compress(color.R); }, }, { From d7696c4765cbc7b61195cdfaa9551ddfc25176c8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 19:09:22 +0100 Subject: [PATCH 066/419] Split VectorInt into separate files for Int2, Int3 and Int4 --- Source/Engine/Core/Math/Int2.h | 279 +++++++++++++++ Source/Engine/Core/Math/Int3.h | 128 +++++++ Source/Engine/Core/Math/Int4.h | 134 +++++++ Source/Engine/Core/Math/VectorInt.h | 518 +--------------------------- 4 files changed, 544 insertions(+), 515 deletions(-) create mode 100644 Source/Engine/Core/Math/Int2.h create mode 100644 Source/Engine/Core/Math/Int3.h create mode 100644 Source/Engine/Core/Math/Int4.h diff --git a/Source/Engine/Core/Math/Int2.h b/Source/Engine/Core/Math/Int2.h new file mode 100644 index 000000000..a6038d1b0 --- /dev/null +++ b/Source/Engine/Core/Math/Int2.h @@ -0,0 +1,279 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Math.h" +#include "Engine/Core/Formatting.h" +#include "Engine/Core/Templates.h" + +struct Vector2; +struct Vector3; +struct Vector4; + +/// +/// Two-components vector (32 bit integer type). +/// +API_STRUCT(InBuild) struct FLAXENGINE_API Int2 +{ +public: + + union + { + struct + { + // X component + int32 X; + + // Y component + int32 Y; + }; + + // Raw values + int32 Raw[2]; + }; + +public: + + // Vector with all components equal 0 + static const Int2 Zero; + + // Vector with all components equal 1 + static const Int2 One; + +public: + + /// + /// Empty constructor. + /// + Int2() + { + } + + // Init + // @param xy Value to assign to the all components + Int2(int32 xy) + : X(xy) + , Y(xy) + { + } + + // Init + // @param x X component value + // @param y Y component value + Int2(int32 x, int32 y) + : X(x) + , Y(y) + { + } + + // Init + // @param v Vector to use X and Y components + explicit Int2(const Vector2& v); + +public: + + String ToString() const; + +public: + + // Arithmetic operators with Int2 + + Int2 operator+(const Int2& b) const + { + return Add(*this, b); + } + + Int2 operator-(const Int2& b) const + { + return Subtract(*this, b); + } + + Int2 operator*(const Int2& b) const + { + return Multiply(*this, b); + } + + Int2 operator/(const Int2& b) const + { + return Divide(*this, b); + } + + Int2 operator-() const + { + return Int2(-X, -Y); + } + + // op= operators with Int2 + + Int2& operator+=(const Int2& b) + { + *this = Add(*this, b); + return *this; + } + + Int2& operator-=(const Int2& b) + { + *this = Subtract(*this, b); + return *this; + } + + Int2& operator*=(const Int2& b) + { + *this = Multiply(*this, b); + return *this; + } + + Int2& operator/=(const Int2& b) + { + *this = Divide(*this, b); + return *this; + } + + // Arithmetic operators with int32 + + Int2 operator+(int32 b) const + { + return Add(*this, b); + } + + Int2 operator-(int32 b) const + { + return Subtract(*this, b); + } + + Int2 operator*(int32 b) const + { + return Multiply(*this, b); + } + + Int2 operator/(int32 b) const + { + return Divide(*this, b); + } + + // op= operators with int32 + + Int2& operator+=(int32 b) + { + *this = Add(*this, b); + return *this; + } + + Int2& operator-=(int32 b) + { + *this = Subtract(*this, b); + return *this; + } + + Int2& operator*=(int32 b) + { + *this = Multiply(*this, b); + return *this; + } + + Int2& operator/=(int32 b) + { + *this = Divide(*this, b); + return *this; + } + + // Comparison operators + + bool operator==(const Int2& b) const + { + return X == b.X && Y == b.Y; + } + + bool operator!=(const Int2& b) const + { + return X != b.X || Y != b.Y; + } + + bool operator>(const Int2& b) const + { + return X > b.X && Y > b.Y; + } + + bool operator>=(const Int2& b) const + { + return X >= b.X && Y >= b.Y; + } + + bool operator<(const Int2& b) const + { + return X < b.X && Y < b.Y; + } + + bool operator<=(const Int2& b) const + { + return X <= b.X && Y <= b.Y; + } + +public: + + static void Add(const Int2& a, const Int2& b, Int2* result) + { + result->X = a.X + b.X; + result->Y = a.Y + b.Y; + } + + static Int2 Add(const Int2& a, const Int2& b) + { + Int2 result; + Add(a, b, &result); + return result; + } + + static void Subtract(const Int2& a, const Int2& b, Int2* result) + { + result->X = a.X - b.X; + result->Y = a.Y - b.Y; + } + + static Int2 Subtract(const Int2& a, const Int2& b) + { + Int2 result; + Subtract(a, b, &result); + return result; + } + + static Int2 Multiply(const Int2& a, const Int2& b) + { + return Int2(a.X * b.X, a.Y * b.Y); + } + + static Int2 Multiply(const Int2& a, int32 b) + { + return Int2(a.X * b, a.Y * b); + } + + static Int2 Divide(const Int2& a, const Int2& b) + { + return Int2(a.X / b.X, a.Y / b.Y); + } + + static Int2 Divide(const Int2& a, int32 b) + { + return Int2(a.X / b, a.Y / b); + } + + // Creates vector from minimum components of two vectors + static Int2 Min(const Int2& a, const Int2& b) + { + return Int2(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y); + } + + // Creates vector from maximum components of two vectors + static Int2 Max(const Int2& a, const Int2& b) + { + return Int2(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y); + } +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +DEFINE_DEFAULT_FORMATTING(Int2, "X:{0} Y:{1}", v.X, v.Y); diff --git a/Source/Engine/Core/Math/Int3.h b/Source/Engine/Core/Math/Int3.h new file mode 100644 index 000000000..6886acc6b --- /dev/null +++ b/Source/Engine/Core/Math/Int3.h @@ -0,0 +1,128 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Math.h" +#include "Engine/Core/Formatting.h" +#include "Engine/Core/Templates.h" + +struct Vector2; +struct Vector3; +struct Vector4; + +/// +/// Three-components vector (32 bit integer type). +/// +API_STRUCT(InBuild) struct FLAXENGINE_API Int3 +{ +public: + + union + { + struct + { + // X component + int32 X; + + // Y component + int32 Y; + + // Y component + int32 Z; + }; + + // Raw values + int32 Raw[3]; + }; + +public: + + // Vector with all components equal 0 + static const Int3 Zero; + + // Vector with all components equal 1 + static const Int3 One; + +public: + + /// + /// Empty constructor. + /// + Int3() + { + } + + // Init + // @param xy Value to assign to the all components + Int3(int32 xyz) + : X(xyz) + , Y(xyz) + , Z(xyz) + { + } + + // Init + // @param x X component value + // @param y Y component value + // @param z Z component value + Int3(int32 x, int32 y, int32 z) + : X(x) + , Y(y) + , Z(z) + { + } + + // Init + // @param v Vector to use X, Y and Z components + explicit Int3(const Vector3& v); + +public: + + String ToString() const; + +public: + + // Returns a vector containing the largest components of the specified vectors + // @param a The first source vector + // @param b The second source vector + // @param result When the method completes, contains an new vector composed of the largest components of the source vectors + static Int3 Max(const Int3& a, const Int3& b) + { + return Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z); + } + + // Returns a vector containing the smallest components of the specified vectors + // @param a The first source vector + // @param b The second source vector + // @param result When the method completes, contains an new vector composed of the smallest components of the source vectors + static Int3 Min(const Int3& a, const Int3& b) + { + return Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z); + } + + // Returns a vector containing the largest components of the specified vectors + // @param a The first source vector + // @param b The second source vector + // @param result When the method completes, contains an new vector composed of the largest components of the source vectors + static void Max(const Int3& a, const Int3& b, Int3* result) + { + *result = Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z); + } + + // Returns a vector containing the smallest components of the specified vectors + // @param a The first source vector + // @param b The second source vector + // @param result When the method completes, contains an new vector composed of the smallest components of the source vectors + static void Min(const Int3& a, const Int3& b, Int3* result) + { + *result = Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z); + } +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +DEFINE_DEFAULT_FORMATTING(Int3, "X:{0} Y:{1} Z:{2}", v.X, v.Y, v.Z); diff --git a/Source/Engine/Core/Math/Int4.h b/Source/Engine/Core/Math/Int4.h new file mode 100644 index 000000000..c8e9eebd8 --- /dev/null +++ b/Source/Engine/Core/Math/Int4.h @@ -0,0 +1,134 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Math.h" +#include "Engine/Core/Formatting.h" +#include "Engine/Core/Templates.h" + +struct Vector2; +struct Vector3; +struct Vector4; + +/// +/// Four-components vector (32 bit integer type). +/// +API_STRUCT(InBuild) struct FLAXENGINE_API Int4 +{ +public: + + union + { + struct + { + // X component + int32 X; + + // Y component + int32 Y; + + // Z component + int32 Z; + + // W component + int32 W; + }; + + // Raw values + int32 Raw[4]; + }; + +public: + + // Vector with all components equal 0 + static const Int4 Zero; + + // Vector with all components equal 1 + static const Int4 One; + +public: + + /// + /// Empty constructor. + /// + Int4() + { + } + + // Init + // @param xy Value to assign to the all components + Int4(int32 xyzw) + : X(xyzw) + , Y(xyzw) + , Z(xyzw) + , W(xyzw) + { + } + + // Init + // @param x X component value + // @param y Y component value + // @param z Z component value + // @param w W component value + Int4(int32 x, int32 y, int32 z, int32 w) + : X(x) + , Y(y) + , Z(z) + , W(w) + { + } + + // Init + // @param v Vector to use X, Y, Z and W components + explicit Int4(const Vector4& v); + +public: + + String ToString() const; + +public: + + /// + /// Returns average arithmetic of all the components + /// + /// Average arithmetic of all the components + float AverageArithmetic() const + { + return (X + Y + Z + W) * 0.25f; + } + + /// + /// Gets sum of all vector components values + /// + /// Sum of X, Y, Z and W + int32 SumValues() const + { + return X + Y + Z + W; + } + + /// + /// Returns minimum value of all the components + /// + /// Minimum value + int32 MinValue() const + { + return Math::Min(X, Y, Z, W); + } + + /// + /// Returns maximum value of all the components + /// + /// Maximum value + int32 MaxValue() const + { + return Math::Max(X, Y, Z, W); + } +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +DEFINE_DEFAULT_FORMATTING(Int4, "X:{0} Y:{1} Z:{2} W:{3}", v.X, v.Y, v.Z, v.W); diff --git a/Source/Engine/Core/Math/VectorInt.h b/Source/Engine/Core/Math/VectorInt.h index 9b5eeb866..d9611d615 100644 --- a/Source/Engine/Core/Math/VectorInt.h +++ b/Source/Engine/Core/Math/VectorInt.h @@ -2,518 +2,6 @@ #pragma once -#include "Math.h" -#include "Engine/Core/Formatting.h" -#include "Engine/Core/Templates.h" - -struct Vector2; -struct Vector3; -struct Vector4; - -/// -/// Two-components vector (32 bit integer type). -/// -API_STRUCT(InBuild) struct FLAXENGINE_API Int2 -{ -public: - - union - { - struct - { - // X component - int32 X; - - // Y component - int32 Y; - }; - - // Raw values - int32 Raw[2]; - }; - -public: - - // Vector with all components equal 0 - static const Int2 Zero; - - // Vector with all components equal 1 - static const Int2 One; - -public: - - /// - /// Empty constructor. - /// - Int2() - { - } - - // Init - // @param xy Value to assign to the all components - Int2(int32 xy) - : X(xy) - , Y(xy) - { - } - - // Init - // @param x X component value - // @param y Y component value - Int2(int32 x, int32 y) - : X(x) - , Y(y) - { - } - - // Init - // @param v Vector to use X and Y components - explicit Int2(const Vector2& v); - -public: - - String ToString() const; - -public: - - // Arithmetic operators with Int2 - - Int2 operator+(const Int2& b) const - { - return Add(*this, b); - } - - Int2 operator-(const Int2& b) const - { - return Subtract(*this, b); - } - - Int2 operator*(const Int2& b) const - { - return Multiply(*this, b); - } - - Int2 operator/(const Int2& b) const - { - return Divide(*this, b); - } - - Int2 operator-() const - { - return Int2(-X, -Y); - } - - // op= operators with Int2 - - Int2& operator+=(const Int2& b) - { - *this = Add(*this, b); - return *this; - } - - Int2& operator-=(const Int2& b) - { - *this = Subtract(*this, b); - return *this; - } - - Int2& operator*=(const Int2& b) - { - *this = Multiply(*this, b); - return *this; - } - - Int2& operator/=(const Int2& b) - { - *this = Divide(*this, b); - return *this; - } - - // Arithmetic operators with int32 - - Int2 operator+(int32 b) const - { - return Add(*this, b); - } - - Int2 operator-(int32 b) const - { - return Subtract(*this, b); - } - - Int2 operator*(int32 b) const - { - return Multiply(*this, b); - } - - Int2 operator/(int32 b) const - { - return Divide(*this, b); - } - - // op= operators with int32 - - Int2& operator+=(int32 b) - { - *this = Add(*this, b); - return *this; - } - - Int2& operator-=(int32 b) - { - *this = Subtract(*this, b); - return *this; - } - - Int2& operator*=(int32 b) - { - *this = Multiply(*this, b); - return *this; - } - - Int2& operator/=(int32 b) - { - *this = Divide(*this, b); - return *this; - } - - // Comparison operators - - bool operator==(const Int2& b) const - { - return X == b.X && Y == b.Y; - } - - bool operator!=(const Int2& b) const - { - return X != b.X || Y != b.Y; - } - - bool operator>(const Int2& b) const - { - return X > b.X && Y > b.Y; - } - - bool operator>=(const Int2& b) const - { - return X >= b.X && Y >= b.Y; - } - - bool operator<(const Int2& b) const - { - return X < b.X && Y < b.Y; - } - - bool operator<=(const Int2& b) const - { - return X <= b.X && Y <= b.Y; - } - -public: - - static void Add(const Int2& a, const Int2& b, Int2* result) - { - result->X = a.X + b.X; - result->Y = a.Y + b.Y; - } - - static Int2 Add(const Int2& a, const Int2& b) - { - Int2 result; - Add(a, b, &result); - return result; - } - - static void Subtract(const Int2& a, const Int2& b, Int2* result) - { - result->X = a.X - b.X; - result->Y = a.Y - b.Y; - } - - static Int2 Subtract(const Int2& a, const Int2& b) - { - Int2 result; - Subtract(a, b, &result); - return result; - } - - static Int2 Multiply(const Int2& a, const Int2& b) - { - return Int2(a.X * b.X, a.Y * b.Y); - } - - static Int2 Multiply(const Int2& a, int32 b) - { - return Int2(a.X * b, a.Y * b); - } - - static Int2 Divide(const Int2& a, const Int2& b) - { - return Int2(a.X / b.X, a.Y / b.Y); - } - - static Int2 Divide(const Int2& a, int32 b) - { - return Int2(a.X / b, a.Y / b); - } - - // Creates vector from minimum components of two vectors - static Int2 Min(const Int2& a, const Int2& b) - { - return Int2(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y); - } - - // Creates vector from maximum components of two vectors - static Int2 Max(const Int2& a, const Int2& b) - { - return Int2(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y); - } -}; - -/// -/// Three-components vector (32 bit integer type). -/// -API_STRUCT(InBuild) struct FLAXENGINE_API Int3 -{ -public: - - union - { - struct - { - // X component - int32 X; - - // Y component - int32 Y; - - // Y component - int32 Z; - }; - - // Raw values - int32 Raw[3]; - }; - -public: - - // Vector with all components equal 0 - static const Int3 Zero; - - // Vector with all components equal 1 - static const Int3 One; - -public: - - /// - /// Empty constructor. - /// - Int3() - { - } - - // Init - // @param xy Value to assign to the all components - Int3(int32 xyz) - : X(xyz) - , Y(xyz) - , Z(xyz) - { - } - - // Init - // @param x X component value - // @param y Y component value - // @param z Z component value - Int3(int32 x, int32 y, int32 z) - : X(x) - , Y(y) - , Z(z) - { - } - - // Init - // @param v Vector to use X, Y and Z components - explicit Int3(const Vector3& v); - -public: - - String ToString() const; - -public: - - // Returns a vector containing the largest components of the specified vectors - // @param a The first source vector - // @param b The second source vector - // @param result When the method completes, contains an new vector composed of the largest components of the source vectors - static Int3 Max(const Int3& a, const Int3& b) - { - return Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z); - } - - // Returns a vector containing the smallest components of the specified vectors - // @param a The first source vector - // @param b The second source vector - // @param result When the method completes, contains an new vector composed of the smallest components of the source vectors - static Int3 Min(const Int3& a, const Int3& b) - { - return Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z); - } - - // Returns a vector containing the largest components of the specified vectors - // @param a The first source vector - // @param b The second source vector - // @param result When the method completes, contains an new vector composed of the largest components of the source vectors - static void Max(const Int3& a, const Int3& b, Int3* result) - { - *result = Int3(a.X > b.X ? a.X : b.X, a.Y > b.Y ? a.Y : b.Y, a.Z > b.Z ? a.Z : b.Z); - } - - // Returns a vector containing the smallest components of the specified vectors - // @param a The first source vector - // @param b The second source vector - // @param result When the method completes, contains an new vector composed of the smallest components of the source vectors - static void Min(const Int3& a, const Int3& b, Int3* result) - { - *result = Int3(a.X < b.X ? a.X : b.X, a.Y < b.Y ? a.Y : b.Y, a.Z < b.Z ? a.Z : b.Z); - } -}; - -/// -/// Four-components vector (32 bit integer type). -/// -API_STRUCT(InBuild) struct FLAXENGINE_API Int4 -{ -public: - - union - { - struct - { - // X component - int32 X; - - // Y component - int32 Y; - - // Z component - int32 Z; - - // W component - int32 W; - }; - - // Raw values - int32 Raw[4]; - }; - -public: - - // Vector with all components equal 0 - static const Int4 Zero; - - // Vector with all components equal 1 - static const Int4 One; - -public: - - /// - /// Empty constructor. - /// - Int4() - { - } - - // Init - // @param xy Value to assign to the all components - Int4(int32 xyzw) - : X(xyzw) - , Y(xyzw) - , Z(xyzw) - , W(xyzw) - { - } - - // Init - // @param x X component value - // @param y Y component value - // @param z Z component value - // @param w W component value - Int4(int32 x, int32 y, int32 z, int32 w) - : X(x) - , Y(y) - , Z(z) - , W(w) - { - } - - // Init - // @param v Vector to use X, Y, Z and W components - explicit Int4(const Vector4& v); - -public: - - String ToString() const; - -public: - - /// - /// Returns average arithmetic of all the components - /// - /// Average arithmetic of all the components - float AverageArithmetic() const - { - return (X + Y + Z + W) * 0.25f; - } - - /// - /// Gets sum of all vector components values - /// - /// Sum of X, Y, Z and W - int32 SumValues() const - { - return X + Y + Z + W; - } - - /// - /// Returns minimum value of all the components - /// - /// Minimum value - int32 MinValue() const - { - return Math::Min(X, Y, Z, W); - } - - /// - /// Returns maximum value of all the components - /// - /// Maximum value - int32 MaxValue() const - { - return Math::Max(X, Y, Z, W); - } -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -template<> -struct TIsPODType -{ - enum { Value = true }; -}; - -DEFINE_DEFAULT_FORMATTING(Int2, "X:{0} Y:{1}", v.X, v.Y); - -DEFINE_DEFAULT_FORMATTING(Int3, "X:{0} Y:{1} Z:{2}", v.X, v.Y, v.Z); - -DEFINE_DEFAULT_FORMATTING(Int4, "X:{0} Y:{1} Z:{2} W:{3}", v.X, v.Y, v.Z, v.W); +#include "Int2.h" +#include "Int3.h" +#include "Int4.h" From 259c419c360f09e8d9dff6d94205c72c39256bd2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 19:09:42 +0100 Subject: [PATCH 067/419] Fix crash when resizing existing MaterialSlots collection for model --- Source/Engine/Content/Assets/ModelBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Content/Assets/ModelBase.h b/Source/Engine/Content/Assets/ModelBase.h index ebf05570d..89805f3c0 100644 --- a/Source/Engine/Content/Assets/ModelBase.h +++ b/Source/Engine/Content/Assets/ModelBase.h @@ -53,7 +53,7 @@ public: ScopeLock lock(Locker); const int32 prevCount = MaterialSlots.Count(); - MaterialSlots.Resize(slotsCount); + MaterialSlots.Resize(slotsCount, false); // Initialize slot names for (int32 i = prevCount; i < slotsCount; i++) From a3fa8ff9f0e4ddd636b6369c19961b07affd51d9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 19:17:08 +0100 Subject: [PATCH 068/419] Optimize some includes --- Source/Editor/Tools/Terrain/TerrainTools.cpp | 2 +- Source/Editor/Utilities/EditorUtilities.cpp | 2 -- Source/Engine/Core/Math/Vector2.cpp | 2 +- Source/Engine/Core/Math/Vector3.cpp | 2 +- Source/Engine/Core/Math/Vector4.cpp | 2 +- Source/Engine/Graphics/Models/ModelData.h | 2 +- Source/Engine/Graphics/Models/Types.h | 2 +- Source/Engine/Navigation/NavMeshBuilder.cpp | 2 +- Source/Engine/Platform/Linux/LinuxWindow.cpp | 2 +- Source/Engine/Renderer/EyeAdaptationPass.cpp | 1 + Source/Engine/Terrain/TerrainPatch.cpp | 1 - Source/Engine/Terrain/TerrainPatch.h | 2 +- Source/Engine/Tools/TextureTool/TextureTool.cpp | 2 +- 13 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 6bf4f8fd4..e89b5e3e9 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -2,7 +2,7 @@ #include "TerrainTools.h" #include "Engine/Core/Cache.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Core/Math/Color32.h" #include "Engine/Core/Collections/CollectionPoolCache.h" #include "Engine/Terrain/TerrainPatch.h" diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index d7136a5c8..fc266ec13 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -6,10 +6,8 @@ #include "Engine/Core/Log.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/PixelFormatExtensions.h" -#include "Engine/Serialization/FileReadStream.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" #include "Engine/Core/Config/GameSettings.h" #include "Engine/Content/Content.h" #include "Engine/Content/AssetReference.h" diff --git a/Source/Engine/Core/Math/Vector2.cpp b/Source/Engine/Core/Math/Vector2.cpp index 6a0468033..c23e79d19 100644 --- a/Source/Engine/Core/Math/Vector2.cpp +++ b/Source/Engine/Core/Math/Vector2.cpp @@ -4,8 +4,8 @@ #include "Vector3.h" #include "Vector4.h" #include "Color.h" +#include "Int2.h" #include "../Types/String.h" -#include "VectorInt.h" static_assert(sizeof(Vector2) == 8, "Invalid Vector2 type size."); diff --git a/Source/Engine/Core/Math/Vector3.cpp b/Source/Engine/Core/Math/Vector3.cpp index e3ff349ba..f66bbcbbd 100644 --- a/Source/Engine/Core/Math/Vector3.cpp +++ b/Source/Engine/Core/Math/Vector3.cpp @@ -6,7 +6,7 @@ #include "Color.h" #include "Quaternion.h" #include "Matrix.h" -#include "VectorInt.h" +#include "Int3.h" #include "../Types/String.h" static_assert(sizeof(Vector3) == 12, "Invalid Vector3 type size."); diff --git a/Source/Engine/Core/Math/Vector4.cpp b/Source/Engine/Core/Math/Vector4.cpp index 3caaab985..34fa27aac 100644 --- a/Source/Engine/Core/Math/Vector4.cpp +++ b/Source/Engine/Core/Math/Vector4.cpp @@ -6,7 +6,7 @@ #include "Color.h" #include "Matrix.h" #include "Rectangle.h" -#include "VectorInt.h" +#include "Int4.h" #include "../Types/String.h" static_assert(sizeof(Vector4) == 16, "Invalid Vector4 type size."); diff --git a/Source/Engine/Graphics/Models/ModelData.h b/Source/Engine/Graphics/Models/ModelData.h index 1e4b59d0a..a3bb47fcc 100644 --- a/Source/Engine/Graphics/Models/ModelData.h +++ b/Source/Engine/Graphics/Models/ModelData.h @@ -5,7 +5,7 @@ #include "Engine/Core/Common.h" #include "Engine/Core/Math/BoundingSphere.h" #include "Engine/Core/Math/BoundingBox.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int4.h" #include "Engine/Serialization/Stream.h" #include "Engine/Graphics/Enums.h" #include "Types.h" diff --git a/Source/Engine/Graphics/Models/Types.h b/Source/Engine/Graphics/Models/Types.h index 248988243..9a95b8815 100644 --- a/Source/Engine/Graphics/Models/Types.h +++ b/Source/Engine/Graphics/Models/Types.h @@ -9,7 +9,7 @@ #include "Engine/Core/Math/Vector4.h" #include "Engine/Core/Math/Color.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int4.h" class Model; class SkinnedModel; diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index b8e9a7544..2220d2cb6 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -4,7 +4,6 @@ #include "NavMeshBuilder.h" #include "Engine/Core/Math/BoundingBox.h" -#include "Engine/Core/Math/VectorInt.h" #include "Engine/Physics/Colliders/BoxCollider.h" #include "Engine/Physics/Colliders/MeshCollider.h" #include "Engine/Threading/ThreadPoolTask.h" @@ -15,6 +14,7 @@ #include "Engine/Level/Level.h" #include "Engine/Level/SceneQuery.h" #include "Engine/Core/Log.h" +#include "Engine/Core/Math/Int3.h" #include "NavigationScene.h" #include "NavigationSettings.h" #include "NavMeshBoundsVolume.h" diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index 0ed5341ec..768df66bd 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -10,7 +10,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Utilities/StringConverter.h" diff --git a/Source/Engine/Renderer/EyeAdaptationPass.cpp b/Source/Engine/Renderer/EyeAdaptationPass.cpp index 754c1489f..a85e0a72b 100644 --- a/Source/Engine/Renderer/EyeAdaptationPass.cpp +++ b/Source/Engine/Renderer/EyeAdaptationPass.cpp @@ -2,6 +2,7 @@ #include "EyeAdaptationPass.h" #include "RenderList.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Content/Assets/Shader.h" #include "Engine/Content/Content.h" #include "Engine/Graphics/PostProcessBase.h" diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index d0050d455..791e93c7f 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -5,7 +5,6 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Physics/Utilities.h" #include "Engine/Physics/Physics.h" diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h index fcd97f4ec..b09848d61 100644 --- a/Source/Engine/Terrain/TerrainPatch.h +++ b/Source/Engine/Terrain/TerrainPatch.h @@ -5,7 +5,7 @@ #include "Terrain.h" #include "TerrainChunk.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Physics/Types.h" #include "Engine/Level/Scene/Lightmap.h" #include "Engine/Content/Assets/RawDataAsset.h" diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index e6502bb6f..67e4d7679 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -7,7 +7,7 @@ #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Math/Packed.h" #include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/VectorInt.h" +#include "Engine/Core/Math/Int2.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Serialization/JsonWriter.h" #include "Engine/Serialization/JsonTools.h" From 932de300e4a03959f210185e6319acd0e88265b0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 23:07:20 +0100 Subject: [PATCH 069/419] Fix missing focus for SliderControl --- Source/Editor/GUI/Input/SliderControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Input/SliderControl.cs b/Source/Editor/GUI/Input/SliderControl.cs index 90288cfd0..77e8a5da8 100644 --- a/Source/Editor/GUI/Input/SliderControl.cs +++ b/Source/Editor/GUI/Input/SliderControl.cs @@ -168,6 +168,7 @@ namespace FlaxEditor.GUI.Input { if (button == MouseButton.Left) { + Focus(); float mousePosition = location.X; if (_thumbRect.Contains(ref location)) @@ -208,7 +209,6 @@ namespace FlaxEditor.GUI.Input { if (button == MouseButton.Left && _isSliding) { - // End sliding EndSliding(); return true; } From fd6158e1a9aabbf444273a06c9a7d05fef68ecd6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Jan 2021 23:36:50 +0100 Subject: [PATCH 070/419] Fix loading `VariantType.Blob` --- Source/Editor/Utilities/Utils.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 5f1636239..ac34d9e45 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -742,6 +742,7 @@ namespace FlaxEditor.Utilities case VariantType.Array: return new ScriptType(typeof(object[])); case VariantType.Dictionary: return new ScriptType(typeof(Dictionary)); case VariantType.ManagedObject: return new ScriptType(typeof(object)); + case VariantType.Blob: return new ScriptType(typeof(byte[])); default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} without typename."); } } @@ -805,6 +806,7 @@ namespace FlaxEditor.Utilities case VariantType.Array: return typeof(object[]); case VariantType.Dictionary: return typeof(Dictionary); case VariantType.ManagedObject: return typeof(object); + case VariantType.Blob: return typeof(byte[]); default: throw new ArgumentOutOfRangeException($"Unknown Variant Type {variantType} without typename."); } } From 9e3c413c918367cf44a1072ce727d487375b6b18 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 13 Jan 2021 00:22:52 +0100 Subject: [PATCH 071/419] Fix crash when using Byte, Int16 or UInt16 as VS parameters --- Source/Engine/Core/Types/Variant.cpp | 25 ++++++++++++++++++++++++- Source/Engine/Serialization/Stream.cpp | 18 +++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 62c791875..db71c2f77 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -2612,7 +2612,8 @@ Variant Variant::Lerp(const Variant& a, const Variant& b, float alpha) void Variant::AllocStructure() { - const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(StringAnsiView(Type.TypeName)); + const StringAnsiView typeName(Type.TypeName); + const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeName); if (typeHandle) { const ScriptingType& type = typeHandle.GetType(); @@ -2620,8 +2621,26 @@ void Variant::AllocStructure() AsBlob.Data = Allocator::Allocate(AsBlob.Length); type.Struct.Ctor(AsBlob.Data); } + else if (typeName == "System.Byte") + { + // Hack for byte + AsBlob.Length = 1; + AsBlob.Data = Allocator::Allocate(AsBlob.Length); + *((byte*)AsBlob.Data) = 0; + } + else if (typeName == "System.Int16" || typeName == "System.UInt16") + { + // Hack for 16bit int + AsBlob.Length = 2; + AsBlob.Data = Allocator::Allocate(AsBlob.Length); + *((int16*)AsBlob.Data) = 0; + } else { + if (typeName.Length() != 0) + { + LOG(Warning, "Missing scripting type \'{0}\'", String(typeName.Get())); + } AsBlob.Data = nullptr; AsBlob.Length = 0; } @@ -2637,6 +2656,10 @@ void Variant::CopyStructure(void* src) auto& type = typeHandle.GetType(); type.Struct.Copy(AsBlob.Data, src); } + else + { + Platform::MemoryCopy(AsBlob.Data, src, AsBlob.Length); + } } } diff --git a/Source/Engine/Serialization/Stream.cpp b/Source/Engine/Serialization/Stream.cpp index e10ae9fd6..2d3ef8b30 100644 --- a/Source/Engine/Serialization/Stream.cpp +++ b/Source/Engine/Serialization/Stream.cpp @@ -4,8 +4,9 @@ #include "WriteStream.h" #include "Engine/Core/Types/CommonValue.h" #include "Engine/Core/Types/Variant.h" -#include "Engine/Content/Asset.h" #include "Engine/Core/Collections/Dictionary.h" +#include "Engine/Content/Asset.h" +#include "Engine/Debug/DebugLog.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingObjectReference.h" @@ -330,8 +331,19 @@ void ReadStream::ReadVariant(Variant* data) { int32 length; ReadInt32(&length); - ASSERT(data->AsBlob.Length == length); - ReadBytes(data->AsBlob.Data, length); + if (data->AsBlob.Length == length) + { + ReadBytes(data->AsBlob.Data, length); + } + else + { + LOG(Error, "Invalid Variant {2} data length {0}. Expected {1} bytes from stream.", data->AsBlob.Length, length, data->Type.ToString()); + + // Skip those bytes + void* ptr = Allocator::Allocate(length); + ReadBytes(ptr, length); + Allocator::Free(ptr); + } break; } case VariantType::Blob: From 27ed23c1b9e8019bf000845f57712f5e09348b0a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 13 Jan 2021 14:28:46 +0100 Subject: [PATCH 072/419] Add support for multiple navmeshes on a scene --- Source/Engine/Level/Scene/Scene.cpp | 88 +++-- Source/Engine/Level/Scene/Scene.h | 37 +- .../{NavigationScene.cpp => NavMesh.cpp} | 133 ++++--- Source/Engine/Navigation/NavMesh.h | 76 ++++ .../Engine/Navigation/NavMeshBoundsVolume.cpp | 7 +- Source/Engine/Navigation/NavMeshBuilder.cpp | 274 +++++++------ Source/Engine/Navigation/NavMeshBuilder.h | 7 +- Source/Engine/Navigation/NavMeshRuntime.cpp | 276 ++++++++++++- Source/Engine/Navigation/NavMeshRuntime.h | 86 ++++- Source/Engine/Navigation/Navigation.cpp | 364 +++++++----------- Source/Engine/Navigation/Navigation.h | 4 +- Source/Engine/Navigation/NavigationScene.h | 94 ----- .../Engine/Navigation/NavigationSettings.cs | 66 ++++ Source/Engine/Navigation/NavigationSettings.h | 77 ++-- Source/Engine/Navigation/NavigationTypes.h | 49 ++- 15 files changed, 1012 insertions(+), 626 deletions(-) rename Source/Engine/Navigation/{NavigationScene.cpp => NavMesh.cpp} (52%) create mode 100644 Source/Engine/Navigation/NavMesh.h delete mode 100644 Source/Engine/Navigation/NavigationScene.h diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp index 2d48b9f14..b86610ab9 100644 --- a/Source/Engine/Level/Scene/Scene.cpp +++ b/Source/Engine/Level/Scene/Scene.cpp @@ -8,7 +8,10 @@ #include "Engine/Physics/Colliders/MeshCollider.h" #include "Engine/Level/Actors/StaticModel.h" #include "Engine/Level/ActorsCache.h" -#include "Engine/Navigation/NavigationScene.h" +#include "Engine/Navigation/NavigationSettings.h" +#include "Engine/Navigation/NavMeshBoundsVolume.h" +#include "Engine/Navigation/NavMesh.h" +#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Serialization/Serialization.h" REGISTER_JSON_ASSET(SceneAsset, "FlaxEngine.SceneAsset"); @@ -27,7 +30,6 @@ Scene::Scene(const SpawnParams& params) , Ticking(this) , LightmapsData(this) , CSGData(this) - , Navigation(::New(this)) { // Default name _name = TEXT("Scene"); @@ -42,7 +44,6 @@ Scene::Scene(const SpawnParams& params) Scene::~Scene() { - Delete(Navigation); } LightmapSettings Scene::GetLightmapSettings() const @@ -55,6 +56,31 @@ void Scene::SetLightmapSettings(const LightmapSettings& value) Info.LightmapSettings = value; } +BoundingBox Scene::GetNavigationBounds() +{ + if (NavigationVolumes.IsEmpty()) + return BoundingBox::Empty; + PROFILE_CPU_NAMED("GetNavigationBounds"); + auto box = NavigationVolumes[0]->GetBox(); + for (int32 i = 1; i < NavigationVolumes.Count(); i++) + BoundingBox::Merge(box, NavigationVolumes[i]->GetBox(), box); + return box; +} + +NavMeshBoundsVolume* Scene::FindNavigationBoundsOverlap(const BoundingBox& bounds) +{ + NavMeshBoundsVolume* result = nullptr; + for (int32 i = 0; i < NavigationVolumes.Count(); i++) + { + if (NavigationVolumes[i]->GetBox().Intersects(bounds)) + { + result = NavigationVolumes[i]; + break; + } + } + return result; +} + void Scene::ClearLightmaps() { LightmapsData.ClearLightmaps(); @@ -216,12 +242,6 @@ void Scene::Serialize(SerializeStream& stream, const void* otherObj) // 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); @@ -230,8 +250,6 @@ void Scene::Serialize(SerializeStream& stream, const void* otherObj) stream.JKEY("CSG"); stream.Object(&CSGData, other ? &other->CSGData : nullptr); } - - SERIALIZE_MEMBER(NavMesh, Navigation->DataAsset); } void Scene::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -243,7 +261,37 @@ void Scene::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) LightmapsData.LoadLightmaps(Info.Lightmaps); CSGData.DeserializeIfExists(stream, "CSG", modifier); - DESERIALIZE_MEMBER(NavMesh, Navigation->DataAsset); + // [Deprecated on 13.01.2021, expires on 13.01.2023] + if (modifier->EngineBuild <= 6215 && NavigationMeshes.IsEmpty()) + { + const auto e = SERIALIZE_FIND_MEMBER(stream, "NavMesh"); + if (e != stream.MemberEnd()) + { + // Upgrade from old single hidden navmesh data into NavMesh actors on a scene + AssetReference dataAsset; + Serialization::Deserialize(e->value, dataAsset, modifier); + const auto settings = NavigationSettings::Get(); + if (dataAsset && settings->NavMeshes.HasItems()) + { + auto navMesh = New(); + navMesh->SetStaticFlags(StaticFlags::FullyStatic); + navMesh->SetName(TEXT("NavMesh.") + settings->NavMeshes[0].Name); + navMesh->DataAsset = dataAsset; + navMesh->Properties = settings->NavMeshes[0]; + if (IsDuringPlay()) + { + navMesh->SetParent(this, false); + } + else + { + navMesh->_parent = this; + navMesh->_scene = this; + Children.Add(navMesh); + navMesh->CreateManaged(); + } + } + } + } } void Scene::OnDeleteObject() @@ -309,22 +357,6 @@ void Scene::EndPlay() Actor::EndPlay(); } -void Scene::OnEnable() -{ - // Base - Actor::OnEnable(); - - Navigation->OnEnable(); -} - -void Scene::OnDisable() -{ - Navigation->OnDisable(); - - // Base - Actor::OnDisable(); -} - void Scene::OnTransformChanged() { // Base diff --git a/Source/Engine/Level/Scene/Scene.h b/Source/Engine/Level/Scene/Scene.h index 628726e4b..d3c1502c6 100644 --- a/Source/Engine/Level/Scene/Scene.h +++ b/Source/Engine/Level/Scene/Scene.h @@ -12,8 +12,9 @@ class MeshCollider; class Level; -class NavigationScene; class ReloadScriptsAction; +class NavMeshBoundsVolume; +class NavMesh; /// /// The scene root object that contains a hierarchy of actors. @@ -69,11 +70,6 @@ public: /// CSG::SceneCSGData CSGData; - /// - /// The navigation scene (always valid). - /// - NavigationScene* Navigation; - /// /// Gets the lightmap settings (per scene). /// @@ -85,6 +81,31 @@ public: /// API_PROPERTY() void SetLightmapSettings(const LightmapSettings& value); +public: + + /// + /// The list of registered navigation bounds volumes (in the scene). + /// + Array NavigationVolumes; + + /// + /// The list of registered navigation meshes (in the scene). + /// + Array NavigationMeshes; + + /// + /// Gets the total navigation volumes bounds. + /// + /// The navmesh bounds. + BoundingBox GetNavigationBounds(); + + /// + /// Finds the navigation volume bounds that have intersection with the given world-space bounding box. + /// + /// The bounds. + /// The intersecting volume or null if none found. + NavMeshBoundsVolume* FindNavigationBoundsOverlap(const BoundingBox& bounds); + public: /// @@ -148,12 +169,10 @@ public: protected: - // [Scene] + // [Actor] void PostLoad() override; void PostSpawn() override; void BeginPlay(SceneBeginData* data) override; - void OnEnable() override; - void OnDisable() override; void OnTransformChanged() override; }; diff --git a/Source/Engine/Navigation/NavigationScene.cpp b/Source/Engine/Navigation/NavMesh.cpp similarity index 52% rename from Source/Engine/Navigation/NavigationScene.cpp rename to Source/Engine/Navigation/NavMesh.cpp index 2138db009..ef90e2989 100644 --- a/Source/Engine/Navigation/NavigationScene.cpp +++ b/Source/Engine/Navigation/NavMesh.cpp @@ -1,61 +1,33 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -#include "NavigationScene.h" -#include "Navigation.h" +#include "NavMesh.h" #include "NavMeshRuntime.h" -#include "NavMeshBoundsVolume.h" -#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Level/Scene/Scene.h" -#include "Engine/Content/Assets/RawDataAsset.h" -#include "Engine/Core/Log.h" -#if USE_EDITOR -#include "Editor/Editor.h" -#endif +#include "Engine/Serialization/Serialization.h" #if COMPILE_WITH_ASSETS_IMPORTER #include "Engine/ContentImporters/AssetsImportingManager.h" #include "Engine/Serialization/MemoryWriteStream.h" +#if USE_EDITOR +#include "Editor/Editor.h" +#endif #endif -NavigationScene::NavigationScene(::Scene* scene) - : Scene(scene) +NavMesh::NavMesh(const SpawnParams& params) + : Actor(params) , IsDataDirty(false) { - DataAsset.Loaded.Bind(this); + DataAsset.Loaded.Bind(this); } -BoundingBox NavigationScene::GetNavigationBounds() -{ - if (Volumes.IsEmpty()) - return BoundingBox::Empty; - - PROFILE_CPU_NAMED("GetNavigationBounds"); - - auto box = Volumes[0]->GetBox(); - for (int32 i = 1; i < Volumes.Count(); i++) - BoundingBox::Merge(box, Volumes[i]->GetBox(), box); - return box; -} - -NavMeshBoundsVolume* NavigationScene::FindNavigationBoundsOverlap(const BoundingBox& bounds) -{ - NavMeshBoundsVolume* result = nullptr; - - for (int32 i = 0; i < Volumes.Count(); i++) - { - if (Volumes[i]->GetBox().Intersects(bounds)) - { - result = Volumes[i]; - break; - } - } - - return result; -} - -void NavigationScene::SaveNavMesh() +void NavMesh::SaveNavMesh() { #if COMPILE_WITH_ASSETS_IMPORTER + // Skip if scene is missing + const auto scene = GetScene(); + if (!scene) + return; + #if USE_EDITOR // Skip if game is running in editor (eg. game scripts update dynamic navmesh) if (Editor::IsPlayMode) @@ -77,7 +49,7 @@ void NavigationScene::SaveNavMesh() Guid assetId = DataAsset.GetID(); if (!assetId.IsValid()) assetId = Guid::New(); - const String assetPath = Scene->GetDataFolderPath() / TEXT("NavMesh") + ASSET_FILES_EXTENSION_WITH_DOT; + const String assetPath = scene->GetDataFolderPath() / TEXT("NavMesh") + Properties.Name + ASSET_FILES_EXTENSION_WITH_DOT; // Generate navmesh tiles data const int32 streamInitialCapacity = Math::RoundUpToPowerOf2((Data.Tiles.Count() + 1) * 1024); @@ -99,32 +71,46 @@ void NavigationScene::SaveNavMesh() #endif } -void NavigationScene::OnEnable() +void NavMesh::ClearData() { - auto navMesh = NavMeshRuntime::Get(); - CHECK(navMesh); + if (Data.Tiles.HasItems()) + { + IsDataDirty = true; + Data.TileSize = 0.0f; + Data.Tiles.Resize(0); + } +} + +NavMeshRuntime* NavMesh::GetRuntime(bool createIfMissing) const +{ + return NavMeshRuntime::Get(Properties, createIfMissing); +} + +void NavMesh::AddTiles() +{ + auto navMesh = NavMeshRuntime::Get(Properties, true); navMesh->AddTiles(this); } -void NavigationScene::OnDisable() +void NavMesh::RemoveTiles() { - auto navMesh = NavMeshRuntime::Get(); + auto navMesh = NavMeshRuntime::Get(Properties, false); if (navMesh) navMesh->RemoveTiles(this); } -void NavigationScene::OnDataAssetLoaded() +void NavMesh::OnDataAssetLoaded() { // Skip if already has data (prevent reloading navmesh on saving) if (Data.Tiles.HasItems()) return; - const bool isEnabled = Scene->IsDuringPlay() && Scene->IsActiveInHierarchy(); + const bool isEnabled = IsDuringPlay() && IsActiveInHierarchy(); // Remove added tiles if (isEnabled) { - OnDisable(); + RemoveTiles(); } // Load navmesh tiles @@ -136,6 +122,49 @@ void NavigationScene::OnDataAssetLoaded() // Add loaded tiles if (isEnabled) { - OnEnable(); + AddTiles(); } } + +void NavMesh::Serialize(SerializeStream& stream, const void* otherObj) +{ + // Base + Actor::Serialize(stream, otherObj); + +#if USE_EDITOR + // Save navmesh tiles to asset (if modified) + if (IsDataDirty) + SaveNavMesh(); +#endif + + SERIALIZE_GET_OTHER_OBJ(NavMesh); + SERIALIZE(DataAsset); + SERIALIZE(Properties); +} + +void NavMesh::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + // Base + Actor::Deserialize(stream, modifier); + + DESERIALIZE(DataAsset); + DESERIALIZE(Properties); +} + +void NavMesh::OnEnable() +{ + // Base + Actor::OnEnable(); + + GetScene()->NavigationMeshes.Add(this); + AddTiles(); +} + +void NavMesh::OnDisable() +{ + RemoveTiles(); + GetScene()->NavigationMeshes.Remove(this); + + // Base + Actor::OnDisable(); +} diff --git a/Source/Engine/Navigation/NavMesh.h b/Source/Engine/Navigation/NavMesh.h new file mode 100644 index 000000000..bd0eab703 --- /dev/null +++ b/Source/Engine/Navigation/NavMesh.h @@ -0,0 +1,76 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "NavMeshData.h" +#include "NavigationTypes.h" +#include "Engine/Content/AssetReference.h" +#include "Engine/Content/Assets/RawDataAsset.h" +#include "Engine/Level/Actor.h" + +class NavMeshBoundsVolume; +class NavMeshRuntime; + +/// +/// The navigation mesh actor that holds a navigation data for a scene. +/// +API_CLASS() class FLAXENGINE_API NavMesh : public Actor +{ +DECLARE_SCENE_OBJECT(NavMesh); +public: + + /// + /// The flag used to mark that navigation data has been modified since load. Used to save runtime data to the file on scene serialization. + /// + bool IsDataDirty; + + /// + /// The navmesh tiles data. + /// + NavMeshData Data; + + /// + /// The cached navmesh data asset. + /// + AssetReference DataAsset; + + /// + /// The navigation mesh properties. + /// + API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Nav Mesh\")") NavMeshProperties Properties; + +public: + + /// + /// Saves the nav mesh tiles data to the asset. Supported only in builds with assets saving enabled (eg. editor) and not during gameplay (eg. design time). + /// + void SaveNavMesh(); + + /// + /// Clears the data. + /// + void ClearData(); + + /// + /// Gets the navmesh runtime object that matches with properties. + /// + NavMeshRuntime* GetRuntime(bool createIfMissing = true) const; + +private: + + void AddTiles(); + void RemoveTiles(); + void OnDataAssetLoaded(); + +public: + + // [Actor] + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + +protected: + + // [Actor] + void OnEnable() override; + void OnDisable() override; +}; diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.cpp b/Source/Engine/Navigation/NavMeshBoundsVolume.cpp index 34a881ede..a3f02249a 100644 --- a/Source/Engine/Navigation/NavMeshBoundsVolume.cpp +++ b/Source/Engine/Navigation/NavMeshBoundsVolume.cpp @@ -2,7 +2,6 @@ #include "NavMeshBoundsVolume.h" #include "Engine/Level/Scene/Scene.h" -#include "NavigationScene.h" #if USE_EDITOR #include "Editor/Editor.h" #include "Editor/Managed/ManagedEditor.h" @@ -16,15 +15,15 @@ NavMeshBoundsVolume::NavMeshBoundsVolume(const SpawnParams& params) void NavMeshBoundsVolume::OnEnable() { - GetScene()->Navigation->Volumes.Add(this); - // Base Actor::OnEnable(); + + GetScene()->NavigationVolumes.Add(this); } void NavMeshBoundsVolume::OnDisable() { - GetScene()->Navigation->Volumes.Remove(this); + GetScene()->NavigationVolumes.Remove(this); // Base Actor::OnDisable(); diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index caa2513d2..dca9a445c 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -3,6 +3,11 @@ #if COMPILE_WITH_NAV_MESH_BUILDER #include "NavMeshBuilder.h" +#include "NavMesh.h" +#include "NavigationSettings.h" +#include "NavMeshBoundsVolume.h" +#include "NavLink.h" +#include "NavMeshRuntime.h" #include "Engine/Core/Math/BoundingBox.h" #include "Engine/Core/Math/VectorInt.h" #include "Engine/Physics/Colliders/BoxCollider.h" @@ -15,12 +20,6 @@ #include "Engine/Level/Level.h" #include "Engine/Level/SceneQuery.h" #include "Engine/Core/Log.h" -#include "NavigationScene.h" -#include "NavigationSettings.h" -#include "NavMeshBoundsVolume.h" -#include "NavLink.h" -#include "Navigation.h" -#include "NavMeshRuntime.h" #include #include #include @@ -113,11 +112,9 @@ struct NavigationSceneRasterization { PROFILE_CPU_NAMED("BoxCollider"); - OrientedBoundingBox box = boxCollider->GetOrientedBox(); - + const OrientedBoundingBox box = boxCollider->GetOrientedBox(); vb.Resize(8); box.GetCorners(vb.Get()); - ib.Add(BoxTrianglesIndicesCache, 36); e.RasterizeTriangles(); @@ -127,7 +124,7 @@ struct NavigationSceneRasterization PROFILE_CPU_NAMED("MeshCollider"); auto collisionData = meshCollider->CollisionData.Get(); - if (!collisionData || collisionData->WaitForLoaded(1000.0f)) + if (!collisionData || collisionData->WaitForLoaded()) return true; collisionData->ExtractGeometry(vb, ib); @@ -181,7 +178,7 @@ void RasterizeGeometry(const BoundingBox& tileBounds, rcContext* context, rcConf // Builds navmesh tile bounds and check if there are any valid navmesh volumes at that tile location // Returns true if tile is intersecting with any navmesh bounds volume actor - which means tile is in use -bool GetNavMeshTileBounds(NavigationScene* scene, int32 x, int32 y, float tileSize, BoundingBox& tileBounds) +bool GetNavMeshTileBounds(Scene* scene, int32 x, int32 y, float tileSize, BoundingBox& tileBounds) { // Build initial tile bounds (with infinite extent) tileBounds.Minimum.X = (float)x * tileSize; @@ -194,9 +191,9 @@ bool GetNavMeshTileBounds(NavigationScene* scene, int32 x, int32 y, float tileSi // Check if any navmesh volume intersects with the tile bool foundAnyVolume = false; Vector2 rangeY; - for (int32 i = 0; i < scene->Volumes.Count(); i++) + for (int32 i = 0; i < scene->NavigationVolumes.Count(); i++) { - const auto volume = scene->Volumes[i]; + const auto volume = scene->NavigationVolumes[i]; const auto& volumeBounds = volume->GetBox(); if (volumeBounds.Intersects(tileBounds)) { @@ -224,27 +221,27 @@ bool GetNavMeshTileBounds(NavigationScene* scene, int32 x, int32 y, float tileSi return foundAnyVolume; } -void RemoveTile(NavMeshRuntime* navMesh, NavigationScene* scene, int32 x, int32 y, int32 layer) +void RemoveTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, int32 layer) { - ScopeLock lock(navMesh->Locker); + ScopeLock lock(runtime->Locker); // Find tile data and remove it - for (int32 i = 0; i < scene->Data.Tiles.Count(); i++) + for (int32 i = 0; i < navMesh->Data.Tiles.Count(); i++) { - auto& tile = scene->Data.Tiles[i]; + auto& tile = navMesh->Data.Tiles[i]; if (tile.PosX == x && tile.PosY == y && tile.Layer == layer) { - scene->Data.Tiles.RemoveAt(i); - scene->IsDataDirty = true; + navMesh->Data.Tiles.RemoveAt(i); + navMesh->IsDataDirty = true; break; } } // Remove tile from navmesh - navMesh->RemoveTile(x, y, layer); + runtime->RemoveTile(x, y, layer); } -bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBounds, float tileSize, rcConfig& config) +bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, BoundingBox& tileBounds, float tileSize, rcConfig& config) { rcContext context; int32 layer = 0; @@ -355,7 +352,7 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou if (polyMesh->nverts == 0) { // Empty tile - RemoveTile(NavMeshRuntime::Get(), scene, x, y, layer); + RemoveTile(navMesh, runtime, x, y, layer); return false; } @@ -438,11 +435,11 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou { PROFILE_CPU_NAMED("Navigation.CreateTile"); - ScopeLock lock(NavMeshRuntime::Get()->Locker); + ScopeLock lock(runtime->Locker); // Add tile data - scene->IsDataDirty = true; - auto& tile = scene->Data.Tiles.AddOne(); + navMesh->IsDataDirty = true; + auto& tile = navMesh->Data.Tiles.AddOne(); tile.PosX = x; tile.PosY = y; tile.Layer = layer; @@ -451,7 +448,7 @@ bool GenerateTile(NavigationScene* scene, int32 x, int32 y, BoundingBox& tileBou tile.Data.Copy(navData, navDataSize); // Add tile to navmesh - NavMeshRuntime::Get()->AddTile(scene, tile); + runtime->AddTile(navMesh, tile); } dtFree(navData); @@ -465,16 +462,17 @@ float GetTileSize() return settings.CellSize * settings.TileSize; } -void InitConfig(rcConfig& config) +void InitConfig(rcConfig& config, NavMesh* navMesh) { auto& settings = *NavigationSettings::Get(); + auto& navMeshProperties = navMesh->Properties; config.cs = settings.CellSize; config.ch = settings.CellHeight; - config.walkableSlopeAngle = settings.WalkableMaxSlopeAngle; - config.walkableHeight = (int)(settings.WalkableHeight / config.ch + 0.99f); - config.walkableClimb = (int)(settings.WalkableMaxClimb / config.ch); - config.walkableRadius = (int)(settings.WalkableRadius / config.cs + 0.99f); + config.walkableSlopeAngle = navMeshProperties.Agent.MaxSlopeAngle; + config.walkableHeight = (int)(navMeshProperties.Agent.Height / config.ch + 0.99f); + config.walkableClimb = (int)(navMeshProperties.Agent.StepHeight / config.ch); + config.walkableRadius = (int)(navMeshProperties.Agent.Radius / config.cs + 0.99f); config.maxEdgeLen = (int)(settings.MaxEdgeLen / config.cs); config.maxSimplificationError = settings.MaxEdgeError; config.minRegionArea = rcSqr(settings.MinRegionArea); @@ -506,7 +504,9 @@ class NavMeshTileBuildTask : public ThreadPoolTask { public: - NavigationScene* Scene; + Scene* Scene; + NavMesh* NavMesh; + NavMeshRuntime* Runtime; BoundingBox TileBounds; int32 X; int32 Y; @@ -520,7 +520,7 @@ public: { PROFILE_CPU_NAMED("BuildNavMeshTile"); - if (GenerateTile(Scene, X, Y, TileBounds, TileSize, Config)) + if (GenerateTile(NavMesh, Runtime, X, Y, TileBounds, TileSize, Config)) { LOG(Warning, "Failed to generate navmesh tile at {0}x{1}.", X, Y); } @@ -557,7 +557,7 @@ void OnSceneUnloading(Scene* scene, const Guid& sceneId) for (int32 i = 0; i < NavBuildTasks.Count(); i++) { auto task = NavBuildTasks[i]; - if (task->Scene == scene->Navigation) + if (task->Scene == scene) { NavBuildTasksLocker.Unlock(); @@ -600,15 +600,16 @@ float NavMeshBuilder::GetNavMeshBuildingProgress() return result; } -void BuildTileAsync(NavigationScene* scene, int32 x, int32 y, rcConfig& config, const BoundingBox& tileBounds, float tileSize) +void BuildTileAsync(NavMesh* navMesh, int32 x, int32 y, rcConfig& config, const BoundingBox& tileBounds, float tileSize) { + NavMeshRuntime* runtime = navMesh->GetRuntime(); NavBuildTasksLocker.Lock(); // Skip if this tile is already during cooking for (int32 i = 0; i < NavBuildTasks.Count(); i++) { const auto task = NavBuildTasks[i]; - if (task->X == x && task->Y == y) + if (task->X == x && task->Y == y && task->Runtime == runtime) { NavBuildTasksLocker.Unlock(); return; @@ -617,7 +618,9 @@ void BuildTileAsync(NavigationScene* scene, int32 x, int32 y, rcConfig& config, // Create task auto task = New(); - task->Scene = scene; + task->Scene = navMesh->GetScene(); + task->NavMesh = navMesh; + task->Runtime = runtime; task->X = x; task->Y = y; task->TileBounds = tileBounds; @@ -632,70 +635,10 @@ void BuildTileAsync(NavigationScene* scene, int32 x, int32 y, rcConfig& config, task->Start(); } -void BuildWholeScene(NavigationScene* scene) +void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBounds) { const float tileSize = GetTileSize(); - const auto navMesh = NavMeshRuntime::Get(); - - // Compute total navigation area bounds - const BoundingBox worldBounds = scene->GetNavigationBounds(); - - // Align total bounds to tile size - BoundingBox worldBoundsAligned; - worldBoundsAligned.Minimum = Vector3::Floor(worldBounds.Minimum / tileSize) * tileSize; - worldBoundsAligned.Maximum = Vector3::Ceil(worldBounds.Maximum / tileSize) * tileSize; - - // Calculate tiles range for the given navigation world bounds (aligned to tiles size) - const Int3 tilesMin = Int3(worldBoundsAligned.Minimum / tileSize); - const Int3 tilesMax = Int3(worldBoundsAligned.Maximum / tileSize); - const int32 tilesX = tilesMax.X - tilesMin.X; - const int32 tilesY = tilesMax.Z - tilesMin.Z; - - { - PROFILE_CPU_NAMED("Prepare"); - - // Prepare navmesh - navMesh->RemoveTiles(scene); - navMesh->SetTileSize(tileSize); - navMesh->EnsureCapacity(tilesX * tilesY); - - // Prepare scene data - scene->Data.TileSize = tileSize; - scene->Data.Tiles.Clear(); - scene->Data.Tiles.EnsureCapacity(tilesX * tilesX); - scene->IsDataDirty = true; - } - - // Initialize nav mesh configuration - rcConfig config; - InitConfig(config); - - // Generate all tiles that intersect with the navigation volume bounds - { - PROFILE_CPU_NAMED("StartBuildingTiles"); - - for (int32 y = tilesMin.Z; y < tilesMax.Z; y++) - { - for (int32 x = tilesMin.X; x < tilesMax.X; x++) - { - BoundingBox tileBounds; - if (GetNavMeshTileBounds(scene, x, y, tileSize, tileBounds)) - { - BuildTileAsync(scene, x, y, config, tileBounds, tileSize); - } - else - { - RemoveTile(navMesh, scene, x, y, 0); - } - } - } - } -} - -void BuildDirtyBounds(NavigationScene* scene, const BoundingBox& dirtyBounds) -{ - const float tileSize = GetTileSize(); - const auto navMesh = NavMeshRuntime::Get(); + NavMeshRuntime* runtime = navMesh->GetRuntime(); // Align dirty bounds to tile size BoundingBox dirtyBoundsAligned; @@ -703,8 +646,8 @@ void BuildDirtyBounds(NavigationScene* scene, const BoundingBox& dirtyBounds) dirtyBoundsAligned.Maximum = Vector3::Ceil(dirtyBounds.Maximum / tileSize) * tileSize; // Calculate tiles range for the given navigation dirty bounds (aligned to tiles size) - const Int3 tilesMin = Int3(dirtyBoundsAligned.Minimum / tileSize); - const Int3 tilesMax = Int3(dirtyBoundsAligned.Maximum / tileSize); + const Int3 tilesMin(dirtyBoundsAligned.Minimum / tileSize); + const Int3 tilesMax(dirtyBoundsAligned.Maximum / tileSize); const int32 tilesX = tilesMax.X - tilesMin.X; const int32 tilesY = tilesMax.Z - tilesMin.Z; @@ -712,28 +655,28 @@ void BuildDirtyBounds(NavigationScene* scene, const BoundingBox& dirtyBounds) PROFILE_CPU_NAMED("Prepare"); // Prepare scene data and navmesh - if (Math::NotNearEqual(scene->Data.TileSize, tileSize)) + if (Math::NotNearEqual(navMesh->Data.TileSize, tileSize)) { - navMesh->RemoveTiles(scene); - navMesh->SetTileSize(tileSize); - navMesh->EnsureCapacity(tilesX * tilesY); + runtime->RemoveTiles(navMesh); + runtime->SetTileSize(tileSize); + runtime->EnsureCapacity(tilesX * tilesY); - scene->Data.TileSize = tileSize; - scene->Data.Tiles.Clear(); - scene->Data.Tiles.EnsureCapacity(tilesX * tilesX); - scene->IsDataDirty = true; + navMesh->Data.TileSize = tileSize; + navMesh->Data.Tiles.Clear(); + navMesh->Data.Tiles.EnsureCapacity(tilesX * tilesX); + navMesh->IsDataDirty = true; } else { // Prepare navmesh - navMesh->SetTileSize(tileSize); - navMesh->EnsureCapacity(tilesX * tilesY); + runtime->SetTileSize(tileSize); + runtime->EnsureCapacity(tilesX * tilesY); } } // Initialize nav mesh configuration rcConfig config; - InitConfig(config); + InitConfig(config, navMesh); // Generate all tiles that intersect with the navigation volume bounds { @@ -746,17 +689,103 @@ void BuildDirtyBounds(NavigationScene* scene, const BoundingBox& dirtyBounds) BoundingBox tileBounds; if (GetNavMeshTileBounds(scene, x, y, tileSize, tileBounds)) { - BuildTileAsync(scene, x, y, config, tileBounds, tileSize); + BuildTileAsync(navMesh, x, y, config, tileBounds, tileSize); } else { - RemoveTile(navMesh, scene, x, y, 0); + RemoveTile(navMesh, runtime, x, y, 0); } } } } } +void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds) +{ + auto settings = NavigationSettings::Get(); + + // Sync navmeshes + for (auto& navMeshProperties : settings->NavMeshes) + { + NavMesh* navMesh = nullptr; + for (auto e : scene->NavigationMeshes) + { + if (e->Properties.Name == navMeshProperties.Name) + { + navMesh = e; + break; + } + } + if (navMesh) + { + // Sync settings + auto runtime = navMesh->GetRuntime(false); + navMesh->Properties = navMeshProperties; + if (runtime) + runtime->Properties = navMeshProperties; + } + else if (settings->AutoAddMissingNavMeshes) + { + // Spawn missing navmesh + navMesh = New(); + navMesh->SetStaticFlags(StaticFlags::FullyStatic); + navMesh->SetName(TEXT("NavMesh.") + navMeshProperties.Name); + navMesh->Properties = navMeshProperties; + navMesh->SetParent(scene, false); + } + } + + // Build all navmeshes on the scene + for (NavMesh* navMesh : scene->NavigationMeshes) + { + BuildDirtyBounds(scene, navMesh, dirtyBounds); + } + + // Remove unused navmeshes + if (settings->AutoRemoveMissingNavMeshes) + { + for (NavMesh* navMesh : scene->NavigationMeshes) + { + // Skip used navmeshes + if (navMesh->Data.Tiles.HasItems()) + continue; + + // Skip navmeshes during async building + int32 usageCount = 0; + NavBuildTasksLocker.Lock(); + for (int32 i = 0; i < NavBuildTasks.Count(); i++) + { + if (NavBuildTasks[i]->NavMesh == navMesh) + usageCount++; + } + NavBuildTasksLocker.Unlock(); + if (usageCount != 0) + continue; + + navMesh->DeleteObject(); + } + } +} + +void BuildWholeScene(Scene* scene) +{ + // Compute total navigation area bounds + const BoundingBox worldBounds = scene->GetNavigationBounds(); + + BuildDirtyBounds(scene, worldBounds); +} + +void ClearNavigation(Scene* scene) +{ + const bool autoRemoveMissingNavMeshes = NavigationSettings::Get()->AutoRemoveMissingNavMeshes; + for (NavMesh* navMesh : scene->NavigationMeshes) + { + navMesh->ClearData(); + if (autoRemoveMissingNavMeshes) + navMesh->DeleteObject(); + } +} + void NavMeshBuilder::Update() { ScopeLock lock(NavBuildQueueLocker); @@ -769,15 +798,12 @@ void NavMeshBuilder::Update() if (now - req.Time >= 0) { NavBuildQueue.RemoveAt(i--); - auto scene = req.Scene->Navigation; + const auto scene = req.Scene.Get(); // Early out if scene has no bounds volumes to define nav mesh area - if (scene->Volumes.IsEmpty()) + if (scene->NavigationVolumes.IsEmpty()) { - // Cleanup if no navigation to use - scene->Data.TileSize = 0; - scene->Data.Tiles.Resize(0); - scene->IsDataDirty = true; + ClearNavigation(scene); continue; } @@ -797,9 +823,9 @@ void NavMeshBuilder::Update() void NavMeshBuilder::Build(Scene* scene, float timeoutMs) { // Early out if scene is not using navigation - if (scene->Navigation->Volumes.IsEmpty()) + if (scene->NavigationVolumes.IsEmpty()) { - scene->Navigation->ClearData(); + ClearNavigation(scene); return; } @@ -828,9 +854,9 @@ void NavMeshBuilder::Build(Scene* scene, float timeoutMs) void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs) { // Early out if scene is not using navigation - if (scene->Navigation->Volumes.IsEmpty()) + if (scene->NavigationVolumes.IsEmpty()) { - scene->Navigation->ClearData(); + ClearNavigation(scene); return; } diff --git a/Source/Engine/Navigation/NavMeshBuilder.h b/Source/Engine/Navigation/NavMeshBuilder.h index 4e59027ce..27dab4273 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.h +++ b/Source/Engine/Navigation/NavMeshBuilder.h @@ -4,9 +4,14 @@ #if COMPILE_WITH_NAV_MESH_BUILDER +#include "Engine/Core/Compiler.h" + class Scene; -class NavMeshBuilder +/// +/// The navigation mesh building utility. +/// +class FLAXENGINE_API NavMeshBuilder { public: diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index e160a11a6..4ca3b7041 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -1,18 +1,23 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "NavMeshRuntime.h" -#include "NavigationScene.h" +#include "NavMesh.h" #include "Engine/Core/Log.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Threading/Threading.h" #include #include +#include #define MAX_NODES 2048 #define USE_DATA_LINK 0 #define USE_NAV_MESH_ALLOC 1 -NavMeshRuntime::NavMeshRuntime() +#define DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL 50.0f +#define DEFAULT_NAV_QUERY_EXTENT_VERTICAL 250.0f + +NavMeshRuntime::NavMeshRuntime(const NavMeshProperties& properties) + : Properties(properties) { _navMesh = nullptr; _navMeshQuery = dtAllocNavMeshQuery(); @@ -30,6 +35,150 @@ int32 NavMeshRuntime::GetTilesCapacity() const return _navMesh ? _navMesh->getMaxTiles() : 0; } +bool NavMeshRuntime::FindDistanceToWall(const Vector3& startPosition, NavMeshHit& hitInfo, float maxDistance) const +{ + ScopeLock lock(Locker); + + const auto query = GetNavMeshQuery(); + if (!query) + { + return false; + } + + dtQueryFilter filter; + Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + + dtPolyRef startPoly = 0; + query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); + if (!startPoly) + { + return false; + } + + return dtStatusSucceed(query->findDistanceToWall(startPoly, &startPosition.X, maxDistance, &filter, &hitInfo.Distance, &hitInfo.Position.X, &hitInfo.Normal.X)); +} + +bool NavMeshRuntime::FindPath(const Vector3& startPosition, const Vector3& endPosition, Array& resultPath) const +{ + resultPath.Clear(); + ScopeLock lock(Locker); + + const auto query = GetNavMeshQuery(); + if (!query) + { + return false; + } + + dtQueryFilter filter; + Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + + dtPolyRef startPoly = 0; + query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); + if (!startPoly) + { + return false; + } + dtPolyRef endPoly = 0; + query->findNearestPoly(&endPosition.X, &extent.X, &filter, &endPoly, nullptr); + if (!endPoly) + { + return false; + } + + dtPolyRef path[NAV_MESH_PATH_MAX_SIZE]; + int32 pathSize; + const auto findPathStatus = query->findPath(startPoly, endPoly, &startPosition.X, &endPosition.X, &filter, path, &pathSize, NAV_MESH_PATH_MAX_SIZE); + if (dtStatusFailed(findPathStatus)) + { + return false; + } + + // Check for special case, where path has not been found, and starting polygon was the one closest to the target + if (pathSize == 1 && dtStatusDetail(findPathStatus, DT_PARTIAL_RESULT)) + { + // In this case we find a point on starting polygon, that's closest to destination and store it as path end + resultPath.Resize(2); + resultPath[0] = startPosition; + resultPath[1] = startPosition; + query->closestPointOnPolyBoundary(startPoly, &endPosition.X, &resultPath[1].X); + } + else + { + int straightPathCount = 0; + resultPath.EnsureCapacity(NAV_MESH_PATH_MAX_SIZE); + const auto findStraightPathStatus = query->findStraightPath(&startPosition.X, &endPosition.X, path, pathSize, (float*)resultPath.Get(), nullptr, nullptr, &straightPathCount, resultPath.Capacity(), DT_STRAIGHTPATH_AREA_CROSSINGS); + if (dtStatusFailed(findStraightPathStatus)) + { + return false; + } + resultPath.Resize(straightPathCount); + } + + return true; +} + +bool NavMeshRuntime::ProjectPoint(const Vector3& point, Vector3& result) const +{ + ScopeLock lock(Locker); + + const auto query = GetNavMeshQuery(); + if (!query) + { + return false; + } + + dtQueryFilter filter; + Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + + dtPolyRef startPoly = 0; + query->findNearestPoly(&point.X, &extent.X, &filter, &startPoly, &result.X); + if (!startPoly) + { + return false; + } + + return true; +} + +bool NavMeshRuntime::RayCast(const Vector3& startPosition, const Vector3& endPosition, NavMeshHit& hitInfo) const +{ + ScopeLock lock(Locker); + + const auto query = GetNavMeshQuery(); + if (!query) + { + return false; + } + + dtQueryFilter filter; + Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + + dtPolyRef startPoly = 0; + query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); + if (!startPoly) + { + return false; + } + + dtRaycastHit hit; + hit.path = nullptr; + hit.maxPath = 0; + const bool result = dtStatusSucceed(query->raycast(startPoly, &startPosition.X, &endPosition.X, &filter, 0, &hit)); + if (hit.t >= MAX_float) + { + hitInfo.Position = endPosition; + hitInfo.Distance = 0; + } + else + { + hitInfo.Position = startPosition + (endPosition - startPosition) * hit.t; + hitInfo.Distance = hit.t; + } + hitInfo.Normal = *(Vector3*)&hit.hitNormal; + + return result; +} + void NavMeshRuntime::SetTileSize(float tileSize) { ScopeLock lock(Locker); @@ -132,13 +281,13 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) } } -void NavMeshRuntime::AddTiles(NavigationScene* scene) +void NavMeshRuntime::AddTiles(NavMesh* navMesh) { // Skip if no data - ASSERT(scene); - if (scene->Data.Tiles.IsEmpty()) + ASSERT(navMesh); + if (navMesh->Data.Tiles.IsEmpty()) return; - auto& data = scene->Data; + auto& data = navMesh->Data; PROFILE_CPU_NAMED("NavMeshRuntime.AddTiles"); @@ -164,14 +313,14 @@ void NavMeshRuntime::AddTiles(NavigationScene* scene) // Add new tiles for (auto& tileData : data.Tiles) { - AddTileInternal(scene, tileData); + AddTileInternal(navMesh, tileData); } } -void NavMeshRuntime::AddTile(NavigationScene* scene, NavMeshTileData& tileData) +void NavMeshRuntime::AddTile(NavMesh* navMesh, NavMeshTileData& tileData) { - ASSERT(scene); - auto& data = scene->Data; + ASSERT(navMesh); + auto& data = navMesh->Data; PROFILE_CPU_NAMED("NavMeshRuntime.AddTile"); @@ -195,17 +344,17 @@ void NavMeshRuntime::AddTile(NavigationScene* scene, NavMeshTileData& tileData) EnsureCapacity(1); // Add new tile - AddTileInternal(scene, tileData); + AddTileInternal(navMesh, tileData); } bool IsTileFromScene(const NavMeshRuntime* navMesh, const NavMeshTile& tile, void* customData) { - return tile.Scene == (NavigationScene*)customData; + return tile.NavMesh == (NavMesh*)customData; } -void NavMeshRuntime::RemoveTiles(NavigationScene* scene) +void NavMeshRuntime::RemoveTiles(NavMesh* navMesh) { - RemoveTiles(IsTileFromScene, scene); + RemoveTiles(IsTileFromScene, navMesh); } void NavMeshRuntime::RemoveTile(int32 x, int32 y, int32 layer) @@ -274,6 +423,101 @@ void NavMeshRuntime::RemoveTiles(bool (* prediction)(const NavMeshRuntime* navMe } } +#if COMPILE_WITH_DEBUG_DRAW + +#include "Engine/Debug/DebugDraw.h" + +void DrawPoly(NavMeshRuntime* navMesh, const dtMeshTile& tile, const dtPoly& poly) +{ + const unsigned int ip = (unsigned int)(&poly - tile.polys); + const dtPolyDetail& pd = tile.detailMeshes[ip]; + const Color color = navMesh->Properties.Color; + const float drawOffsetY = 10.0f + ((float)GetHash(color) / (float)MAX_uint32) * 10.0f; // Apply some offset to prevent Z-fighting for different navmeshes + const Color fillColor = color * 0.5f; + const Color edgesColor = Color::FromHSV(color.ToHSV() + Vector3(20.0f, 0, -0.1f), color.A); + + for (int i = 0; i < pd.triCount; i++) + { + Vector3 v[3]; + const unsigned char* t = &tile.detailTris[(pd.triBase + i) * 4]; + + for (int k = 0; k < 3; k++) + { + if (t[k] < poly.vertCount) + { + v[k] = *(Vector3*)&tile.verts[poly.verts[t[k]] * 3]; + } + else + { + v[k] = *(Vector3*)&tile.detailVerts[(pd.vertBase + t[k] - poly.vertCount) * 3]; + } + } + + v[0].Y += drawOffsetY; + v[1].Y += drawOffsetY; + v[2].Y += drawOffsetY; + + DEBUG_DRAW_TRIANGLE(v[0], v[1], v[2], fillColor, 0, true); + } + + for (int k = 0; k < pd.triCount; k++) + { + const unsigned char* t = &tile.detailTris[(pd.triBase + k) * 4]; + Vector3 v[3]; + + for (int m = 0; m < 3; m++) + { + if (t[m] < poly.vertCount) + v[m] = *(Vector3*)&tile.verts[poly.verts[t[m]] * 3]; + else + v[m] = *(Vector3*)&tile.detailVerts[(pd.vertBase + (t[m] - poly.vertCount)) * 3]; + } + + v[0].Y += drawOffsetY; + v[1].Y += drawOffsetY; + v[2].Y += drawOffsetY; + + for (int m = 0, n = 2; m < 3; n = m++) + { + // Skip inner detail edges + if (((t[3] >> (n * 2)) & 0x3) == 0) + continue; + + DEBUG_DRAW_LINE(v[n], v[m], edgesColor, 0, true); + } + } +} + +void NavMeshRuntime::DebugDraw() +{ + ScopeLock lock(Locker); + + const dtNavMesh* dtNavMesh = GetNavMesh(); + const int tilesCount = dtNavMesh ? dtNavMesh->getMaxTiles() : 0; + if (tilesCount == 0) + return; + + for (int tileIndex = 0; tileIndex < tilesCount; tileIndex++) + { + const dtMeshTile* tile = dtNavMesh->getTile(tileIndex); + if (!tile->header) + continue; + + //DebugDraw::DrawWireBox(*(BoundingBox*)&tile->header->bmin[0], Color::CadetBlue); + + for (int i = 0; i < tile->header->polyCount; i++) + { + const dtPoly* poly = &tile->polys[i]; + if (poly->getType() != DT_POLYTYPE_GROUND) + continue; + + DrawPoly(this, *tile, *poly); + } + } +} + +#endif + void NavMeshRuntime::Dispose() { if (_navMesh) @@ -284,7 +528,7 @@ void NavMeshRuntime::Dispose() _tiles.Resize(0); } -void NavMeshRuntime::AddTileInternal(NavigationScene* scene, NavMeshTileData& tileData) +void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData) { // Check if that tile has been added to navmesh NavMeshTile* tile = nullptr; @@ -313,7 +557,7 @@ void NavMeshRuntime::AddTileInternal(NavigationScene* scene, NavMeshTileData& ti ASSERT(tile); // Copy tile properties - tile->Scene = scene; + tile->NavMesh = navMesh; tile->X = tileData.PosX; tile->Y = tileData.PosY; tile->Layer = tileData.Layer; diff --git a/Source/Engine/Navigation/NavMeshRuntime.h b/Source/Engine/Navigation/NavMeshRuntime.h index 6c538ae79..5d1b8ca63 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.h +++ b/Source/Engine/Navigation/NavMeshRuntime.h @@ -5,27 +5,41 @@ #include "Engine/Core/Types/BaseTypes.h" #include "Engine/Platform/CriticalSection.h" #include "NavMeshData.h" +#include "NavigationTypes.h" -class NavigationScene; class dtNavMesh; class dtNavMeshQuery; +class NavMesh; -class NavMeshTile +/// +/// The navigation mesh tile data. +/// +class FLAXENGINE_API NavMeshTile { public: int32 X; int32 Y; int32 Layer; - NavigationScene* Scene; + NavMesh* NavMesh; BytesContainer Data; }; -class NavMeshRuntime +/// +/// The navigation mesh runtime object that builds the navmesh from all loaded scenes. +/// +class FLAXENGINE_API NavMeshRuntime { public: - static NavMeshRuntime* Get(); + // Gets the navigation mesh runtime for a given navmesh name. Return null if missing. + static NavMeshRuntime* Get(const StringView& navMeshName); + + // Gets the navigation mesh runtime for a given agent properties trying to pick the best matching navmesh. + static NavMeshRuntime* Get(const NavAgentProperties& agentProperties); + + // Gets the navigation mesh runtime for a given navmesh properties. + static NavMeshRuntime* Get(const NavMeshProperties& navMeshProperties, bool createIfMissing = false); private: @@ -36,7 +50,7 @@ private: public: - NavMeshRuntime(); + NavMeshRuntime(const NavMeshProperties& properties); ~NavMeshRuntime(); public: @@ -46,6 +60,11 @@ public: /// CriticalSection Locker; + /// + /// The navigation mesh properties. + /// + NavMeshProperties Properties; + /// /// Gets the size of the tile (in world-units). Returns zero if not initialized yet. /// @@ -66,6 +85,43 @@ public: int32 GetTilesCapacity() const; +public: + + /// + /// Finds the distance from the specified start position to the nearest polygon wall. + /// + /// The start position. + /// The result hit information. Valid only when query succeed. + /// The maximum distance to search for wall (search radius). + /// True if ray hits an matching object, otherwise false. + bool FindDistanceToWall(const Vector3& startPosition, NavMeshHit& hitInfo, float maxDistance = MAX_float) const; + + /// + /// Finds the path between the two positions presented as a list of waypoints stored in the corners array. + /// + /// The start position. + /// The end position. + /// The result path. + /// True if found valid path between given two points (it may be partial), otherwise false if failed. + bool FindPath(const Vector3& startPosition, const Vector3& endPosition, Array& resultPath) const; + + /// + /// Projects the point to nav mesh surface (finds the nearest polygon). + /// + /// The source point. + /// The result position on the navmesh (valid only if method returns true). + /// True if found valid location on the navmesh, otherwise false. + bool ProjectPoint(const Vector3& point, Vector3& result) const; + + /// + /// Casts a 'walkability' ray along the surface of the navigation mesh from the start position toward the end position. + /// + /// The start position. + /// The end position. + /// The result hit information. Valid only when query succeed. + /// True if ray hits an matching object, otherwise false. + bool RayCast(const Vector3& startPosition, const Vector3& endPosition, NavMeshHit& hitInfo) const; + public: /// @@ -83,21 +139,21 @@ public: /// /// Adds the tiles from the given scene to the runtime navmesh. /// - /// The navigation scene. - void AddTiles(NavigationScene* scene); + /// The navigation mesh. + void AddTiles(NavMesh* navMesh); /// /// Adds the tile from the given scene to the runtime navmesh. /// - /// The navigation scene. + /// The navigation mesh. /// The tile data. - void AddTile(NavigationScene* scene, NavMeshTileData& tileData); + void AddTile(NavMesh* navMesh, NavMeshTileData& tileData); /// /// Removes all the tiles from the navmesh that has been added from the given navigation scene. /// - /// The scene. - void RemoveTiles(NavigationScene* scene); + /// The navigation mesh. + void RemoveTiles(NavMesh* navMesh); /// /// Removes the tile from the navmesh. @@ -114,6 +170,10 @@ public: /// The user data passed to the callback method. void RemoveTiles(bool (*prediction)(const NavMeshRuntime* navMesh, const NavMeshTile& tile, void* customData), void* userData); +#if COMPILE_WITH_DEBUG_DRAW + void DebugDraw(); +#endif + /// /// Releases the navmesh. /// @@ -121,5 +181,5 @@ public: private: - void AddTileInternal(NavigationScene* scene, NavMeshTileData& tileData); + void AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData); }; diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 6decb4be7..d146e9c1e 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -11,21 +11,90 @@ #include "Engine/Level/Scene/Scene.h" #include "Engine/Engine/EngineService.h" #include "Engine/Profiler/ProfilerCPU.h" -#include +#include "Engine/Serialization/Serialization.h" #include -#include - -#define DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL 50.0f -#define DEFAULT_NAV_QUERY_EXTENT_VERTICAL 250.0f +#include namespace { - NavMeshRuntime* _navMesh; + Array> NavMeshes; } -NavMeshRuntime* NavMeshRuntime::Get() +NavMeshRuntime* NavMeshRuntime::Get(const StringView& navMeshName) { - return ::_navMesh; + NavMeshRuntime* result = nullptr; + for (auto navMesh : NavMeshes) + { + if (navMesh->Properties.Name == navMeshName) + { + result = navMesh; + break; + } + } + return result; +} + +NavMeshRuntime* NavMeshRuntime::Get(const NavAgentProperties& agentProperties) +{ + NavMeshRuntime* result = nullptr; + // TODO: maybe build lookup table for agentProperties -> navMesh to improve perf on frequent calls? + float bestAgentRadiusDiff = -MAX_float; + float bestAgentHeightDiff = -MAX_float; + bool bestIsValid = false; + for (auto navMesh : NavMeshes) + { + const auto& navMeshProperties = navMesh->Properties; + const float agentRadiusDiff = navMeshProperties.Agent.Radius - agentProperties.Radius; + const float agentHeightDiff = navMeshProperties.Agent.Height - agentProperties.Height; + const bool isValid = agentRadiusDiff >= 0.0f && agentHeightDiff >= 0.0f; + + // NavMesh must be valid for an agent and be first valid or have better properties than the best matching result so far + if (isValid + && + ( + !bestIsValid + || + (agentRadiusDiff + agentHeightDiff < bestAgentRadiusDiff + bestAgentHeightDiff) + ) + ) + { + result = navMesh; + bestIsValid = true; + bestAgentRadiusDiff = agentRadiusDiff; + bestAgentHeightDiff = agentHeightDiff; + } + } + return result; +} + +NavMeshRuntime* NavMeshRuntime::Get(const NavMeshProperties& navMeshProperties, bool createIfMissing) +{ + NavMeshRuntime* result = nullptr; + for (auto navMesh : NavMeshes) + { + if (navMesh->Properties == navMeshProperties) + { + result = navMesh; + break; + } + } + if (!result && createIfMissing) + { + // Create a new navmesh + result = New(navMeshProperties); + NavMeshes.Add(result); + } + return result; +} + +bool NavAgentProperties::operator==(const NavAgentProperties& other) const +{ + return Math::NearEqual(Radius, other.Radius) && Math::NearEqual(Height, other.Height) && Math::NearEqual(StepHeight, other.StepHeight) && Math::NearEqual(MaxSlopeAngle, other.MaxSlopeAngle); +} + +bool NavMeshProperties::operator==(const NavMeshProperties& other) const +{ + return Name == other.Name && Quaternion::NearEqual(Rotation, other.Rotation, 0.001f) && Agent == other.Agent; } class NavigationService : public EngineService @@ -59,17 +128,59 @@ void* rcAllocDefault(size_t size, rcAllocHint) return Allocator::Allocate(size); } +NavigationSettings::NavigationSettings() +{ + NavMeshes.Resize(1); + auto& navMesh = NavMeshes[0]; + navMesh.Name = TEXT("Default"); +} + IMPLEMENT_SETTINGS_GETTER(NavigationSettings, Navigation); +void NavigationSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + DESERIALIZE(AutoAddMissingNavMeshes); + DESERIALIZE(AutoRemoveMissingNavMeshes); + DESERIALIZE(CellHeight); + DESERIALIZE(CellSize); + DESERIALIZE(TileSize); + DESERIALIZE(MinRegionArea); + DESERIALIZE(MergeRegionArea); + DESERIALIZE(MaxEdgeLen); + DESERIALIZE(MaxEdgeError); + DESERIALIZE(DetailSamplingDist); + DESERIALIZE(MaxDetailSamplingError); + if (modifier->EngineBuild >= 6215) + { + DESERIALIZE(NavMeshes); + } + else + { + // [Deprecated on 12.01.2021, expires on 12.01.2022] + float WalkableRadius = 34.0f; + float WalkableHeight = 144.0f; + float WalkableMaxClimb = 35.0f; + float WalkableMaxSlopeAngle = 60.0f; + DESERIALIZE(WalkableRadius); + DESERIALIZE(WalkableHeight); + DESERIALIZE(WalkableMaxClimb); + DESERIALIZE(WalkableMaxSlopeAngle); + NavMeshes.Resize(1); + auto& navMesh = NavMeshes[0]; + navMesh.Name = TEXT("Default"); + navMesh.Agent.Radius = WalkableRadius; + navMesh.Agent.Height = WalkableHeight; + navMesh.Agent.StepHeight = WalkableMaxClimb; + navMesh.Agent.MaxSlopeAngle = WalkableMaxSlopeAngle; + } +} + bool NavigationService::Init() { // Link memory allocation calls to use engine default allocator dtAllocSetCustom(dtAllocDefault, Allocator::Free); rcAllocSetCustom(rcAllocDefault, Allocator::Free); - // Create global nav mesh - _navMesh = New(); - return false; } @@ -84,156 +195,42 @@ void NavigationService::Update() void NavigationService::Dispose() { - if (_navMesh) + // Release nav meshes + for (auto navMesh : NavMeshes) { - _navMesh->Dispose(); - Delete(_navMesh); - _navMesh = nullptr; + navMesh->Dispose(); + Delete(navMesh); } + NavMeshes.Clear(); + NavMeshes.ClearDelete(); } bool Navigation::FindDistanceToWall(const Vector3& startPosition, NavMeshHit& hitInfo, float maxDistance) { - ScopeLock lock(_navMesh->Locker); - - const auto query = _navMesh->GetNavMeshQuery(); - if (!query) - { + if (NavMeshes.IsEmpty()) return false; - } - - dtQueryFilter filter; - Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); - - dtPolyRef startPoly = 0; - query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); - if (!startPoly) - { - return false; - } - - return dtStatusSucceed(_navMesh->GetNavMeshQuery()->findDistanceToWall(startPoly, &startPosition.X, maxDistance, &filter, &hitInfo.Distance, &hitInfo.Position.X, &hitInfo.Normal.X)); + return NavMeshes.First()->FindDistanceToWall(startPosition, hitInfo, maxDistance); } bool Navigation::FindPath(const Vector3& startPosition, const Vector3& endPosition, Array& resultPath) { - resultPath.Clear(); - ScopeLock lock(_navMesh->Locker); - - const auto query = _navMesh->GetNavMeshQuery(); - if (!query) - { + if (NavMeshes.IsEmpty()) return false; - } - - dtQueryFilter filter; - Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); - - dtPolyRef startPoly = 0; - query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); - if (!startPoly) - { - return false; - } - dtPolyRef endPoly = 0; - query->findNearestPoly(&endPosition.X, &extent.X, &filter, &endPoly, nullptr); - if (!endPoly) - { - return false; - } - - dtPolyRef path[NAV_MESH_PATH_MAX_SIZE]; - int32 pathSize; - const auto findPathStatus = _navMesh->GetNavMeshQuery()->findPath(startPoly, endPoly, &startPosition.X, &endPosition.X, &filter, path, &pathSize, NAV_MESH_PATH_MAX_SIZE); - if (dtStatusFailed(findPathStatus)) - { - return false; - } - - // Check for special case, where path has not been found, and starting polygon was the one closest to the target - if (pathSize == 1 && dtStatusDetail(findPathStatus, DT_PARTIAL_RESULT)) - { - // In this case we find a point on starting polygon, that's closest to destination and store it as path end - resultPath.Resize(2); - resultPath[0] = startPosition; - resultPath[1] = startPosition; - _navMesh->GetNavMeshQuery()->closestPointOnPolyBoundary(startPoly, &endPosition.X, &resultPath[1].X); - } - else - { - int straightPathCount = 0; - resultPath.EnsureCapacity(NAV_MESH_PATH_MAX_SIZE); - const auto findStraightPathStatus = _navMesh->GetNavMeshQuery()->findStraightPath(&startPosition.X, &endPosition.X, path, pathSize, (float*)resultPath.Get(), nullptr, nullptr, &straightPathCount, resultPath.Capacity(), DT_STRAIGHTPATH_AREA_CROSSINGS); - if (dtStatusFailed(findStraightPathStatus)) - { - return false; - } - resultPath.Resize(straightPathCount); - } - - return true; + return NavMeshes.First()->FindPath(startPosition, endPosition, resultPath); } bool Navigation::ProjectPoint(const Vector3& point, Vector3& result) { - ScopeLock lock(_navMesh->Locker); - - const auto query = _navMesh->GetNavMeshQuery(); - if (!query) - { + if (NavMeshes.IsEmpty()) return false; - } - - dtQueryFilter filter; - Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); - - dtPolyRef startPoly = 0; - query->findNearestPoly(&point.X, &extent.X, &filter, &startPoly, &result.X); - if (!startPoly) - { - return false; - } - - return true; + return NavMeshes.First()->ProjectPoint(point, result); } bool Navigation::RayCast(const Vector3& startPosition, const Vector3& endPosition, NavMeshHit& hitInfo) { - ScopeLock lock(_navMesh->Locker); - - const auto query = _navMesh->GetNavMeshQuery(); - if (!query) - { + if (NavMeshes.IsEmpty()) return false; - } - - dtQueryFilter filter; - Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); - - dtPolyRef startPoly = 0; - query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); - if (!startPoly) - { - return false; - } - - dtRaycastHit hit; - hit.path = nullptr; - hit.maxPath = 0; - const bool result = dtStatusSucceed(_navMesh->GetNavMeshQuery()->raycast(startPoly, &startPosition.X, &endPosition.X, &filter, 0, &hit)); - if (hit.t >= MAX_float) - { - hitInfo.Position = endPosition; - hitInfo.Distance = 0; - } - else - { - hitInfo.Position = startPosition + (endPosition - startPosition) * hit.t; - hitInfo.Distance = hit.t; - } - hitInfo.Normal = *(Vector3*)&hit.hitNormal; - - return result; + return NavMeshes.First()->RayCast(startPosition, endPosition, hitInfo); } #if COMPILE_WITH_NAV_MESH_BUILDER @@ -260,96 +257,13 @@ void Navigation::BuildNavMesh(Scene* scene, const BoundingBox& dirtyBounds, floa #endif -#if USE_EDITOR - -#include "Engine/Debug/DebugDraw.h" - -void DrawPoly(const dtMeshTile& tile, const dtPoly& poly) -{ - const unsigned int ip = (unsigned int)(&poly - tile.polys); - const dtPolyDetail& pd = tile.detailMeshes[ip]; - const float DrawOffsetY = 20.0f; - - for (int i = 0; i < pd.triCount; i++) - { - Vector3 v[3]; - const unsigned char* t = &tile.detailTris[(pd.triBase + i) * 4]; - - for (int k = 0; k < 3; k++) - { - if (t[k] < poly.vertCount) - { - v[k] = *(Vector3*)&tile.verts[poly.verts[t[k]] * 3]; - } - else - { - v[k] = *(Vector3*)&tile.detailVerts[(pd.vertBase + t[k] - poly.vertCount) * 3]; - } - } - - v[0].Y += DrawOffsetY; - v[1].Y += DrawOffsetY; - v[2].Y += DrawOffsetY; - - DEBUG_DRAW_TRIANGLE(v[0], v[1], v[2], Color::Green * 0.5f, 0, true); - } - - for (int k = 0; k < pd.triCount; k++) - { - const unsigned char* t = &tile.detailTris[(pd.triBase + k) * 4]; - Vector3 v[3]; - - for (int m = 0; m < 3; m++) - { - if (t[m] < poly.vertCount) - v[m] = *(Vector3*)&tile.verts[poly.verts[t[m]] * 3]; - else - v[m] = *(Vector3*)&tile.detailVerts[(pd.vertBase + (t[m] - poly.vertCount)) * 3]; - } - - v[0].Y += DrawOffsetY; - v[1].Y += DrawOffsetY; - v[2].Y += DrawOffsetY; - - for (int m = 0, n = 2; m < 3; n = m++) - { - // Skip inner detail edges - if (((t[3] >> (n * 2)) & 0x3) == 0) - continue; - - DEBUG_DRAW_LINE(v[n], v[m], Color::YellowGreen, 0, true); - } - } -} +#if COMPILE_WITH_DEBUG_DRAW void Navigation::DrawNavMesh() { - if (!_navMesh) - return; - - ScopeLock lock(_navMesh->Locker); - - const dtNavMesh* dtNavMesh = _navMesh->GetNavMesh(); - const int tilesCount = dtNavMesh ? dtNavMesh->getMaxTiles() : 0; - if (tilesCount == 0) - return; - - for (int tileIndex = 0; tileIndex < tilesCount; tileIndex++) + for (auto navMesh : NavMeshes) { - const dtMeshTile* tile = dtNavMesh->getTile(tileIndex); - if (!tile->header) - continue; - - //DebugDraw::DrawWireBox(*(BoundingBox*)&tile->header->bmin[0], Color::CadetBlue); - - for (int i = 0; i < tile->header->polyCount; i++) - { - const dtPoly* poly = &tile->polys[i]; - if (poly->getType() != DT_POLYTYPE_GROUND) - continue; - - DrawPoly(*tile, *poly); - } + navMesh->DebugDraw(); } } diff --git a/Source/Engine/Navigation/Navigation.h b/Source/Engine/Navigation/Navigation.h index ba15a9ff7..2557e31e8 100644 --- a/Source/Engine/Navigation/Navigation.h +++ b/Source/Engine/Navigation/Navigation.h @@ -6,8 +6,6 @@ class Scene; -#define NAV_MESH_PATH_MAX_SIZE 200 - /// /// The navigation service used for path finding and agents navigation system. /// @@ -88,7 +86,7 @@ public: #endif -#if USE_EDITOR +#if COMPILE_WITH_DEBUG_DRAW /// /// Draws the navigation for all the scenes (uses DebugDraw interface). diff --git a/Source/Engine/Navigation/NavigationScene.h b/Source/Engine/Navigation/NavigationScene.h deleted file mode 100644 index ecdca0744..000000000 --- a/Source/Engine/Navigation/NavigationScene.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#pragma once - -#include "NavMeshData.h" -#include "Engine/Content/AssetReference.h" -#include "Engine/Content/Assets/RawDataAsset.h" - -class Scene; -class NavMeshBoundsVolume; - -/// -/// Scene object navigation data. -/// -class NavigationScene -{ - friend Scene; - -public: - - /// - /// Initializes a new instance of the class. - /// - /// The scene. - NavigationScene(Scene* scene); - -public: - - /// - /// The parent scene. - /// - Scene* Scene; - - /// - /// The flag used to mark that navigation data has been modified since load. Used to save runtime data to the file on scene serialization. - /// - bool IsDataDirty; - - /// - /// The navmesh tiles data. - /// - NavMeshData Data; - - /// - /// The cached navmesh data asset. - /// - AssetReference DataAsset; - -public: - - /// - /// The list of registered navigation bounds volumes (in the scene). - /// - Array Volumes; - - /// - /// Gets the total navigation volumes bounds. - /// - /// The navmesh bounds. - BoundingBox GetNavigationBounds(); - - /// - /// Finds the navigation volume bounds that have intersection with the given world-space bounding box. - /// - /// The bounds. - /// The intersecting volume or null if none found. - NavMeshBoundsVolume* FindNavigationBoundsOverlap(const BoundingBox& bounds); - -public: - - /// - /// Saves the nav mesh tiles data to the asset. Supported only in builds with assets saving enabled (eg. editor) and not during gameplay (eg. design time). - /// - void SaveNavMesh(); - - /// - /// Clears the data. - /// - void ClearData() - { - if (Data.Tiles.HasItems()) - { - IsDataDirty = true; - Data.TileSize = 0.0f; - Data.Tiles.Resize(0); - } - } - -protected: - - void OnEnable(); - void OnDisable(); - void OnDataAssetLoaded(); -}; diff --git a/Source/Engine/Navigation/NavigationSettings.cs b/Source/Engine/Navigation/NavigationSettings.cs index ff6f2aa4a..da300b3f1 100644 --- a/Source/Engine/Navigation/NavigationSettings.cs +++ b/Source/Engine/Navigation/NavigationSettings.cs @@ -1,8 +1,74 @@ // Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. +using System; +using FlaxEngine; + namespace FlaxEditor.Content.Settings { partial class NavigationSettings { + // [Deprecated on 12.01.2021, expires on 12.01.2022] + private void UpgradeToNavMeshes() + { + if (NavMeshes != null && NavMeshes.Length == 1) + return; + NavMeshes = new NavMeshProperties[1]; + ref var navMesh = ref NavMeshes[0]; + navMesh.Name = "Default"; + navMesh.Color = Color.Green; + navMesh.Rotation = Quaternion.Identity; + navMesh.Agent.Radius = 34.0f; + navMesh.Agent.Height = 144.0f; + navMesh.Agent.StepHeight = 35.0f; + navMesh.Agent.MaxSlopeAngle = 60.0f; + } + + // [Deprecated on 12.01.2021, expires on 12.01.2022] + [Serialize, Obsolete] + private float WalkableRadius + { + get => throw new Exception(); + set + { + UpgradeToNavMeshes(); + NavMeshes[0].Agent.Radius = value; + } + } + + // [Deprecated on 12.01.2021, expires on 12.01.2022] + [Serialize, Obsolete] + private float WalkableHeight + { + get => throw new Exception(); + set + { + UpgradeToNavMeshes(); + NavMeshes[0].Agent.Height = value; + } + } + + // [Deprecated on 12.01.2021, expires on 12.01.2022] + [Serialize, Obsolete] + private float WalkableMaxClimb + { + get => throw new Exception(); + set + { + UpgradeToNavMeshes(); + NavMeshes[0].Agent.StepHeight = value; + } + } + + // [Deprecated on 12.01.2021, expires on 12.01.2022] + [Serialize, Obsolete] + private float WalkableMaxSlopeAngle + { + get => throw new Exception(); + set + { + UpgradeToNavMeshes(); + NavMeshes[0].Agent.MaxSlopeAngle = value; + } + } } } diff --git a/Source/Engine/Navigation/NavigationSettings.h b/Source/Engine/Navigation/NavigationSettings.h index fe4189955..e225a2f07 100644 --- a/Source/Engine/Navigation/NavigationSettings.h +++ b/Source/Engine/Navigation/NavigationSettings.h @@ -4,7 +4,7 @@ #include "NavigationTypes.h" #include "Engine/Core/Config/Settings.h" -#include "Engine/Serialization/Serialization.h" +#include "Engine/Core/Collections/Array.h" /// /// The navigation system settings container. @@ -12,108 +12,93 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API NavigationSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(NavigationSettings); +public: + + /// + /// If checked, enables automatic navmesh actors spawning on a scenes that are using it during navigation building. + /// + API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Navigation\")") + bool AutoAddMissingNavMeshes = true; + + /// + /// If checked, enables automatic navmesh actors removing from a scenes that are not using it during navigation building. + /// + API_FIELD(Attributes="EditorOrder(110), EditorDisplay(\"Navigation\")") + bool AutoRemoveMissingNavMeshes = true; + public: /// /// The height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the vertical axis but longer build times. /// - API_FIELD(Attributes="DefaultValue(10.0f), Limit(1, 400), EditorOrder(10), EditorDisplay(\"Nav Mesh Options\")") + API_FIELD(Attributes="Limit(1, 400), EditorOrder(210), EditorDisplay(\"Nav Mesh Options\")") float CellHeight = 10.0f; /// /// The width/height of a grid cell in the navigation mesh building steps using heightfields. A lower number means higher precision on the horizontal axes but longer build times. /// - API_FIELD(Attributes="DefaultValue(30.0f), Limit(1, 400), EditorOrder(20), EditorDisplay(\"Nav Mesh Options\")") + API_FIELD(Attributes="Limit(1, 400), EditorOrder(220), EditorDisplay(\"Nav Mesh Options\")") float CellSize = 30.0f; /// /// Tile size used for Navigation mesh tiles, the final size of a tile is CellSize*TileSize. /// - API_FIELD(Attributes="DefaultValue(64), Limit(8, 4096), EditorOrder(30), EditorDisplay(\"Nav Mesh Options\")") + API_FIELD(Attributes="Limit(8, 4096), EditorOrder(230), EditorDisplay(\"Nav Mesh Options\")") int32 TileSize = 64; /// /// The minimum number of cells allowed to form isolated island areas. /// - API_FIELD(Attributes="DefaultValue(0), Limit(0, 100), EditorOrder(40), EditorDisplay(\"Nav Mesh Options\")") + API_FIELD(Attributes="Limit(0, 100), EditorOrder(240), EditorDisplay(\"Nav Mesh Options\")") int32 MinRegionArea = 0; /// /// Any regions with a span count smaller than this value will, if possible, be merged with larger regions. /// - API_FIELD(Attributes="DefaultValue(20), Limit(0, 100), EditorOrder(50), EditorDisplay(\"Nav Mesh Options\")") + API_FIELD(Attributes="Limit(0, 100), EditorOrder(250), EditorDisplay(\"Nav Mesh Options\")") int32 MergeRegionArea = 20; /// /// The maximum allowed length for contour edges along the border of the mesh. /// - API_FIELD(Attributes="DefaultValue(1200.0f), Limit(100), EditorOrder(60), EditorDisplay(\"Nav Mesh Options\", \"Max Edge Length\")") + API_FIELD(Attributes="Limit(100), EditorOrder(260), EditorDisplay(\"Nav Mesh Options\", \"Max Edge Length\")") float MaxEdgeLen = 1200.0f; /// /// The maximum distance a simplified contour's border edges should deviate from the original raw contour. /// - API_FIELD(Attributes="DefaultValue(1.3f), Limit(0.1f, 4), EditorOrder(70), EditorDisplay(\"Nav Mesh Options\")") + API_FIELD(Attributes="Limit(0.1f, 4), EditorOrder(270), EditorDisplay(\"Nav Mesh Options\")") float MaxEdgeError = 1.3f; /// /// The sampling distance to use when generating the detail mesh. /// - API_FIELD(Attributes="DefaultValue(600.0f), Limit(1), EditorOrder(80), EditorDisplay(\"Nav Mesh Options\", \"Detail Sampling Distance\")") + API_FIELD(Attributes="Limit(1), EditorOrder(280), EditorDisplay(\"Nav Mesh Options\", \"Detail Sampling Distance\")") float DetailSamplingDist = 600.0f; /// /// The maximum distance the detail mesh surface should deviate from heightfield data. /// - API_FIELD(Attributes="DefaultValue(1.0f), Limit(0, 3), EditorOrder(90), EditorDisplay(\"Nav Mesh Options\")") + API_FIELD(Attributes="Limit(0, 3), EditorOrder(290), EditorDisplay(\"Nav Mesh Options\")") float MaxDetailSamplingError = 1.0f; - /// - /// The radius of the smallest objects to traverse this nav mesh. Objects can't pass through gaps of less than twice the radius. - /// - API_FIELD(Attributes="DefaultValue(34.0f), Limit(0), EditorOrder(1000), EditorDisplay(\"Agent Options\")") - float WalkableRadius = 34.0f; +public: /// - /// The height of the smallest objects to traverse this nav mesh. Objects can't enter areas with ceilings lower than this value. + /// The configuration for navmeshes. /// - API_FIELD(Attributes="DefaultValue(144.0f), Limit(0), EditorOrder(1010), EditorDisplay(\"Agent Options\")") - float WalkableHeight = 144.0f; - - /// - /// The maximum ledge height that is considered to still be traversable. - /// - API_FIELD(Attributes="DefaultValue(35.0f), Limit(0), EditorOrder(1020), EditorDisplay(\"Agent Options\")") - float WalkableMaxClimb = 35.0f; - - /// - /// The maximum slope that is considered walkable (in degrees). Objects can't go up or down slopes higher than this value. - /// - API_FIELD(Attributes="DefaultValue(60.0f), Limit(0, 89.0f), EditorOrder(1030), EditorDisplay(\"Agent Options\")") - float WalkableMaxSlopeAngle = 60.0f; + API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Agents\")") + Array NavMeshes; public: + NavigationSettings(); + /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// static NavigationSettings* Get(); // [SettingsBase] - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override - { - DESERIALIZE(CellHeight); - DESERIALIZE(CellSize); - DESERIALIZE(TileSize); - DESERIALIZE(MinRegionArea); - DESERIALIZE(MergeRegionArea); - DESERIALIZE(MaxEdgeLen); - DESERIALIZE(MaxEdgeError); - DESERIALIZE(DetailSamplingDist); - DESERIALIZE(MaxDetailSamplingError); - DESERIALIZE(WalkableRadius); - DESERIALIZE(WalkableHeight); - DESERIALIZE(WalkableMaxClimb); - DESERIALIZE(WalkableMaxSlopeAngle); - } + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override; }; diff --git a/Source/Engine/Navigation/NavigationTypes.h b/Source/Engine/Navigation/NavigationTypes.h index a5672f342..f00c1683e 100644 --- a/Source/Engine/Navigation/NavigationTypes.h +++ b/Source/Engine/Navigation/NavigationTypes.h @@ -7,61 +7,88 @@ #include "Engine/Core/Math/Color.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Quaternion.h" +#include "Engine/Core/ISerializable.h" + +#define NAV_MESH_PATH_MAX_SIZE 200 /// /// The navigation system agent properties container for navmesh building and querying. /// -API_STRUCT() struct FLAXENGINE_API NavAgentProperties +API_STRUCT() struct FLAXENGINE_API NavAgentProperties : ISerializable { +API_AUTO_SERIALIZATION(); DECLARE_SCRIPTING_TYPE_MINIMAL(NavAgentProperties); /// /// The radius of the agent used for navigation. Agents can't pass through gaps of less than twice the radius. /// - API_FIELD() float Radius = 34.0f; + API_FIELD(Attributes="EditorOrder(0)") + float Radius = 34.0f; /// /// The height of the agent used for navigation. Agents can't enter areas with ceilings lower than this value. /// - API_FIELD() float Height = 144.0f; + API_FIELD(Attributes="EditorOrder(10)") + float Height = 144.0f; /// /// The step height used for navigation. Defines the maximum ledge height that is considered to still be traversable by the agent. /// - API_FIELD() float StepHeight = 35.0f; + API_FIELD(Attributes="EditorOrder(20)") + float StepHeight = 35.0f; /// /// The maximum slope (in degrees) that is considered walkable for navigation. Agents can't go up or down slopes higher than this value. /// - API_FIELD() float MaxSlopeAngle = 60.0f; + API_FIELD(Attributes="EditorOrder(30)") + float MaxSlopeAngle = 60.0f; + + bool operator==(const NavAgentProperties& other) const; + + bool operator!=(const NavAgentProperties& other) const + { + return !operator==(other); + } }; /// /// The navigation mesh properties container for navmesh building. /// -API_STRUCT() struct FLAXENGINE_API NavMeshProperties +API_STRUCT() struct FLAXENGINE_API NavMeshProperties : ISerializable { +API_AUTO_SERIALIZATION(); DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshProperties); /// - /// The navmesh type name (for debugging). + /// The navmesh type name. Identifies different types of the navmeshes, used to sync navmesh properties with settings asset. /// - API_FIELD() String Name; + API_FIELD(Attributes="EditorOrder(0)") + String Name; /// /// The navmesh type color (for debugging). /// - API_FIELD() Color Color; + API_FIELD(Attributes="EditorOrder(10)") + Color Color = Color::Green; /// /// The navmesh rotation applied to navigation surface. Used during building to the rotate scene geometry and to revert back result during path finding queries. Can be used to generate navmesh on walls. /// - API_FIELD() Quaternion Rotation; + API_FIELD(Attributes="EditorOrder(20)") + Quaternion Rotation = Quaternion::Identity; /// /// The properties of the agent used to generate walkable navigation surface. /// - API_FIELD() NavAgentProperties Agent; + API_FIELD(Attributes="EditorOrder(30)") + NavAgentProperties Agent; + + bool operator==(const NavMeshProperties& other) const; + + bool operator!=(const NavMeshProperties& other) const + { + return !operator==(other); + } }; /// From 29c3e2d54bb54372cb80cb25695e407e5ebebc9e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 13 Jan 2021 14:28:58 +0100 Subject: [PATCH 073/419] Fix Color doc comments --- Source/Engine/Core/Math/Color.cpp | 35 +++++++++++++++++++++++ Source/Engine/Core/Math/Color.cs | 47 +------------------------------ Source/Engine/Core/Math/Color.h | 40 ++++++-------------------- 3 files changed, 44 insertions(+), 78 deletions(-) diff --git a/Source/Engine/Core/Math/Color.cpp b/Source/Engine/Core/Math/Color.cpp index 168db71a5..5a00a52e5 100644 --- a/Source/Engine/Core/Math/Color.cpp +++ b/Source/Engine/Core/Math/Color.cpp @@ -137,6 +137,26 @@ String Color::ToHexString() const return String(result, 6); } +bool Color::IsTransparent() const +{ + return Math::IsZero(R + G + B + A); +} + +bool Color::HasOpacity() const +{ + return !Math::IsOne(A); +} + +bool Color::NearEqual(const Color& a, const Color& b) +{ + return Math::NearEqual(a.R, b.R) && Math::NearEqual(a.G, b.G) && Math::NearEqual(a.B, b.B) && Math::NearEqual(a.A, b.A); +} + +bool Color::NearEqual(const Color& a, const Color& b, float epsilon) +{ + return Math::NearEqual(a.R, b.R, epsilon) && Math::NearEqual(a.G, b.G, epsilon) && Math::NearEqual(a.B, b.B, epsilon) && Math::NearEqual(a.A, b.A, epsilon); +} + Vector3 Color::ToVector3() const { return Vector3(R, G, B); @@ -158,6 +178,21 @@ Vector3 Color::ToHSV() const return Vector3(hue, saturation, value); } +void Color::Lerp(const Color& start, const Color& end, float amount, Color& result) +{ + result.R = Math::Lerp(start.R, end.R, amount); + result.G = Math::Lerp(start.G, end.G, amount); + result.B = Math::Lerp(start.B, end.B, amount); + result.A = Math::Lerp(start.A, end.A, amount); +} + +Color Color::Lerp(const Color& start, const Color& end, float amount) +{ + Color result; + Lerp(start, end, amount, result); + return result; +} + Color Color::LinearToSrgb(const Color& linear) { #define LINEAR_TO_SRGB(value) value < 0.00313067f ? value * 12.92f : Math::Pow(value, (1.0f / 2.4f)) * 1.055f - 0.055f diff --git a/Source/Engine/Core/Math/Color.cs b/Source/Engine/Core/Math/Color.cs index 5aee4c428..a98f35126 100644 --- a/Source/Engine/Core/Math/Color.cs +++ b/Source/Engine/Core/Math/Color.cs @@ -716,7 +716,7 @@ namespace FlaxEngine } /// - /// Converts the color to HSV color space (returned as vector). + /// Gets Hue[0-360], Saturation[0-1] and Value[0-1] from RGB color. /// /// The HSV color. public Vector3 ToHSV() @@ -743,51 +743,6 @@ namespace FlaxEngine return new Vector3(hue, saturation, value); } - /// - /// Convert color from the RGB color space to HSV color space. - /// - /// Color of the RGB. - /// The output Hue. - /// The output Saturation. - /// The output Value. - public static void RGBToHSV(Color rgbColor, out float h, out float s, out float v) - { - if ((rgbColor.B > rgbColor.G) && (rgbColor.B > rgbColor.R)) - RGBToHSVHelper(4f, rgbColor.B, rgbColor.R, rgbColor.G, out h, out s, out v); - else if (rgbColor.G <= rgbColor.R) - RGBToHSVHelper(0f, rgbColor.R, rgbColor.G, rgbColor.B, out h, out s, out v); - else - RGBToHSVHelper(2f, rgbColor.G, rgbColor.B, rgbColor.R, out h, out s, out v); - } - - private static void RGBToHSVHelper(float offset, float dominantcolor, float colorone, float colortwo, out float h, out float s, out float v) - { - v = dominantcolor; - if (Mathf.IsZero(v)) - { - s = 0f; - h = 0f; - } - else - { - var single = colorone <= colortwo ? colorone : colortwo; - float vv = v - single; - if (Mathf.IsZero(vv)) - { - s = 0f; - h = offset + (colorone - colortwo); - } - else - { - s = vv / v; - h = offset + (colorone - colortwo) / vv; - } - h = h / 6f; - if (h < 0f) - h = h + 1f; - } - } - /// /// Adjusts the contrast of a color. /// diff --git a/Source/Engine/Core/Math/Color.h b/Source/Engine/Core/Math/Color.h index d11097d5c..3eee6d9f8 100644 --- a/Source/Engine/Core/Math/Color.h +++ b/Source/Engine/Core/Math/Color.h @@ -166,7 +166,7 @@ public: /// Creates RGB color from Hue[0-360], Saturation[0-1] and Value[0-1] packed to XYZ vector. /// /// The HSV color. - /// The alpha value. Default is 1. + /// The alpha value. Default is 1. /// The RGB color. static Color FromHSV(const Vector3& hsv, float alpha = 1.0f); @@ -264,28 +264,15 @@ public: } // Returns true if color is fully transparent (all components are equal zero). - bool IsTransparent() const - { - return Math::IsZero(R + G + B + A); - } + bool IsTransparent() const; // Returns true if color has opacity channel in use (different from 1). - bool HasOpacity() const - { - return !Math::IsOne(A); - } + bool HasOpacity() const; public: - static bool NearEqual(const Color& a, const Color& b) - { - return Math::NearEqual(a.R, b.R) && Math::NearEqual(a.G, b.G) & Math::NearEqual(a.B, b.B) && Math::NearEqual(a.A, b.A); - } - - static bool NearEqual(const Color& a, const Color& b, float epsilon) - { - return Math::NearEqual(a.R, b.R, epsilon) && Math::NearEqual(a.G, b.G, epsilon) & Math::NearEqual(a.B, b.B, epsilon) && Math::NearEqual(a.A, b.A, epsilon); - } + static bool NearEqual(const Color& a, const Color& b); + static bool NearEqual(const Color& a, const Color& b, float epsilon); public: @@ -296,7 +283,7 @@ public: Vector4 ToVector4() const; /// - /// Gets Hue[0-1], Saturation[0-1] and Value[0-360] from RGB color. + /// Gets Hue[0-360], Saturation[0-1] and Value[0-1] from RGB color. /// /// HSV color Vector3 ToHSV() const; @@ -308,13 +295,7 @@ public: /// The end color. /// The value between 0 and 1 indicating the weight of interpolation. /// When the method completes, contains the linear interpolation of the two colors. - static void Lerp(const Color& start, const Color& end, float amount, Color& result) - { - result.R = Math::Lerp(start.R, end.R, amount); - result.G = Math::Lerp(start.G, end.G, amount); - result.B = Math::Lerp(start.B, end.B, amount); - result.A = Math::Lerp(start.A, end.A, amount); - } + static void Lerp(const Color& start, const Color& end, float amount, Color& result); /// /// Performs a linear interpolation between two colors. @@ -323,12 +304,7 @@ public: /// The end color. /// The value between 0 and 1 indicating the weight of interpolation. /// The linear interpolation of the two colors. - static Color Lerp(const Color& start, const Color& end, float amount) - { - Color result; - Lerp(start, end, amount, result); - return result; - } + static Color Lerp(const Color& start, const Color& end, float amount); // Converts a [0.0, 1.0] linear value into a [0.0, 1.0] sRGB value. static Color LinearToSrgb(const Color& linear); From 88bddfb1417e7e33f02b0ae2b1f89e00848c587c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 13 Jan 2021 14:49:50 +0100 Subject: [PATCH 074/419] Add navmesh name to logs for better debugging --- Source/Engine/Navigation/NavMeshRuntime.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index 4ca3b7041..017d35383 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -223,7 +223,7 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) newCapacity = 32; } - LOG(Info, "Resizing navmesh from {0} to {1} tiles capacity", capacity, newCapacity); + LOG(Info, "Resizing navmesh {2} from {0} to {1} tiles capacity", capacity, newCapacity, Properties.Name); // Ensure to have size assigned ASSERT(_tileSize != 0); @@ -238,7 +238,7 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) _navMesh = dtAllocNavMesh(); if (dtStatusFailed(_navMeshQuery->init(_navMesh, MAX_NODES))) { - LOG(Fatal, "Failed to initialize nav mesh."); + LOG(Error, "Failed to initialize navmesh {0}.", Properties.Name); } // Prepare parameters @@ -255,7 +255,7 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) // Initialize nav mesh if (dtStatusFailed(_navMesh->init(¶ms))) { - LOG(Fatal, "Navmesh init failed."); + LOG(Error, "Navmesh {0} init failed.", Properties.Name); return; } @@ -276,7 +276,7 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) #endif if (dtStatusFailed(_navMesh->addTile(data, dataSize, flags, 0, nullptr))) { - LOG(Warning, "Could not add tile to navmesh."); + LOG(Warning, "Could not add tile to navmesh {0}.", Properties.Name); } } } @@ -298,7 +298,7 @@ void NavMeshRuntime::AddTiles(NavMesh* navMesh) { if (Math::NotNearEqual(data.TileSize, _tileSize)) { - LOG(Warning, "Cannot add navigation scene tiles to the navmesh. Navmesh tile size: {0}, input tiles size: {1}", _tileSize, data.TileSize); + LOG(Warning, "Cannot add navigation scene tiles to the navmesh {2}. Navmesh tile size: {0}, input tiles size: {1}", _tileSize, data.TileSize, Properties.Name); return; } } @@ -331,7 +331,7 @@ void NavMeshRuntime::AddTile(NavMesh* navMesh, NavMeshTileData& tileData) { if (Math::NotNearEqual(data.TileSize, _tileSize)) { - LOG(Warning, "Cannot add navigation scene tile to the navmesh. Navmesh tile size: {0}, input tile size: {1}", _tileSize, data.TileSize); + LOG(Warning, "Cannot add navigation scene tile to the navmesh {2}. Navmesh tile size: {0}, input tile size: {1}", _tileSize, data.TileSize, Properties.Name); return; } } @@ -375,7 +375,7 @@ void NavMeshRuntime::RemoveTile(int32 x, int32 y, int32 layer) if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr))) { - LOG(Warning, "Failed to remove tile from navmesh."); + LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name); } for (int32 i = 0; i < _tiles.Count(); i++) @@ -408,13 +408,13 @@ void NavMeshRuntime::RemoveTiles(bool (* prediction)(const NavMeshRuntime* navMe const auto tileRef = _navMesh->getTileRefAt(tile.X, tile.Y, tile.Layer); if (tileRef == 0) { - LOG(Warning, "Missing navmesh tile at {0}x{1}, layer: {2}", tile.X, tile.Y, tile.Layer); + LOG(Warning, "Missing navmesh {3} tile at {0}x{1}, layer: {2}", tile.X, tile.Y, tile.Layer, Properties.Name); } else { if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr))) { - LOG(Warning, "Failed to remove tile from navmesh."); + LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name); } } @@ -579,6 +579,6 @@ void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData #endif if (dtStatusFailed(_navMesh->addTile(data, dataSize, flags, 0, nullptr))) { - LOG(Warning, "Could not add tile to navmesh."); + LOG(Warning, "Could not add tile to navmesh {0}.", Properties.Name); } } From d1a282e2288525517f9247789df9ed082b95c594 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 13 Jan 2021 14:50:12 +0100 Subject: [PATCH 075/419] Fix removing unused navmesh tiles on whole scene rebuild --- Source/Engine/Navigation/NavMeshBuilder.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index dca9a445c..7e440648f 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -635,7 +635,7 @@ void BuildTileAsync(NavMesh* navMesh, int32 x, int32 y, rcConfig& config, const task->Start(); } -void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBounds) +void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBounds, bool rebuild) { const float tileSize = GetTileSize(); NavMeshRuntime* runtime = navMesh->GetRuntime(); @@ -655,12 +655,15 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo PROFILE_CPU_NAMED("Prepare"); // Prepare scene data and navmesh - if (Math::NotNearEqual(navMesh->Data.TileSize, tileSize)) + rebuild |= Math::NotNearEqual(navMesh->Data.TileSize, tileSize); + if (rebuild) { + // Remove all tiles from navmesh runtime runtime->RemoveTiles(navMesh); runtime->SetTileSize(tileSize); runtime->EnsureCapacity(tilesX * tilesY); + // Remove all tiles from navmesh data navMesh->Data.TileSize = tileSize; navMesh->Data.Tiles.Clear(); navMesh->Data.Tiles.EnsureCapacity(tilesX * tilesX); @@ -668,7 +671,7 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo } else { - // Prepare navmesh + // Ensure to have enough memory for tiles runtime->SetTileSize(tileSize); runtime->EnsureCapacity(tilesX * tilesY); } @@ -700,7 +703,7 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo } } -void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds) +void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild) { auto settings = NavigationSettings::Get(); @@ -738,7 +741,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds) // Build all navmeshes on the scene for (NavMesh* navMesh : scene->NavigationMeshes) { - BuildDirtyBounds(scene, navMesh, dirtyBounds); + BuildDirtyBounds(scene, navMesh, dirtyBounds, rebuild); } // Remove unused navmeshes @@ -772,7 +775,7 @@ void BuildWholeScene(Scene* scene) // Compute total navigation area bounds const BoundingBox worldBounds = scene->GetNavigationBounds(); - BuildDirtyBounds(scene, worldBounds); + BuildDirtyBounds(scene, worldBounds, true); } void ClearNavigation(Scene* scene) @@ -814,7 +817,7 @@ void NavMeshBuilder::Update() } else { - BuildDirtyBounds(scene, req.DirtyBounds); + BuildDirtyBounds(scene, req.DirtyBounds, false); } } } From af692605ab77924292049ba22b588eea8a1a8988 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 13 Jan 2021 15:21:50 +0100 Subject: [PATCH 076/419] Add option to hide navmesh in editor debug view --- Source/Engine/Navigation/NavMesh.h | 9 +++++++++ Source/Engine/Navigation/Navigation.cpp | 26 +++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/Source/Engine/Navigation/NavMesh.h b/Source/Engine/Navigation/NavMesh.h index bd0eab703..f1a901967 100644 --- a/Source/Engine/Navigation/NavMesh.h +++ b/Source/Engine/Navigation/NavMesh.h @@ -34,6 +34,15 @@ public: /// AssetReference DataAsset; +#if USE_EDITOR + + /// + /// If checked, the navmesh will be drawn in debug view when showing navigation data. + /// + API_FIELD(Attributes="EditorOrder(-10), EditorDisplay(\"Nav Mesh\")") bool ShowDebugDraw = true; + +#endif + /// /// The navigation mesh properties. /// diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index d146e9c1e..7f8ca5e9a 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -8,7 +8,12 @@ #include "Engine/Content/Content.h" #include "Engine/Content/JsonAsset.h" #include "Engine/Threading/Threading.h" +#if USE_EDITOR +#include "Engine/Level/Level.h" #include "Engine/Level/Scene/Scene.h" +#endif +#include "NavMesh.h" + #include "Engine/Engine/EngineService.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Serialization/Serialization.h" @@ -263,6 +268,27 @@ void Navigation::DrawNavMesh() { for (auto navMesh : NavMeshes) { +#if USE_EDITOR + // Skip drawing if any of the navmeshes on scene has disabled ShowDebugDraw option + bool skip = false; + for (auto scene : Level::Scenes) + { + for (auto e : scene->NavigationMeshes) + { + if (e->Properties == navMesh->Properties) + { + if (!e->ShowDebugDraw) + { + skip = true; + } + break; + } + } + } + if (skip) + continue; +#endif + navMesh->DebugDraw(); } } From b2a2652b5653345feebdeea69dcddd471c14ec53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 13 Jan 2021 15:35:53 +0100 Subject: [PATCH 077/419] Fix not using hardcoded order for showing Actor main properties in editor --- .../CustomEditors/Dedicated/ActorEditor.cs | 37 ------------------- .../CustomEditors/Editors/GenericEditor.cs | 13 +++---- Source/Engine/Level/Actor.cpp | 10 +++++ Source/Engine/Level/Actor.h | 24 ++++-------- Source/Engine/Navigation/NavMesh.h | 4 +- 5 files changed, 26 insertions(+), 62 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index ad974c55a..924f5aeb3 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -24,43 +24,6 @@ namespace FlaxEditor.CustomEditors.Dedicated { private Guid _linkedPrefabId; - /// - protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item) - { - // Note: we cannot specify actor properties editor types directly because we want to keep editor classes in FlaxEditor assembly - int order = item.Order?.Order ?? int.MinValue; - switch (order) - { - // Override static flags editor - case -80: - item.CustomEditor = new CustomEditorAttribute(typeof(ActorStaticFlagsEditor)); - break; - - // Override layer editor - case -69: - item.CustomEditor = new CustomEditorAttribute(typeof(ActorLayerEditor)); - break; - - // Override tag editor - case -68: - item.CustomEditor = new CustomEditorAttribute(typeof(ActorTagEditor)); - break; - - // Override position/scale editor - case -30: - case -10: - item.CustomEditor = new CustomEditorAttribute(typeof(ActorTransformEditor.PositionScaleEditor)); - break; - - // Override orientation editor - case -20: - item.CustomEditor = new CustomEditorAttribute(typeof(ActorTransformEditor.OrientationEditor)); - break; - } - - base.SpawnProperty(itemLayout, itemValues, item); - } - /// protected override List GetItemsForType(ScriptType type) { diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs index 485a9b735..9e7295e7e 100644 --- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs @@ -517,23 +517,22 @@ namespace FlaxEditor.CustomEditors.Editors if (item.Header != null) itemLayout.Header(item.Header.Text); - // Peek values - ValueContainer itemValues; try { - itemValues = item.GetValues(Values); + // Peek values + ValueContainer itemValues = item.GetValues(Values); + + // Spawn property editor + SpawnProperty(itemLayout, itemValues, item); } catch (Exception ex) { - Editor.LogWarning("Failed to get object values for item " + item); + Editor.LogWarning("Failed to setup values and UI for item " + item); Editor.LogWarning(ex.Message); Editor.LogWarning(ex.StackTrace); return; } - // Spawn property editor - SpawnProperty(itemLayout, itemValues, item); - // Expand all parent groups if need to if (item.ExpandGroups) { diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 900d802ed..30ca06daf 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -597,6 +597,11 @@ void Actor::SetDirection(const Vector3& value) SetOrientation(orientation); } +void Actor::ResetLocalTransform() +{ + SetLocalTransform(Transform::Identity); +} + void Actor::SetLocalTransform(const Transform& value) { CHECK(!value.IsNanOrInfinity()); @@ -1258,6 +1263,11 @@ Script* Actor::GetScriptByPrefabObjectId(const Guid& prefabObjectId) const return result; } +bool Actor::IsPrefabRoot() const +{ + return _isPrefabRoot != 0; +} + Actor* Actor::FindActor(const StringView& name) const { Actor* result = nullptr; diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 334021c28..88da10f0e 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -79,7 +79,7 @@ public: /// /// Gets the object layer (index). Can be used for selective rendering or ignoring raycasts. /// - API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-69)") + API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-69), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorLayerEditor\")") FORCE_INLINE int32 GetLayer() const { return _layer; @@ -123,7 +123,7 @@ public: /// /// Gets the name of the tag. /// - API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-68)") + API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-68), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTagEditor\")") const String& GetTag() const; /// @@ -355,7 +355,7 @@ public: /// /// Gets the actor static fags. /// - API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-80)") + API_PROPERTY(Attributes="NoAnimate, EditorDisplay(\"General\"), EditorOrder(-80), CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorStaticFlagsEditor\")") FORCE_INLINE StaticFlags GetStaticFlags() const { return _staticFlags; @@ -500,10 +500,7 @@ public: /// /// Resets the actor local transform. /// - FORCE_INLINE void ResetLocalTransform() - { - SetLocalTransform(Transform::Identity); - } + void ResetLocalTransform(); /// /// Gets local transform of the actor in parent actor space. @@ -523,7 +520,7 @@ public: /// /// Gets local position of the actor in parent actor space. /// - API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Position\"), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorOrder(-30), NoSerialize") + API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Position\"), DefaultValue(typeof(Vector3), \"0,0,0\"), EditorOrder(-30), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+PositionScaleEditor\")") FORCE_INLINE Vector3 GetLocalPosition() const { return _localTransform.Translation; @@ -538,7 +535,7 @@ public: /// /// Gets local rotation of the actor in parent actor space. /// - API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Rotation\"), DefaultValue(typeof(Quaternion), \"0,0,0,1\"), EditorOrder(-20), NoSerialize") + API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Rotation\"), DefaultValue(typeof(Quaternion), \"0,0,0,1\"), EditorOrder(-20), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+OrientationEditor\")") FORCE_INLINE Quaternion GetLocalOrientation() const { return _localTransform.Orientation; @@ -553,7 +550,7 @@ public: /// /// Gets local scale vector of the actor in parent actor space. /// - API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Scale\"), DefaultValue(typeof(Vector3), \"1,1,1\"), Limit(float.MinValue, float.MaxValue, 0.01f), EditorOrder(-10), NoSerialize") + API_PROPERTY(Attributes="EditorDisplay(\"Transform\", \"Scale\"), DefaultValue(typeof(Vector3), \"1,1,1\"), Limit(float.MinValue, float.MaxValue, 0.01f), EditorOrder(-10), NoSerialize, CustomEditorAlias(\"FlaxEditor.CustomEditors.Editors.ActorTransformEditor+PositionScaleEditor\")") FORCE_INLINE Vector3 GetLocalScale() const { return _localTransform.Scale; @@ -708,15 +705,10 @@ public: /// The script or null. Script* GetScriptByPrefabObjectId(const Guid& prefabObjectId) const; -public: - /// /// Gets a value indicating whether this actor is a prefab instance root object. /// - API_PROPERTY() FORCE_INLINE bool IsPrefabRoot() const - { - return _isPrefabRoot != 0; - } + API_PROPERTY() bool IsPrefabRoot() const; public: diff --git a/Source/Engine/Navigation/NavMesh.h b/Source/Engine/Navigation/NavMesh.h index f1a901967..c9096d323 100644 --- a/Source/Engine/Navigation/NavMesh.h +++ b/Source/Engine/Navigation/NavMesh.h @@ -39,14 +39,14 @@ public: /// /// If checked, the navmesh will be drawn in debug view when showing navigation data. /// - API_FIELD(Attributes="EditorOrder(-10), EditorDisplay(\"Nav Mesh\")") bool ShowDebugDraw = true; + API_FIELD(Attributes="EditorOrder(1), EditorDisplay(\"Nav Mesh\")") bool ShowDebugDraw = true; #endif /// /// The navigation mesh properties. /// - API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Nav Mesh\")") NavMeshProperties Properties; + API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Nav Mesh\")") NavMeshProperties Properties; public: From 93aa265b209b12a3df79e52d31adb7050b6a5654 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Jan 2021 13:08:53 +0100 Subject: [PATCH 078/419] Add support for rotated navmeshes --- Source/Engine/Navigation/NavMeshBuilder.cpp | 116 +++++++++++++------- Source/Engine/Navigation/NavMeshRuntime.cpp | 72 +++++++++--- 2 files changed, 137 insertions(+), 51 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 7e440648f..e9c8cb578 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -55,7 +55,8 @@ struct OffMeshLink struct NavigationSceneRasterization { - BoundingBox TileBounds; + BoundingBox TileBoundsNavMesh; + Matrix WorldToNavMesh; rcContext* Context; rcConfig* Config; rcHeightfield* Heightfield; @@ -63,9 +64,12 @@ struct NavigationSceneRasterization Array VertexBuffer; Array IndexBuffer; Array* OffMeshLinks; + const bool IsWorldToNavMeshIdentity; - NavigationSceneRasterization(const BoundingBox& tileBounds, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks) - : TileBounds(tileBounds) + NavigationSceneRasterization(const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks) + : TileBoundsNavMesh(tileBoundsNavMesh) + , WorldToNavMesh(worldToNavMesh) + , IsWorldToNavMeshIdentity(worldToNavMesh.IsIdentity()) { Context = context; Config = config; @@ -82,23 +86,47 @@ struct NavigationSceneRasterization return; // Rasterize triangles - for (int32 i0 = 0; i0 < ib.Count();) + if (IsWorldToNavMeshIdentity) { - auto v0 = vb[ib[i0++]]; - auto v1 = vb[ib[i0++]]; - auto v2 = vb[ib[i0++]]; + // Faster path + for (int32 i0 = 0; i0 < ib.Count();) + { + auto v0 = vb[ib[i0++]]; + auto v1 = vb[ib[i0++]]; + auto v2 = vb[ib[i0++]]; - auto n = Vector3::Cross(v0 - v1, v0 - v2); - n.Normalize(); - const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : 0; - rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield); + auto n = Vector3::Cross(v0 - v1, v0 - v2); + n.Normalize(); + const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : 0; + rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield); + } + } + else + { + // Transform vertices from world space into the navmesh space + const Matrix worldToNavMesh = WorldToNavMesh; + for (int32 i0 = 0; i0 < ib.Count();) + { + auto v0 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh); + auto v1 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh); + auto v2 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh); + + auto n = Vector3::Cross(v0 - v1, v0 - v2); + n.Normalize(); + const char area = n.Y > WalkableThreshold ? RC_WALKABLE_AREA : RC_NULL_AREA; + rcRasterizeTriangle(Context, &v0.X, &v1.X, &v2.X, area, *Heightfield); + } } } static bool Walk(Actor* actor, NavigationSceneRasterization& e) { // Early out if object is not intersecting with the tile bounds or is not using navigation - if (!actor->GetIsActive() || !(actor->GetStaticFlags() & StaticFlags::Navigation) || !actor->GetBox().Intersects(e.TileBounds)) + if (!actor->GetIsActive() || !(actor->GetStaticFlags() & StaticFlags::Navigation)) + return true; + BoundingBox actorBoxNavMesh; + BoundingBox::Transform(actor->GetBox(), e.WorldToNavMesh, actorBoxNavMesh); + if (!actorBoxNavMesh.Intersects(e.TileBoundsNavMesh)) return true; // Prepare buffers (for triangles) @@ -138,7 +166,9 @@ struct NavigationSceneRasterization for (int32 patchIndex = 0; patchIndex < terrain->GetPatchesCount(); patchIndex++) { const auto patch = terrain->GetPatch(patchIndex); - if (!patch->GetBounds().Intersects(e.TileBounds)) + BoundingBox patchBoundsNavMesh; + BoundingBox::Transform(patch->GetBounds(), e.WorldToNavMesh, patchBoundsNavMesh); + if (!patchBoundsNavMesh.Intersects(e.TileBoundsNavMesh)) continue; patch->ExtractCollisionGeometry(vb, ib); @@ -152,7 +182,9 @@ struct NavigationSceneRasterization OffMeshLink link; link.Start = navLink->GetTransform().LocalToWorld(navLink->Start); + Vector3::Transform(link.Start, e.WorldToNavMesh, link.Start); link.End = navLink->GetTransform().LocalToWorld(navLink->End); + Vector3::Transform(link.End, e.WorldToNavMesh, link.End); link.Radius = navLink->Radius; link.BiDir = navLink->BiDirectional; link.Id = GetHash(navLink->GetID()); @@ -167,26 +199,26 @@ struct NavigationSceneRasterization } }; -void RasterizeGeometry(const BoundingBox& tileBounds, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks) +void RasterizeGeometry(const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks) { PROFILE_CPU_NAMED("RasterizeGeometry"); - NavigationSceneRasterization rasterization(tileBounds, context, config, heightfield, offMeshLinks); + NavigationSceneRasterization rasterization(tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks); Function treeWalkFunction(NavigationSceneRasterization::Walk); SceneQuery::TreeExecute(treeWalkFunction, rasterization); } // Builds navmesh tile bounds and check if there are any valid navmesh volumes at that tile location // Returns true if tile is intersecting with any navmesh bounds volume actor - which means tile is in use -bool GetNavMeshTileBounds(Scene* scene, int32 x, int32 y, float tileSize, BoundingBox& tileBounds) +bool GetNavMeshTileBounds(Scene* scene, int32 x, int32 y, float tileSize, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh) { // Build initial tile bounds (with infinite extent) - tileBounds.Minimum.X = (float)x * tileSize; - tileBounds.Minimum.Y = -NAV_MESH_TILE_MAX_EXTENT; - tileBounds.Minimum.Z = (float)y * tileSize; - tileBounds.Maximum.X = tileBounds.Minimum.X + tileSize; - tileBounds.Maximum.Y = NAV_MESH_TILE_MAX_EXTENT; - tileBounds.Maximum.Z = tileBounds.Minimum.Z + tileSize; + tileBoundsNavMesh.Minimum.X = (float)x * tileSize; + tileBoundsNavMesh.Minimum.Y = -NAV_MESH_TILE_MAX_EXTENT; + tileBoundsNavMesh.Minimum.Z = (float)y * tileSize; + tileBoundsNavMesh.Maximum.X = tileBoundsNavMesh.Minimum.X + tileSize; + tileBoundsNavMesh.Maximum.Y = NAV_MESH_TILE_MAX_EXTENT; + tileBoundsNavMesh.Maximum.Z = tileBoundsNavMesh.Minimum.Z + tileSize; // Check if any navmesh volume intersects with the tile bool foundAnyVolume = false; @@ -195,7 +227,9 @@ bool GetNavMeshTileBounds(Scene* scene, int32 x, int32 y, float tileSize, Boundi { const auto volume = scene->NavigationVolumes[i]; const auto& volumeBounds = volume->GetBox(); - if (volumeBounds.Intersects(tileBounds)) + BoundingBox volumeBoundsNavMesh; + BoundingBox::Transform(volumeBounds, worldToNavMesh, volumeBoundsNavMesh); + if (volumeBoundsNavMesh.Intersects(tileBoundsNavMesh)) { if (foundAnyVolume) { @@ -214,8 +248,8 @@ bool GetNavMeshTileBounds(Scene* scene, int32 x, int32 y, float tileSize, Boundi if (foundAnyVolume) { // Build proper tile bounds - tileBounds.Minimum.Y = rangeY.X; - tileBounds.Maximum.Y = rangeY.Y; + tileBoundsNavMesh.Minimum.Y = rangeY.X; + tileBoundsNavMesh.Maximum.Y = rangeY.Y; } return foundAnyVolume; @@ -241,18 +275,18 @@ void RemoveTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, int runtime->RemoveTile(x, y, layer); } -bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, BoundingBox& tileBounds, float tileSize, rcConfig& config) +bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize, rcConfig& config) { rcContext context; int32 layer = 0; // Expand tile bounds by a certain margin const float tileBorderSize = (1.0f + (float)config.borderSize) * config.cs; - tileBounds.Minimum -= tileBorderSize; - tileBounds.Maximum += tileBorderSize; + tileBoundsNavMesh.Minimum -= tileBorderSize; + tileBoundsNavMesh.Maximum += tileBorderSize; - rcVcopy(config.bmin, &tileBounds.Minimum.X); - rcVcopy(config.bmax, &tileBounds.Maximum.X); + rcVcopy(config.bmin, &tileBoundsNavMesh.Minimum.X); + rcVcopy(config.bmax, &tileBoundsNavMesh.Maximum.X); rcHeightfield* heightfield = rcAllocHeightfield(); if (!heightfield) @@ -267,7 +301,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B } Array offMeshLinks; - RasterizeGeometry(tileBounds, &context, &config, heightfield, &offMeshLinks); + RasterizeGeometry(tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks); rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield); rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield); @@ -507,7 +541,8 @@ public: Scene* Scene; NavMesh* NavMesh; NavMeshRuntime* Runtime; - BoundingBox TileBounds; + BoundingBox TileBoundsNavMesh; + Matrix WorldToNavMesh; int32 X; int32 Y; float TileSize; @@ -520,7 +555,7 @@ public: { PROFILE_CPU_NAMED("BuildNavMeshTile"); - if (GenerateTile(NavMesh, Runtime, X, Y, TileBounds, TileSize, Config)) + if (GenerateTile(NavMesh, Runtime, X, Y, TileBoundsNavMesh, WorldToNavMesh, TileSize, Config)) { LOG(Warning, "Failed to generate navmesh tile at {0}x{1}.", X, Y); } @@ -600,7 +635,7 @@ float NavMeshBuilder::GetNavMeshBuildingProgress() return result; } -void BuildTileAsync(NavMesh* navMesh, int32 x, int32 y, rcConfig& config, const BoundingBox& tileBounds, float tileSize) +void BuildTileAsync(NavMesh* navMesh, int32 x, int32 y, rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize) { NavMeshRuntime* runtime = navMesh->GetRuntime(); NavBuildTasksLocker.Lock(); @@ -623,7 +658,8 @@ void BuildTileAsync(NavMesh* navMesh, int32 x, int32 y, rcConfig& config, const task->Runtime = runtime; task->X = x; task->Y = y; - task->TileBounds = tileBounds; + task->TileBoundsNavMesh = tileBoundsNavMesh; + task->WorldToNavMesh = worldToNavMesh; task->TileSize = tileSize; task->Config = config; NavBuildTasks.Add(task); @@ -639,8 +675,12 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo { const float tileSize = GetTileSize(); NavMeshRuntime* runtime = navMesh->GetRuntime(); + Matrix worldToNavMesh; + Matrix::RotationQuaternion(runtime->Properties.Rotation, worldToNavMesh); // Align dirty bounds to tile size + BoundingBox dirtyBoundsNavMesh; + BoundingBox::Transform(dirtyBounds, worldToNavMesh, dirtyBoundsNavMesh); BoundingBox dirtyBoundsAligned; dirtyBoundsAligned.Minimum = Vector3::Floor(dirtyBounds.Minimum / tileSize) * tileSize; dirtyBoundsAligned.Maximum = Vector3::Ceil(dirtyBounds.Maximum / tileSize) * tileSize; @@ -689,10 +729,10 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo { for (int32 x = tilesMin.X; x < tilesMax.X; x++) { - BoundingBox tileBounds; - if (GetNavMeshTileBounds(scene, x, y, tileSize, tileBounds)) + BoundingBox tileBoundsNavMesh; + if (GetNavMeshTileBounds(scene, x, y, tileSize, tileBoundsNavMesh, worldToNavMesh)) { - BuildTileAsync(navMesh, x, y, config, tileBounds, tileSize); + BuildTileAsync(navMesh, x, y, config, tileBoundsNavMesh, worldToNavMesh, tileSize); } else { diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index 017d35383..95b07924c 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -3,6 +3,7 @@ #include "NavMeshRuntime.h" #include "NavMesh.h" #include "Engine/Core/Log.h" +#include "Engine/Core/Math/Matrix.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Threading/Threading.h" #include @@ -48,14 +49,27 @@ bool NavMeshRuntime::FindDistanceToWall(const Vector3& startPosition, NavMeshHit dtQueryFilter filter; Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + Vector3 startPositionNavMesh; + Vector3::Transform(startPosition, Properties.Rotation, startPositionNavMesh); + dtPolyRef startPoly = 0; - query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); + query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr); if (!startPoly) { return false; } - return dtStatusSucceed(query->findDistanceToWall(startPoly, &startPosition.X, maxDistance, &filter, &hitInfo.Distance, &hitInfo.Position.X, &hitInfo.Normal.X)); + if (!dtStatusSucceed(query->findDistanceToWall(startPoly, &startPosition.X, maxDistance, &filter, &hitInfo.Distance, &hitInfo.Position.X, &hitInfo.Normal.X))) + { + return false; + } + + Quaternion invRotation; + Quaternion::Invert(Properties.Rotation, invRotation); + Vector3::Transform(hitInfo.Position, invRotation, hitInfo.Position); + Vector3::Transform(hitInfo.Normal, invRotation, hitInfo.Normal); + + return true; } bool NavMeshRuntime::FindPath(const Vector3& startPosition, const Vector3& endPosition, Array& resultPath) const @@ -72,14 +86,18 @@ bool NavMeshRuntime::FindPath(const Vector3& startPosition, const Vector3& endPo dtQueryFilter filter; Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + Vector3 startPositionNavMesh, endPositionNavMesh; + Vector3::Transform(startPosition, Properties.Rotation, startPositionNavMesh); + Vector3::Transform(endPosition, Properties.Rotation, endPositionNavMesh); + dtPolyRef startPoly = 0; - query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); + query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr); if (!startPoly) { return false; } dtPolyRef endPoly = 0; - query->findNearestPoly(&endPosition.X, &extent.X, &filter, &endPoly, nullptr); + query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr); if (!endPoly) { return false; @@ -87,31 +105,37 @@ bool NavMeshRuntime::FindPath(const Vector3& startPosition, const Vector3& endPo dtPolyRef path[NAV_MESH_PATH_MAX_SIZE]; int32 pathSize; - const auto findPathStatus = query->findPath(startPoly, endPoly, &startPosition.X, &endPosition.X, &filter, path, &pathSize, NAV_MESH_PATH_MAX_SIZE); + const auto findPathStatus = query->findPath(startPoly, endPoly, &startPositionNavMesh.X, &endPositionNavMesh.X, &filter, path, &pathSize, NAV_MESH_PATH_MAX_SIZE); if (dtStatusFailed(findPathStatus)) { return false; } + Quaternion invRotation; + Quaternion::Invert(Properties.Rotation, invRotation); + // Check for special case, where path has not been found, and starting polygon was the one closest to the target if (pathSize == 1 && dtStatusDetail(findPathStatus, DT_PARTIAL_RESULT)) { // In this case we find a point on starting polygon, that's closest to destination and store it as path end resultPath.Resize(2); resultPath[0] = startPosition; - resultPath[1] = startPosition; - query->closestPointOnPolyBoundary(startPoly, &endPosition.X, &resultPath[1].X); + resultPath[1] = startPositionNavMesh; + query->closestPointOnPolyBoundary(startPoly, &endPositionNavMesh.X, &resultPath[1].X); + Vector3::Transform(resultPath[1], invRotation, resultPath[1]); } else { int straightPathCount = 0; resultPath.EnsureCapacity(NAV_MESH_PATH_MAX_SIZE); - const auto findStraightPathStatus = query->findStraightPath(&startPosition.X, &endPosition.X, path, pathSize, (float*)resultPath.Get(), nullptr, nullptr, &straightPathCount, resultPath.Capacity(), DT_STRAIGHTPATH_AREA_CROSSINGS); + const auto findStraightPathStatus = query->findStraightPath(&startPositionNavMesh.X, &endPositionNavMesh.X, path, pathSize, (float*)resultPath.Get(), nullptr, nullptr, &straightPathCount, resultPath.Capacity(), DT_STRAIGHTPATH_AREA_CROSSINGS); if (dtStatusFailed(findStraightPathStatus)) { return false; } resultPath.Resize(straightPathCount); + for (auto& pos : resultPath) + Vector3::Transform(pos, invRotation, pos); } return true; @@ -130,13 +154,20 @@ bool NavMeshRuntime::ProjectPoint(const Vector3& point, Vector3& result) const dtQueryFilter filter; Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + Vector3 pointNavMesh; + Vector3::Transform(point, Properties.Rotation, pointNavMesh); + dtPolyRef startPoly = 0; - query->findNearestPoly(&point.X, &extent.X, &filter, &startPoly, &result.X); + query->findNearestPoly(&pointNavMesh.X, &extent.X, &filter, &startPoly, &result.X); if (!startPoly) { return false; } + Quaternion invRotation; + Quaternion::Invert(Properties.Rotation, invRotation); + Vector3::Transform(result, invRotation, result); + return true; } @@ -153,8 +184,12 @@ bool NavMeshRuntime::RayCast(const Vector3& startPosition, const Vector3& endPos dtQueryFilter filter; Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + Vector3 startPositionNavMesh, endPositionNavMesh; + Vector3::Transform(startPosition, Properties.Rotation, startPositionNavMesh); + Vector3::Transform(endPosition, Properties.Rotation, endPositionNavMesh); + dtPolyRef startPoly = 0; - query->findNearestPoly(&startPosition.X, &extent.X, &filter, &startPoly, nullptr); + query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr); if (!startPoly) { return false; @@ -163,7 +198,7 @@ bool NavMeshRuntime::RayCast(const Vector3& startPosition, const Vector3& endPos dtRaycastHit hit; hit.path = nullptr; hit.maxPath = 0; - const bool result = dtStatusSucceed(query->raycast(startPoly, &startPosition.X, &endPosition.X, &filter, 0, &hit)); + const bool result = dtStatusSucceed(query->raycast(startPoly, &startPositionNavMesh.X, &endPositionNavMesh.X, &filter, 0, &hit)); if (hit.t >= MAX_float) { hitInfo.Position = endPosition; @@ -427,7 +462,7 @@ void NavMeshRuntime::RemoveTiles(bool (* prediction)(const NavMeshRuntime* navMe #include "Engine/Debug/DebugDraw.h" -void DrawPoly(NavMeshRuntime* navMesh, const dtMeshTile& tile, const dtPoly& poly) +void DrawPoly(NavMeshRuntime* navMesh, const Matrix& navMeshToWorld, const dtMeshTile& tile, const dtPoly& poly) { const unsigned int ip = (unsigned int)(&poly - tile.polys); const dtPolyDetail& pd = tile.detailMeshes[ip]; @@ -457,6 +492,10 @@ void DrawPoly(NavMeshRuntime* navMesh, const dtMeshTile& tile, const dtPoly& pol v[1].Y += drawOffsetY; v[2].Y += drawOffsetY; + Vector3::Transform(v[0], navMeshToWorld, v[0]); + Vector3::Transform(v[1], navMeshToWorld, v[1]); + Vector3::Transform(v[2], navMeshToWorld, v[2]); + DEBUG_DRAW_TRIANGLE(v[0], v[1], v[2], fillColor, 0, true); } @@ -477,6 +516,10 @@ void DrawPoly(NavMeshRuntime* navMesh, const dtMeshTile& tile, const dtPoly& pol v[1].Y += drawOffsetY; v[2].Y += drawOffsetY; + Vector3::Transform(v[0], navMeshToWorld, v[0]); + Vector3::Transform(v[1], navMeshToWorld, v[1]); + Vector3::Transform(v[2], navMeshToWorld, v[2]); + for (int m = 0, n = 2; m < 3; n = m++) { // Skip inner detail edges @@ -496,6 +539,9 @@ void NavMeshRuntime::DebugDraw() const int tilesCount = dtNavMesh ? dtNavMesh->getMaxTiles() : 0; if (tilesCount == 0) return; + Matrix worldToNavMesh, navMeshToWorld; + Matrix::RotationQuaternion(Properties.Rotation, worldToNavMesh); + Matrix::Invert(worldToNavMesh, navMeshToWorld); for (int tileIndex = 0; tileIndex < tilesCount; tileIndex++) { @@ -511,7 +557,7 @@ void NavMeshRuntime::DebugDraw() if (poly->getType() != DT_POLYTYPE_GROUND) continue; - DrawPoly(this, *tile, *poly); + DrawPoly(this, navMeshToWorld, *tile, *poly); } } } From 7835259a839008b1788354991ec6962cf384b09d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Jan 2021 13:11:32 +0100 Subject: [PATCH 079/419] Add `TestPath` utility to navigation system --- Source/Engine/Navigation/NavMeshRuntime.cpp | 46 +++++++++++++++++++++ Source/Engine/Navigation/NavMeshRuntime.h | 8 ++++ Source/Engine/Navigation/Navigation.cpp | 7 ++++ Source/Engine/Navigation/Navigation.h | 8 ++++ 4 files changed, 69 insertions(+) diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index 95b07924c..d81f46e06 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -141,6 +141,52 @@ bool NavMeshRuntime::FindPath(const Vector3& startPosition, const Vector3& endPo return true; } +bool NavMeshRuntime::TestPath(const Vector3& startPosition, const Vector3& endPosition) const +{ + ScopeLock lock(Locker); + + const auto query = GetNavMeshQuery(); + if (!query) + { + return false; + } + + dtQueryFilter filter; + Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); + + Vector3 startPositionNavMesh, endPositionNavMesh; + Vector3::Transform(startPosition, Properties.Rotation, startPositionNavMesh); + Vector3::Transform(endPosition, Properties.Rotation, endPositionNavMesh); + + dtPolyRef startPoly = 0; + query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr); + if (!startPoly) + { + return false; + } + dtPolyRef endPoly = 0; + query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr); + if (!endPoly) + { + return false; + } + + dtPolyRef path[NAV_MESH_PATH_MAX_SIZE]; + int32 pathSize; + const auto findPathStatus = query->findPath(startPoly, endPoly, &startPositionNavMesh.X, &endPositionNavMesh.X, &filter, path, &pathSize, NAV_MESH_PATH_MAX_SIZE); + if (dtStatusFailed(findPathStatus)) + { + return false; + } + + if (dtStatusDetail(findPathStatus, DT_PARTIAL_RESULT)) + { + return false; + } + + return true; +} + bool NavMeshRuntime::ProjectPoint(const Vector3& point, Vector3& result) const { ScopeLock lock(Locker); diff --git a/Source/Engine/Navigation/NavMeshRuntime.h b/Source/Engine/Navigation/NavMeshRuntime.h index 5d1b8ca63..7da4be0e2 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.h +++ b/Source/Engine/Navigation/NavMeshRuntime.h @@ -105,6 +105,14 @@ public: /// True if found valid path between given two points (it may be partial), otherwise false if failed. bool FindPath(const Vector3& startPosition, const Vector3& endPosition, Array& resultPath) const; + /// + /// Tests the path between the two positions (non-partial). + /// + /// The start position. + /// The end position. + /// True if found valid path between given two points, otherwise false if failed. + bool TestPath(const Vector3& startPosition, const Vector3& endPosition) const; + /// /// Projects the point to nav mesh surface (finds the nearest polygon). /// diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 7f8ca5e9a..29b081150 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -224,6 +224,13 @@ bool Navigation::FindPath(const Vector3& startPosition, const Vector3& endPositi return NavMeshes.First()->FindPath(startPosition, endPosition, resultPath); } +bool Navigation::TestPath(const Vector3& startPosition, const Vector3& endPosition) +{ + if (NavMeshes.IsEmpty()) + return false; + return NavMeshes.First()->TestPath(startPosition, endPosition); +} + bool Navigation::ProjectPoint(const Vector3& point, Vector3& result) { if (NavMeshes.IsEmpty()) diff --git a/Source/Engine/Navigation/Navigation.h b/Source/Engine/Navigation/Navigation.h index 2557e31e8..dcd6b9fbb 100644 --- a/Source/Engine/Navigation/Navigation.h +++ b/Source/Engine/Navigation/Navigation.h @@ -32,6 +32,14 @@ public: /// True if found valid path between given two points (it may be partial), otherwise false if failed. API_FUNCTION() static bool FindPath(const Vector3& startPosition, const Vector3& endPosition, API_PARAM(Out) Array& resultPath); + /// + /// Tests the path between the two positions (non-partial). + /// + /// The start position. + /// The end position. + /// True if found valid path between given two points, otherwise false if failed. + API_FUNCTION() static bool TestPath(const Vector3& startPosition, const Vector3& endPosition); + /// /// Projects the point to nav mesh surface (finds the nearest polygon). /// From 98d272a9030a5ccba358caaed0297d24032636b2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Jan 2021 14:18:22 +0100 Subject: [PATCH 080/419] Add support for SphereCollider and CapsuleCollider for navmesh --- Source/Engine/Navigation/NavMeshBuilder.cpp | 95 ++++++++++++++++++++- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index e9c8cb578..4d62b03b8 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -11,6 +11,8 @@ #include "Engine/Core/Math/BoundingBox.h" #include "Engine/Core/Math/VectorInt.h" #include "Engine/Physics/Colliders/BoxCollider.h" +#include "Engine/Physics/Colliders/SphereCollider.h" +#include "Engine/Physics/Colliders/CapsuleCollider.h" #include "Engine/Physics/Colliders/MeshCollider.h" #include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Terrain/TerrainPatch.h" @@ -119,6 +121,77 @@ struct NavigationSceneRasterization } } + static void TriangulateBox(Array& vb, Array& ib, const OrientedBoundingBox& box) + { + vb.Resize(8); + box.GetCorners(vb.Get()); + ib.Add(BoxTrianglesIndicesCache, 36); + } + + static void TriangulateBox(Array& vb, Array& ib, const BoundingBox& box) + { + vb.Resize(8); + box.GetCorners(vb.Get()); + ib.Add(BoxTrianglesIndicesCache, 36); + } + + static void TriangulateSphere(Array& vb, Array& ib, const BoundingSphere& sphere) + { + const int32 sphereResolution = 12; + const int32 verticalSegments = sphereResolution; + const int32 horizontalSegments = sphereResolution * 2; + + // Generate vertices for unit sphere + Vector3 vertices[(verticalSegments + 1) * (horizontalSegments + 1)]; + int32 vertexCount = 0; + for (int32 j = 0; j <= horizontalSegments; j++) + vertices[vertexCount++] = Vector3(0, -1, 0); + for (int32 i = 1; i < verticalSegments; i++) + { + const float latitude = (float)i * PI / verticalSegments - PI / 2.0f; + const float dy = Math::Sin(latitude); + const float dxz = Math::Cos(latitude); + auto& firstHorizontalVertex = vertices[vertexCount++]; + firstHorizontalVertex = Vector3(0, dy, dxz); + for (int32 j = 1; j < horizontalSegments; j++) + { + const float longitude = (float)j * 2.0f * PI / horizontalSegments; + const float dx = Math::Sin(longitude) * dxz; + const float dz = Math::Cos(longitude) * dxz; + vertices[vertexCount++] = Vector3(dx, dy, dz); + } + vertices[vertexCount++] = firstHorizontalVertex; + } + for (int32 j = 0; j <= horizontalSegments; j++) + vertices[vertexCount++] = Vector3(0, 1, 0); + + // Transform vertices into world space vertex buffer + vb.Resize(vertexCount); + for (int32 i = 0; i < vertexCount; i++) + vb[i] = sphere.Center + vertices[i] * sphere.Radius; + + // Generate index buffer + const int32 stride = horizontalSegments + 1; + int32 indexCount = 0; + ib.Resize(verticalSegments * (horizontalSegments + 1) * 6); + for (int32 i = 0; i < verticalSegments; i++) + { + const int32 nextI = i + 1; + for (int32 j = 0; j <= horizontalSegments; j++) + { + const int32 nextJ = (j + 1) % stride; + + ib[indexCount++] = i * stride + j; + ib[indexCount++] = nextI * stride + j; + ib[indexCount++] = i * stride + nextJ; + + ib[indexCount++] = i * stride + nextJ; + ib[indexCount++] = nextI * stride + j; + ib[indexCount++] = nextI * stride + nextJ; + } + } + } + static bool Walk(Actor* actor, NavigationSceneRasterization& e) { // Early out if object is not intersecting with the tile bounds or is not using navigation @@ -141,9 +214,25 @@ struct NavigationSceneRasterization PROFILE_CPU_NAMED("BoxCollider"); const OrientedBoundingBox box = boxCollider->GetOrientedBox(); - vb.Resize(8); - box.GetCorners(vb.Get()); - ib.Add(BoxTrianglesIndicesCache, 36); + TriangulateBox(vb, ib, box); + + e.RasterizeTriangles(); + } + else if (const auto* sphereCollider = dynamic_cast(actor)) + { + PROFILE_CPU_NAMED("SphereCollider"); + + const BoundingSphere sphere = sphereCollider->GetSphere(); + TriangulateSphere(vb, ib, sphere); + + e.RasterizeTriangles(); + } + else if (const auto* capsuleCollider = dynamic_cast(actor)) + { + PROFILE_CPU_NAMED("CapsuleCollider"); + + const BoundingBox box = capsuleCollider->GetBox(); + TriangulateBox(vb, ib, box); e.RasterizeTriangles(); } From f8624ae768abb49d0798d3d87ea3adc65b10f65b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Jan 2021 14:18:40 +0100 Subject: [PATCH 081/419] Fix crash during navmesh build when actor gets removed --- Source/Engine/Navigation/NavMeshBuilder.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 4d62b03b8..a12a72c69 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -628,7 +628,7 @@ class NavMeshTileBuildTask : public ThreadPoolTask public: Scene* Scene; - NavMesh* NavMesh; + ScriptingObjectReference NavMesh; NavMeshRuntime* Runtime; BoundingBox TileBoundsNavMesh; Matrix WorldToNavMesh; @@ -644,6 +644,11 @@ public: { PROFILE_CPU_NAMED("BuildNavMeshTile"); + const auto navMesh = NavMesh.Get(); + if (!navMesh) + { + return false; + } if (GenerateTile(NavMesh, Runtime, X, Y, TileBoundsNavMesh, WorldToNavMesh, TileSize, Config)) { LOG(Warning, "Failed to generate navmesh tile at {0}x{1}.", X, Y); From d7949b54056249ef04edff934995a01b43132e52 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Jan 2021 14:29:27 +0100 Subject: [PATCH 082/419] Remove deprecated comment --- Source/Engine/Navigation/NavMeshBuilder.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index a12a72c69..b0e41504b 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -281,9 +281,6 @@ struct NavigationSceneRasterization e.OffMeshLinks->Add(link); } - // TODO: nav mesh for capsule collider - // TODO: nav mesh for sphere collider - return true; } }; From b76d5d34ea7d54f1e0614d90eb21e62e47671385 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Jan 2021 21:21:08 +0100 Subject: [PATCH 083/419] Fix lambda --- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 34 +++++++++---------- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.h | 4 +++ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 33df0db47..ea9e6a990 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1363,23 +1363,7 @@ PixelFormat GPUDeviceVulkan::GetClosestSupportedPixelFormat(PixelFormat format, if (flags & GPUTextureFlags::UnorderedAccess) wantedFeatureFlags |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT; - // Check actual device for format support - const auto isSupported = [&](VkFormat vkFormat) - { - VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(Adapter->Gpu, vkFormat, &props); - const VkFormatFeatureFlags featureFlags = optimalTiling ? props.optimalTilingFeatures : props.linearTilingFeatures; - if ((featureFlags & wantedFeatureFlags) != wantedFeatureFlags) - return false; - - //VkImageFormatProperties imageProps; - //vkGetPhysicalDeviceImageFormatProperties(Adapter->Gpu, vkFormat, , &imageProps); - - return true; - }; - - VkFormat vkFormat = RenderToolsVulkan::ToVulkanFormat(format); - if (!isSupported(vkFormat)) + if (!IsVkFormatSupported(RenderToolsVulkan::ToVulkanFormat(format), wantedFeatureFlags, optimalTiling)) { // Special case for depth-stencil formats if (flags & GPUTextureFlags::DepthStencil) @@ -1389,7 +1373,7 @@ PixelFormat GPUDeviceVulkan::GetClosestSupportedPixelFormat(PixelFormat format, // Spec guarantees at least one depth-only, and one depth-stencil format to be supported if (hasStencil) { - if (isSupported(VK_FORMAT_D32_SFLOAT_S8_UINT)) + if (IsVkFormatSupported(VK_FORMAT_D32_SFLOAT_S8_UINT, wantedFeatureFlags, optimalTiling)) format = PixelFormat::D32_Float; else format = PixelFormat::D24_UNorm_S8_UInt; @@ -1493,6 +1477,20 @@ bool GPUDeviceVulkan::SaveValidationCache() #endif +bool GPUDeviceVulkan::IsVkFormatSupported(VkFormat vkFormat, VkFormatFeatureFlags wantedFeatureFlags, bool optimalTiling) const +{ + VkFormatProperties props; + vkGetPhysicalDeviceFormatProperties(Adapter->Gpu, vkFormat, &props); + const VkFormatFeatureFlags featureFlags = optimalTiling ? props.optimalTilingFeatures : props.linearTilingFeatures; + if ((featureFlags & wantedFeatureFlags) != wantedFeatureFlags) + return false; + + //VkImageFormatProperties imageProps; + //vkGetPhysicalDeviceImageFormatProperties(Adapter->Gpu, vkFormat, , &imageProps); + + return true; +} + GPUContext* GPUDeviceVulkan::GetMainContext() { return reinterpret_cast(MainContext); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index 9bab15dda..ba972932b 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -720,6 +720,10 @@ public: #endif +private: + + bool IsVkFormatSupported(VkFormat vkFormat, VkFormatFeatureFlags wantedFeatureFlags, bool optimalTiling) const; + public: // [GPUDevice] From f80f7cdd33dbb010efa1454b5cc3ce67f652e040 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 14 Jan 2021 22:23:22 +0100 Subject: [PATCH 084/419] Fix crash on Android if GPU doesn't support linear sampling of the shadow map --- Source/Engine/Graphics/GPULimits.h | 4 +- Source/Engine/Renderer/MotionBlurPass.cpp | 6 +-- Source/Engine/Renderer/ShadowsPass.cpp | 57 +++++++++++++++-------- Source/Engine/Renderer/ShadowsPass.h | 1 + 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/Source/Engine/Graphics/GPULimits.h b/Source/Engine/Graphics/GPULimits.h index 485f5d866..b50714fcd 100644 --- a/Source/Engine/Graphics/GPULimits.h +++ b/Source/Engine/Graphics/GPULimits.h @@ -174,10 +174,10 @@ API_ENUM(Attributes="Flags") enum class FormatSupport : int32 DECLARE_ENUM_OPERATORS(FormatSupport); // Helper macro to check if given format does not support a given set of feature flags -#define FORMAT_FEATURES_ARE_NOT_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & formatSupportFlags) != static_cast(formatSupportFlags)) +#define FORMAT_FEATURES_ARE_NOT_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & (formatSupportFlags)) != static_cast(formatSupportFlags)) // Helper macro to check if given format does support a given set of feature flags -#define FORMAT_FEATURES_ARE_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & formatSupportFlags) == static_cast(formatSupportFlags)) +#define FORMAT_FEATURES_ARE_SUPPORTED(formatSupport, formatSupportFlags) ((formatSupport & (formatSupportFlags)) == static_cast(formatSupportFlags)) /// /// The features exposed for a particular format. diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index f25d14c51..609712e1f 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -57,11 +57,11 @@ bool MotionBlurPass::Init() // Prepare formats for the buffers auto format = MOTION_VECTORS_PIXEL_FORMAT; - if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(format).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(format).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) { - if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32_Float).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32_Float).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) format = PixelFormat::R32G32_Float; - else if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R16G16B16A16_Float).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))) + else if (FORMAT_FEATURES_ARE_NOT_SUPPORTED(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R16G16B16A16_Float).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)) format = PixelFormat::R16G16B16A16_Float; else format = PixelFormat::R32G32B32A32_Float; diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 5ec9d6446..11812a45c 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -6,6 +6,7 @@ #include "Engine/Graphics/Graphics.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Content/Content.h" +#include "Engine/Graphics/PixelFormatExtensions.h" #if USE_EDITOR #include "Engine/Renderer/Lightmaps.h" #endif @@ -77,6 +78,19 @@ bool ShadowsPass::Init() _shader.Get()->OnReloading.Bind(this); #endif + // If GPU doesn't support linear sampling for the shadow map then fallback to the single sample on lowest quality + const auto formatTexture = PixelFormatExtensions::FindShaderResourceFormat(SHADOW_MAPS_FORMAT, false); + const auto formatFeaturesDepth = GPUDevice::Instance->GetFormatFeatures(SHADOW_MAPS_FORMAT); + const auto formatFeaturesTexture = GPUDevice::Instance->GetFormatFeatures(formatTexture); + _supportsShadows = FORMAT_FEATURES_ARE_SUPPORTED(formatFeaturesDepth.Support, FormatSupport::DepthStencil | FormatSupport::Texture2D) + && FORMAT_FEATURES_ARE_SUPPORTED(formatFeaturesTexture.Support, FormatSupport::ShaderSample | FormatSupport::ShaderSampleComparison); + if (!_supportsShadows) + { + LOG(Warning, "GPU doesn't support shadows rendering"); + LOG(Warning, "Format: {0} features support: {1}", (int32)SHADOW_MAPS_FORMAT, (uint32)formatFeaturesDepth.Support); + LOG(Warning, "Format: {0} features support: {1}", (int32)formatTexture, (uint32)formatFeaturesTexture.Support); + } + return false; } @@ -130,24 +144,27 @@ void ShadowsPass::updateShadowMapSize() // Select new size _currentShadowMapsQuality = Graphics::ShadowMapsQuality; - switch (_currentShadowMapsQuality) + if (_supportsShadows) { - case Quality::Ultra: - newSizeCSM = 2048; - newSizeCube = 1024; - break; - case Quality::High: - newSizeCSM = 1024; - newSizeCube = 1024; - break; - case Quality::Medium: - newSizeCSM = 1024; - newSizeCube = 512; - break; - case Quality::Low: - newSizeCSM = 512; - newSizeCube = 256; - break; + switch (_currentShadowMapsQuality) + { + case Quality::Ultra: + newSizeCSM = 2048; + newSizeCube = 1024; + break; + case Quality::High: + newSizeCSM = 1024; + newSizeCube = 1024; + break; + case Quality::Medium: + newSizeCSM = 1024; + newSizeCube = 512; + break; + case Quality::Low: + newSizeCSM = 512; + newSizeCube = 256; + break; + } } // Check if size will change @@ -194,7 +211,7 @@ bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererPo const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - return fade > ZeroTolerance; + return fade > ZeroTolerance && _supportsShadows; } bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSpotLightData& light) @@ -206,12 +223,12 @@ bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSp const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f); const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance); - return fade > ZeroTolerance; + return fade > ZeroTolerance && _supportsShadows; } bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererDirectionalLightData& light) { - return true; + return _supportsShadows; } void ShadowsPass::Prepare(RenderContext& renderContext, GPUContext* context) diff --git a/Source/Engine/Renderer/ShadowsPass.h b/Source/Engine/Renderer/ShadowsPass.h index 8deef8258..3d3190670 100644 --- a/Source/Engine/Renderer/ShadowsPass.h +++ b/Source/Engine/Renderer/ShadowsPass.h @@ -25,6 +25,7 @@ private: GPUPipelineStatePermutationsPs(Quality::MAX) * 2 * 2> _psShadowDir; GPUPipelineStatePermutationsPs(Quality::MAX) * 2> _psShadowPoint; GPUPipelineStatePermutationsPs(Quality::MAX) * 2> _psShadowSpot; + bool _supportsShadows; // Shadow maps stuff int32 _shadowMapsSizeCSM; From 2c2c1af97f7e0538ea30415e345b50ea348ccddf Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 15 Jan 2021 11:59:04 +0100 Subject: [PATCH 085/419] Add NavAgentMask --- .../Dedicated/NavAgentMaskEditor.cs | 92 +++++++++++++++++++ Source/Engine/Navigation/Navigation.cpp | 36 ++++++++ .../Engine/Navigation/NavigationSettings.cs | 12 +++ Source/Engine/Navigation/NavigationTypes.h | 24 +++++ Source/Engine/Serialization/JsonSerializer.cs | 1 + 5 files changed, 165 insertions(+) create mode 100644 Source/Editor/CustomEditors/Dedicated/NavAgentMaskEditor.cs diff --git a/Source/Editor/CustomEditors/Dedicated/NavAgentMaskEditor.cs b/Source/Editor/CustomEditors/Dedicated/NavAgentMaskEditor.cs new file mode 100644 index 000000000..ab0151bb9 --- /dev/null +++ b/Source/Editor/CustomEditors/Dedicated/NavAgentMaskEditor.cs @@ -0,0 +1,92 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System.Linq; +using FlaxEditor.Content.Settings; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.CustomEditors.Dedicated +{ + /// + /// Custom editor for . + /// + /// + [CustomEditor(typeof(NavAgentMask)), DefaultEditor] + internal class NavAgentMaskEditor : CustomEditor + { + private CheckBox[] _checkBoxes; + + /// + public override void Initialize(LayoutElementsContainer layout) + { + var settings = GameSettings.Load(); + if (settings.NavMeshes == null || settings.NavMeshes.Length == 0) + { + layout.Label("Missing navmesh settings"); + return; + } + + _checkBoxes = new CheckBox[settings.NavMeshes.Length]; + for (int i = 0; i < settings.NavMeshes.Length; i++) + { + ref var navmesh = ref settings.NavMeshes[i]; + var property = layout.AddPropertyItem(navmesh.Name, navmesh.Agent.ToString()); + property.Labels.Last().TextColorHighlighted = navmesh.Color; + var checkbox = property.Checkbox().CheckBox; + UpdateCheckbox(checkbox, i); + checkbox.Tag = i; + checkbox.StateChanged += OnCheckboxStateChanged; + _checkBoxes[i] = checkbox; + } + } + + /// + protected override void Deinitialize() + { + _checkBoxes = null; + + base.Deinitialize(); + } + + /// + public override void Refresh() + { + if (_checkBoxes != null) + { + for (int i = 0; i < _checkBoxes.Length; i++) + { + UpdateCheckbox(_checkBoxes[i], i); + } + } + + base.Refresh(); + } + + private void OnCheckboxStateChanged(CheckBox checkBox) + { + var i = (int)checkBox.Tag; + var value = (NavAgentMask)Values[0]; + var mask = 1u << i; + value.Mask &= ~mask; + value.Mask |= checkBox.Checked ? mask : 0; + SetValue(value); + } + + private void UpdateCheckbox(CheckBox checkbox, int i) + { + for (var j = 0; j < Values.Count; j++) + { + var value = (((NavAgentMask)Values[j]).Mask & (1 << i)) != 0; + if (j == 0) + { + checkbox.Checked = value; + } + else if (checkbox.State != CheckBoxState.Intermediate) + { + if (checkbox.Checked != value) + checkbox.State = CheckBoxState.Intermediate; + } + } + } + } +} diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 29b081150..5e73f40c7 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -97,6 +97,42 @@ bool NavAgentProperties::operator==(const NavAgentProperties& other) const return Math::NearEqual(Radius, other.Radius) && Math::NearEqual(Height, other.Height) && Math::NearEqual(StepHeight, other.StepHeight) && Math::NearEqual(MaxSlopeAngle, other.MaxSlopeAngle); } +bool NavAgentMask::IsAgentSupported(int32 agentIndex) const +{ + return (Mask & (1 << agentIndex)) != 0; +} + +bool NavAgentMask::IsAgentSupported(const NavAgentProperties& agentProperties) const +{ + auto settings = NavigationSettings::Get(); + for (int32 agentIndex = 0; agentIndex < settings->NavMeshes.Count(); agentIndex++) + { + if (settings->NavMeshes[agentIndex].Agent == agentProperties) + { + return (Mask & (1 << agentIndex)) != 0; + } + } + return false; +} + +bool NavAgentMask::IsNavMeshSupported(const NavMeshProperties& navMeshProperties) const +{ + auto settings = NavigationSettings::Get(); + for (int32 agentIndex = 0; agentIndex < settings->NavMeshes.Count(); agentIndex++) + { + if (settings->NavMeshes[agentIndex] == navMeshProperties) + { + return (Mask & (1 << agentIndex)) != 0; + } + } + return false; +} + +bool NavAgentMask::operator==(const NavAgentMask& other) const +{ + return Mask == other.Mask; +} + bool NavMeshProperties::operator==(const NavMeshProperties& other) const { return Name == other.Name && Quaternion::NearEqual(Rotation, other.Rotation, 0.001f) && Agent == other.Agent; diff --git a/Source/Engine/Navigation/NavigationSettings.cs b/Source/Engine/Navigation/NavigationSettings.cs index da300b3f1..4127ebe6b 100644 --- a/Source/Engine/Navigation/NavigationSettings.cs +++ b/Source/Engine/Navigation/NavigationSettings.cs @@ -72,3 +72,15 @@ namespace FlaxEditor.Content.Settings } } } + +namespace FlaxEngine +{ + partial struct NavAgentProperties + { + /// + public override string ToString() + { + return $"Radius: {Radius}, Height: {Height}, StepHeight: {StepHeight}, MaxSlopeAngle: {MaxSlopeAngle}"; + } + } +} diff --git a/Source/Engine/Navigation/NavigationTypes.h b/Source/Engine/Navigation/NavigationTypes.h index f00c1683e..d02b2acc4 100644 --- a/Source/Engine/Navigation/NavigationTypes.h +++ b/Source/Engine/Navigation/NavigationTypes.h @@ -91,6 +91,30 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshProperties); } }; +/// +/// The navigation system agents selection mask (from navigation system settings). Uses 1 bit per agent type (up to 32 agents). +/// +API_STRUCT() struct FLAXENGINE_API NavAgentMask +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(NavAgentMask); + + /// + /// The agents selection mask. + /// + API_FIELD() uint32 Mask = MAX_uint32; + + bool IsAgentSupported(int32 agentIndex) const; + bool IsAgentSupported(const NavAgentProperties& agentProperties) const; + bool IsNavMeshSupported(const NavMeshProperties& navMeshProperties) const; + + bool operator==(const NavAgentMask& other) const; + + bool operator!=(const NavAgentMask& other) const + { + return !operator==(other); + } +}; + /// /// The result information for navigation mesh queries. /// diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index 231831442..535499442 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -120,6 +120,7 @@ namespace FlaxEngine.Json } } */ + /// /// Objects serialization tool (json format). /// From 65d3883f032223cd45e145ee36b4fa807090423d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 15 Jan 2021 11:59:21 +0100 Subject: [PATCH 086/419] Add support for masking navmesh agents in NavMeshBoundsVolume --- .../Engine/Navigation/NavMeshBoundsVolume.cpp | 19 +++++++++++++++++++ .../Engine/Navigation/NavMeshBoundsVolume.h | 15 +++++++++++++++ Source/Engine/Navigation/NavMeshBuilder.cpp | 6 ++++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.cpp b/Source/Engine/Navigation/NavMeshBoundsVolume.cpp index a3f02249a..700442b2f 100644 --- a/Source/Engine/Navigation/NavMeshBoundsVolume.cpp +++ b/Source/Engine/Navigation/NavMeshBoundsVolume.cpp @@ -2,6 +2,7 @@ #include "NavMeshBoundsVolume.h" #include "Engine/Level/Scene/Scene.h" +#include "Engine/Serialization/Serialization.h" #if USE_EDITOR #include "Editor/Editor.h" #include "Editor/Managed/ManagedEditor.h" @@ -13,6 +14,24 @@ NavMeshBoundsVolume::NavMeshBoundsVolume(const SpawnParams& params) { } +void NavMeshBoundsVolume::Serialize(SerializeStream& stream, const void* otherObj) +{ + // Base + BoxVolume::Serialize(stream, otherObj); + + SERIALIZE_GET_OTHER_OBJ(NavMeshBoundsVolume); + + SERIALIZE_MEMBER(AgentsMask, AgentsMask.Mask); +} + +void NavMeshBoundsVolume::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + // Base + BoxVolume::Deserialize(stream, modifier); + + DESERIALIZE_MEMBER(AgentsMask, AgentsMask.Mask); +} + void NavMeshBoundsVolume::OnEnable() { // Base diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.h b/Source/Engine/Navigation/NavMeshBoundsVolume.h index 2846f9e4f..897a83666 100644 --- a/Source/Engine/Navigation/NavMeshBoundsVolume.h +++ b/Source/Engine/Navigation/NavMeshBoundsVolume.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Level/Actors/BoxVolume.h" +#include "NavigationTypes.h" /// /// A special type of volume that defines the areas of the scene in which navigation meshes are generated. @@ -10,6 +11,20 @@ API_CLASS() class FLAXENGINE_API NavMeshBoundsVolume : public BoxVolume { DECLARE_SCENE_OBJECT(NavMeshBoundsVolume); +public: + + /// + /// The agent types used by this navmesh bounds volume (from navigation settings). Can be used to generate navmesh for a certain set of agents. + /// + API_FIELD(Attributes="EditorDisplay(\"Box Volume\"), EditorOrder(10)") + NavAgentMask AgentsMask; + +public: + + // [BoxVolume] + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + protected: // [BoxVolume] diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index b0e41504b..57cea5d14 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -296,7 +296,7 @@ void RasterizeGeometry(const BoundingBox& tileBoundsNavMesh, const Matrix& world // Builds navmesh tile bounds and check if there are any valid navmesh volumes at that tile location // Returns true if tile is intersecting with any navmesh bounds volume actor - which means tile is in use -bool GetNavMeshTileBounds(Scene* scene, int32 x, int32 y, float tileSize, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh) +bool GetNavMeshTileBounds(Scene* scene, NavMesh* navMesh, int32 x, int32 y, float tileSize, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh) { // Build initial tile bounds (with infinite extent) tileBoundsNavMesh.Minimum.X = (float)x * tileSize; @@ -312,6 +312,8 @@ bool GetNavMeshTileBounds(Scene* scene, int32 x, int32 y, float tileSize, Boundi for (int32 i = 0; i < scene->NavigationVolumes.Count(); i++) { const auto volume = scene->NavigationVolumes[i]; + if (!volume->AgentsMask.IsNavMeshSupported(navMesh->Properties)) + continue; const auto& volumeBounds = volume->GetBox(); BoundingBox volumeBoundsNavMesh; BoundingBox::Transform(volumeBounds, worldToNavMesh, volumeBoundsNavMesh); @@ -821,7 +823,7 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo for (int32 x = tilesMin.X; x < tilesMax.X; x++) { BoundingBox tileBoundsNavMesh; - if (GetNavMeshTileBounds(scene, x, y, tileSize, tileBoundsNavMesh, worldToNavMesh)) + if (GetNavMeshTileBounds(scene, navMesh, x, y, tileSize, tileBoundsNavMesh, worldToNavMesh)) { BuildTileAsync(navMesh, x, y, config, tileBoundsNavMesh, worldToNavMesh, tileSize); } From 070faf12dae59858c6004ae9597c4683e1ea66e9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 15 Jan 2021 12:48:22 +0100 Subject: [PATCH 087/419] Fixes for the rotated navmeshes building --- Source/Engine/Navigation/NavMeshBuilder.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 57cea5d14..5cd5a52bd 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -321,13 +321,13 @@ bool GetNavMeshTileBounds(Scene* scene, NavMesh* navMesh, int32 x, int32 y, floa { if (foundAnyVolume) { - rangeY.X = Math::Min(rangeY.X, volumeBounds.Minimum.Y); - rangeY.Y = Math::Max(rangeY.Y, volumeBounds.Maximum.Y); + rangeY.X = Math::Min(rangeY.X, volumeBoundsNavMesh.Minimum.Y); + rangeY.Y = Math::Max(rangeY.Y, volumeBoundsNavMesh.Maximum.Y); } else { - rangeY.X = volumeBounds.Minimum.Y; - rangeY.Y = volumeBounds.Maximum.Y; + rangeY.X = volumeBoundsNavMesh.Minimum.Y; + rangeY.Y = volumeBoundsNavMesh.Maximum.Y; } foundAnyVolume = true; } @@ -775,8 +775,8 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo BoundingBox dirtyBoundsNavMesh; BoundingBox::Transform(dirtyBounds, worldToNavMesh, dirtyBoundsNavMesh); BoundingBox dirtyBoundsAligned; - dirtyBoundsAligned.Minimum = Vector3::Floor(dirtyBounds.Minimum / tileSize) * tileSize; - dirtyBoundsAligned.Maximum = Vector3::Ceil(dirtyBounds.Maximum / tileSize) * tileSize; + dirtyBoundsAligned.Minimum = Vector3::Floor(dirtyBoundsNavMesh.Minimum / tileSize) * tileSize; + dirtyBoundsAligned.Maximum = Vector3::Ceil(dirtyBoundsNavMesh.Maximum / tileSize) * tileSize; // Calculate tiles range for the given navigation dirty bounds (aligned to tiles size) const Int3 tilesMin(dirtyBoundsAligned.Minimum / tileSize); From 01a30faa624f332374d0db24f5edc3009bae32ad Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 15 Jan 2021 16:46:40 +0100 Subject: [PATCH 088/419] Add NavMesh Modifier Volume actor type --- .../Actors/NavModifierVolumeNode.cs | 20 +++++++ Source/Editor/SceneGraph/SceneGraphFactory.cs | 1 + .../Editor/Windows/SceneTreeWindow.Actors.cs | 3 +- Source/Editor/Windows/ToolboxWindow.cs | 3 +- .../Engine/Navigation/NavMeshBoundsVolume.h | 2 +- Source/Engine/Navigation/NavMeshBuilder.cpp | 41 ++++++++++++-- .../Engine/Navigation/NavModifierVolume.cpp | 54 +++++++++++++++++++ Source/Engine/Navigation/NavModifierVolume.h | 35 ++++++++++++ 8 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 Source/Editor/SceneGraph/Actors/NavModifierVolumeNode.cs create mode 100644 Source/Engine/Navigation/NavModifierVolume.cpp create mode 100644 Source/Engine/Navigation/NavModifierVolume.h diff --git a/Source/Editor/SceneGraph/Actors/NavModifierVolumeNode.cs b/Source/Editor/SceneGraph/Actors/NavModifierVolumeNode.cs new file mode 100644 index 000000000..38faba8ef --- /dev/null +++ b/Source/Editor/SceneGraph/Actors/NavModifierVolumeNode.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using FlaxEngine; + +namespace FlaxEditor.SceneGraph.Actors +{ + /// + /// Actor node for . + /// + /// + [HideInEditor] + public sealed class NavModifierVolumeNode : BoxVolumeNode + { + /// + public NavModifierVolumeNode(Actor actor) + : base(actor) + { + } + } +} diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs index 68cb627f0..0076742b3 100644 --- a/Source/Editor/SceneGraph/SceneGraphFactory.cs +++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs @@ -62,6 +62,7 @@ namespace FlaxEditor.SceneGraph CustomNodesTypes.Add(typeof(NavMeshBoundsVolume), typeof(NavMeshBoundsVolumeNode)); CustomNodesTypes.Add(typeof(BoxVolume), typeof(BoxVolumeNode)); CustomNodesTypes.Add(typeof(NavLink), typeof(NavLinkNode)); + CustomNodesTypes.Add(typeof(NavModifierVolume), typeof(NavModifierVolumeNode)); CustomNodesTypes.Add(typeof(ParticleEffect), typeof(ParticleEffectNode)); CustomNodesTypes.Add(typeof(SceneAnimationPlayer), typeof(SceneAnimationPlayerNode)); } diff --git a/Source/Editor/Windows/SceneTreeWindow.Actors.cs b/Source/Editor/Windows/SceneTreeWindow.Actors.cs index d3fff78d8..6fafe444f 100644 --- a/Source/Editor/Windows/SceneTreeWindow.Actors.cs +++ b/Source/Editor/Windows/SceneTreeWindow.Actors.cs @@ -97,7 +97,8 @@ namespace FlaxEditor.Windows new KeyValuePair("Audio Listener", typeof(AudioListener)), new KeyValuePair("Scene Animation", typeof(SceneAnimationPlayer)), new KeyValuePair("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)), - new KeyValuePair("Nav Mesh Link", typeof(NavLink)), + new KeyValuePair("Nav Link", typeof(NavLink)), + new KeyValuePair("Nav Modifier Volume", typeof(NavModifierVolume)), } }, new ActorsGroup diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index d10a111b6..8c30621b0 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -165,7 +165,8 @@ namespace FlaxEditor.Windows groupOther.AddChild(CreateActorItem("Empty Actor", typeof(EmptyActor))); groupOther.AddChild(CreateActorItem("Scene Animation", typeof(SceneAnimationPlayer))); groupOther.AddChild(CreateActorItem("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume))); - groupOther.AddChild(CreateActorItem("Nav Mesh Link", typeof(NavLink))); + groupOther.AddChild(CreateActorItem("Nav Link", typeof(NavLink))); + groupOther.AddChild(CreateActorItem("Nav Modifier Volume", typeof(NavModifierVolume))); var groupGui = CreateGroupWithList(actorGroups, "GUI"); groupGui.AddChild(CreateActorItem("UI Control", typeof(UIControl))); diff --git a/Source/Engine/Navigation/NavMeshBoundsVolume.h b/Source/Engine/Navigation/NavMeshBoundsVolume.h index 897a83666..674ba4877 100644 --- a/Source/Engine/Navigation/NavMeshBoundsVolume.h +++ b/Source/Engine/Navigation/NavMeshBoundsVolume.h @@ -6,7 +6,7 @@ #include "NavigationTypes.h" /// -/// A special type of volume that defines the areas of the scene in which navigation meshes are generated. +/// A special type of volume that defines the area of the scene in which navigation meshes are generated. /// API_CLASS() class FLAXENGINE_API NavMeshBoundsVolume : public BoxVolume { diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 5cd5a52bd..8d0bbafeb 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -7,6 +7,7 @@ #include "NavigationSettings.h" #include "NavMeshBoundsVolume.h" #include "NavLink.h" +#include "NavModifierVolume.h" #include "NavMeshRuntime.h" #include "Engine/Core/Math/BoundingBox.h" #include "Engine/Core/Math/VectorInt.h" @@ -55,8 +56,14 @@ struct OffMeshLink int32 Id; }; +struct Modifier +{ + BoundingBox Bounds; +}; + struct NavigationSceneRasterization { + NavMesh* NavMesh; BoundingBox TileBoundsNavMesh; Matrix WorldToNavMesh; rcContext* Context; @@ -66,18 +73,21 @@ struct NavigationSceneRasterization Array VertexBuffer; Array IndexBuffer; Array* OffMeshLinks; + Array* Modifiers; const bool IsWorldToNavMeshIdentity; - NavigationSceneRasterization(const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks) + NavigationSceneRasterization(::NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks, Array* modifiers) : TileBoundsNavMesh(tileBoundsNavMesh) , WorldToNavMesh(worldToNavMesh) , IsWorldToNavMeshIdentity(worldToNavMesh.IsIdentity()) { + NavMesh = navMesh; Context = context; Config = config; Heightfield = heightfield; WalkableThreshold = Math::Cos(config->walkableSlopeAngle * DegreesToRadians); OffMeshLinks = offMeshLinks; + Modifiers = modifiers; } void RasterizeTriangles() @@ -280,16 +290,30 @@ struct NavigationSceneRasterization e.OffMeshLinks->Add(link); } + else if (const auto* navModifierVolume = dynamic_cast(actor)) + { + if (navModifierVolume->AgentsMask.IsNavMeshSupported(e.NavMesh->Properties)) + { + PROFILE_CPU_NAMED("NavModifierVolume"); + + Modifier modifier; + OrientedBoundingBox bounds = navModifierVolume->GetOrientedBox(); + bounds.Transform(e.WorldToNavMesh); + bounds.GetBoundingBox(modifier.Bounds); + + e.Modifiers->Add(modifier); + } + } return true; } }; -void RasterizeGeometry(const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks) +void RasterizeGeometry(NavMesh* navMesh, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, rcContext* context, rcConfig* config, rcHeightfield* heightfield, Array* offMeshLinks, Array* modifiers) { PROFILE_CPU_NAMED("RasterizeGeometry"); - NavigationSceneRasterization rasterization(tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks); + NavigationSceneRasterization rasterization(navMesh, tileBoundsNavMesh, worldToNavMesh, context, config, heightfield, offMeshLinks, modifiers); Function treeWalkFunction(NavigationSceneRasterization::Walk); SceneQuery::TreeExecute(treeWalkFunction, rasterization); } @@ -389,7 +413,8 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B } Array offMeshLinks; - RasterizeGeometry(tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks); + Array modifiers; + RasterizeGeometry(navMesh, tileBoundsNavMesh, worldToNavMesh, &context, &config, heightfield, &offMeshLinks, &modifiers); rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield); rcFilterLedgeSpans(&context, config.walkableHeight, config.walkableClimb, *heightfield); @@ -415,6 +440,12 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B return true; } + // Mark areas + for (auto& modifier : modifiers) + { + rcMarkBoxArea(&context, &modifier.Bounds.Minimum.X, &modifier.Bounds.Maximum.X, RC_NULL_AREA, *compactHeightfield); + } + if (!rcBuildDistanceField(&context, *compactHeightfield)) { LOG(Warning, "Could not generate navmesh: Could not build distance field."); @@ -468,7 +499,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B for (int i = 0; i < polyMesh->npolys; i++) { - polyMesh->flags[i] = polyMesh->areas[i] == RC_WALKABLE_AREA ? 1 : 0; + polyMesh->flags[i] = polyMesh->areas[i] != RC_NULL_AREA ? 1 : 0; } if (polyMesh->nverts == 0) diff --git a/Source/Engine/Navigation/NavModifierVolume.cpp b/Source/Engine/Navigation/NavModifierVolume.cpp new file mode 100644 index 000000000..65325195a --- /dev/null +++ b/Source/Engine/Navigation/NavModifierVolume.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "NavModifierVolume.h" +#include "Engine/Level/Scene/Scene.h" +#include "Engine/Serialization/Serialization.h" +#if USE_EDITOR +#include "Editor/Editor.h" +#include "Editor/Managed/ManagedEditor.h" +#include "NavMeshBuilder.h" +#endif + +NavModifierVolume::NavModifierVolume(const SpawnParams& params) + : BoxVolume(params) +{ + _size = 100.0f; +} + +void NavModifierVolume::Serialize(SerializeStream& stream, const void* otherObj) +{ + // Base + BoxVolume::Serialize(stream, otherObj); + + SERIALIZE_GET_OTHER_OBJ(NavModifierVolume); + + SERIALIZE_MEMBER(AgentsMask, AgentsMask.Mask); +} + +void NavModifierVolume::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + // Base + BoxVolume::Deserialize(stream, modifier); + + DESERIALIZE_MEMBER(AgentsMask, AgentsMask.Mask); +} + +#if USE_EDITOR + +void NavModifierVolume::OnBoundsChanged(const BoundingBox& prevBounds) +{ + // Auto-rebuild modified navmesh area + if (IsDuringPlay() && IsActiveInHierarchy() && !Editor::IsPlayMode && Editor::Managed->CanAutoBuildNavMesh()) + { + BoundingBox dirtyBounds; + BoundingBox::Merge(prevBounds, _box, dirtyBounds); + NavMeshBuilder::Build(GetScene(), dirtyBounds, ManagedEditor::ManagedEditorOptions.AutoRebuildNavMeshTimeoutMs); + } +} + +Color NavModifierVolume::GetWiresColor() +{ + return Color::Red; +} + +#endif diff --git a/Source/Engine/Navigation/NavModifierVolume.h b/Source/Engine/Navigation/NavModifierVolume.h new file mode 100644 index 000000000..3ca0d4b20 --- /dev/null +++ b/Source/Engine/Navigation/NavModifierVolume.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Level/Actors/BoxVolume.h" +#include "NavigationTypes.h" + +/// +/// A special type of volume that defines the area of the scene in which navigation is restricted (eg. higher traversal cost or dynamic obstacle block). +/// +API_CLASS() class FLAXENGINE_API NavModifierVolume : public BoxVolume +{ +DECLARE_SCENE_OBJECT(NavModifierVolume); +public: + + /// + /// The agent types used by this navmesh modifier volume (from navigation settings). Can be used to adjust navmesh for a certain set of agents. + /// + API_FIELD(Attributes="EditorDisplay(\"Box Volume\"), EditorOrder(10)") + NavAgentMask AgentsMask; + +public: + + // [BoxVolume] + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + +protected: + + // [BoxVolume] +#if USE_EDITOR + void OnBoundsChanged(const BoundingBox& prevBounds) override; + Color GetWiresColor() override; +#endif +}; From c112274666cf330b89a7deda70be295a177e5b25 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 15 Jan 2021 19:51:23 +0100 Subject: [PATCH 089/419] Add additional comments to the formatting algorithm --- Source/Editor/Surface/VisjectSurface.Formatting.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs index 723f8555f..e53ed4a3c 100644 --- a/Source/Editor/Surface/VisjectSurface.Formatting.cs +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -152,6 +152,7 @@ namespace FlaxEditor.Surface /// The number of the maximum layer private int SetLayers(Dictionary nodeData, List endNodes) { + // Longest path layering int maxLayer = 0; var stack = new Stack(endNodes); @@ -193,8 +194,9 @@ namespace FlaxEditor.Surface /// The number of the maximum offset private int SetOffsets(Dictionary nodeData, List endNodes, int maxLayer) { - // This piece of code should be explained a bit better, since it does some fairly fancy stuff int maxOffset = 0; + + // Keeps track of the largest offset (Y axis) for every layer int[] offsets = new int[maxLayer + 1]; var visitedNodes = new HashSet(); @@ -203,14 +205,18 @@ namespace FlaxEditor.Surface { if (!nodeData.TryGetValue(node, out var data)) return; + // If we realize that the current node would collide with an already existing node in this layer if (data.Layer >= 0 && offsets[data.Layer] > data.Offset) { + // Move the entire sub-tree down straightParentData.SubtreeOffset = Math.Max(straightParentData.SubtreeOffset, offsets[data.Layer] - data.Offset); } + // Keeps track of the offset of the last direct child we visited int childOffset = data.Offset; bool straightChild = true; + // Run the algorithm for every child for (int i = 0; i < node.Elements.Count; i++) { if (node.Elements[i] is InputBox box && box.HasAnyConnection) @@ -229,6 +235,7 @@ namespace FlaxEditor.Surface if (data.Layer >= 0) { + // When coming out of the recursion, apply the extra subtree offsets data.Offset += straightParentData.SubtreeOffset; if (data.Offset > maxOffset) { From 8003055ba6d18eb1f25f1c7268d44782e1b4f262 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 15 Jan 2021 20:23:40 +0100 Subject: [PATCH 090/419] Improve node layout algorithm --- .../Surface/VisjectSurface.Formatting.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs index e53ed4a3c..0356af7ac 100644 --- a/Source/Editor/Surface/VisjectSurface.Formatting.cs +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -103,6 +103,8 @@ namespace FlaxEditor.Surface // Set the vertical offsets int maxOffset = SetOffsets(nodeData, endNodes, maxLayer); + // Layout the nodes + // Get the largest nodes in the Y and X direction float[] widths = new float[maxLayer + 1]; float[] heights = new float[maxOffset + 1]; @@ -121,17 +123,31 @@ namespace FlaxEditor.Surface } } - // Layout the nodes - Vector2 minSize = new Vector2(180, 180); + Vector2 minDistanceBetweenNodes = new Vector2(20, 20); + // Figure out the node positions (aligned to a grid) + float[] nodeXPositions = new float[widths.Length]; + for (int i = 1; i < widths.Length; i++) + { + // Go from right to left (backwards) through the nodes + nodeXPositions[i] = nodeXPositions[i - 1] + minDistanceBetweenNodes.X + widths[i]; + } + + float[] nodeYPositions = new float[heights.Length]; + for (int i = 1; i < heights.Length; i++) + { + // Go from top to bottom through the nodes + nodeYPositions[i] = nodeYPositions[i - 1] + heights[i - 1] + minDistanceBetweenNodes.Y; + } + + // Set the node positions var undoActions = new List(); var topRightPosition = endNodes[0].Location; for (int i = 0; i < nodes.Count; i++) { if (nodeData.TryGetValue(nodes[i], out var data)) { - Vector2 size = Vector2.Max(minSize, new Vector2(widths[data.Layer], heights[data.Offset])); - Vector2 newLocation = (new Vector2(-data.Layer, data.Offset) * size) + topRightPosition; + Vector2 newLocation = new Vector2(-nodeXPositions[data.Layer], nodeYPositions[data.Offset]) + topRightPosition; Vector2 locationDelta = newLocation - nodes[i].Location; nodes[i].Location = newLocation; From 3356013858fabfaf83fffba2e79b452c87791361 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 16 Jan 2021 18:16:48 +0100 Subject: [PATCH 091/419] Improve empty space finding --- Source/Editor/Surface/VisjectSurface.Input.cs | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 510ac1939..f982d7596 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -686,31 +686,38 @@ namespace FlaxEditor.Surface private Vector2 FindEmptySpace(Box box) { - int boxIndex = 0; + Vector2 distanceBetweenNodes = new Vector2(20, 20); var node = box.ParentNode; + + // Same height as node + float yLocation = node.Top; + for (int i = 0; i < node.Elements.Count; i++) { - // Box on the same side above the current box if (node.Elements[i] is Box nodeBox && nodeBox.IsOutput == box.IsOutput && nodeBox.Y < box.Y) { - boxIndex++; + // Below connected node + yLocation = Mathf.Max(yLocation, nodeBox.ParentNode.Bottom + distanceBetweenNodes.Y); } } + // TODO: Dodge the other nodes - Vector2 distanceBetweenNodes = new Vector2(40, 20); - const float NodeHeight = 120; + float xLocation = node.Location.X; + if (box.IsOutput) + { + xLocation += node.Width + distanceBetweenNodes.X; + } + else + { + xLocation += -120 - distanceBetweenNodes.X; + } - float direction = box.IsOutput ? 1 : -1; - - Vector2 newNodeLocation = node.Location + - new Vector2( - (node.Width + distanceBetweenNodes.X) * direction, - boxIndex * (NodeHeight + distanceBetweenNodes.Y) - ); - - return newNodeLocation; + return new Vector2( + xLocation, + yLocation + ); } } } From aa4ecab271048568ea4c6c9e2e605ad54be3f0a5 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 16 Jan 2021 19:20:36 +0100 Subject: [PATCH 092/419] Improve Visject bracket positioning --- Source/Editor/Surface/VisjectSurface.Input.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index f982d7596..878db380e 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -24,7 +24,9 @@ namespace FlaxEditor.Surface private class InputBracket { + private readonly float DefaultWidth = 120f; private readonly Margin _padding = new Margin(10f); + public Box Box { get; } public Vector2 EndBracketPosition { get; } public List Nodes { get; } = new List(); @@ -33,7 +35,7 @@ namespace FlaxEditor.Surface public InputBracket(Box box, Vector2 nodePosition) { Box = box; - EndBracketPosition = nodePosition; + EndBracketPosition = nodePosition + new Vector2(DefaultWidth, 0); Update(); } @@ -47,11 +49,10 @@ namespace FlaxEditor.Surface } else { - area = new Rectangle(EndBracketPosition, new Vector2(120f, 80f)); + area = new Rectangle(EndBracketPosition, new Vector2(DefaultWidth, 80f)); } _padding.ExpandRectangle(ref area); - Vector2 endPoint = area.Location + new Vector2(area.Width, area.Height / 2f); - Vector2 offset = EndBracketPosition - endPoint; + Vector2 offset = EndBracketPosition - area.UpperRight; area.Location += offset; Area = area; if (!offset.IsZero) From 4db3c6ca71282098afe8dffa62734033385b338d Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 16 Jan 2021 19:23:35 +0100 Subject: [PATCH 093/419] Increase default distance between nodes --- Source/Editor/Surface/VisjectSurface.Formatting.cs | 2 +- Source/Editor/Surface/VisjectSurface.Input.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs index 0356af7ac..848f79157 100644 --- a/Source/Editor/Surface/VisjectSurface.Formatting.cs +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -123,7 +123,7 @@ namespace FlaxEditor.Surface } } - Vector2 minDistanceBetweenNodes = new Vector2(20, 20); + Vector2 minDistanceBetweenNodes = new Vector2(30, 30); // Figure out the node positions (aligned to a grid) float[] nodeXPositions = new float[widths.Length]; diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 878db380e..dec89e143 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -687,7 +687,7 @@ namespace FlaxEditor.Surface private Vector2 FindEmptySpace(Box box) { - Vector2 distanceBetweenNodes = new Vector2(20, 20); + Vector2 distanceBetweenNodes = new Vector2(30, 30); var node = box.ParentNode; From 8fd0af3f394a3f472b9857e34d370be34a4e612f Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sun, 17 Jan 2021 10:18:30 +0100 Subject: [PATCH 094/419] Fixed Typos and padding --- Source/Editor/Content/Import/ModelImportEntry.cs | 4 ++-- Source/Editor/GUI/Docking/MasterDockPanel.cs | 2 +- Source/Editor/History/HistoryStack.cs | 2 +- Source/Editor/Scripting/ScriptsBuilder.cpp | 2 +- Source/Editor/Windows/AboutDialog.cs | 2 +- Source/Editor/Windows/Assets/SceneAnimationWindow.cs | 2 +- Source/Editor/Windows/GameWindow.cs | 4 ++-- Source/Engine/Content/Loading/ContentLoadingManager.cpp | 2 +- Source/Engine/Core/Log.cpp | 8 ++++---- Source/Engine/Core/ObjectsRemovalService.cpp | 2 +- Source/Engine/Engine/Engine.cpp | 2 +- .../Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp | 2 +- .../Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h | 2 +- Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp | 2 +- Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp | 2 +- Source/Engine/Particles/ParticleManager.cpp | 2 +- Source/Engine/Platform/Base/WindowsManager.cpp | 2 +- Source/Engine/Render2D/FontAsset.cpp | 2 +- Source/Engine/Utilities/RectPack.h | 2 +- 19 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Source/Editor/Content/Import/ModelImportEntry.cs b/Source/Editor/Content/Import/ModelImportEntry.cs index e7cbe96ea..37525e6d3 100644 --- a/Source/Editor/Content/Import/ModelImportEntry.cs +++ b/Source/Editor/Content/Import/ModelImportEntry.cs @@ -262,9 +262,9 @@ namespace FlaxEditor.Content.Import public int BaseLOD { get; set; } = 0; /// - /// The amount of LODs to include in the model (all reaming ones starting from Base LOD will be generated). + /// The amount of LODs to include in the model (all remaining ones starting from Base LOD will be generated). /// - [EditorOrder(1120), DefaultValue(4), Limit(1, Model.MaxLODs), EditorDisplay("Level Of Detail", "LOD Count"), Tooltip("The amount of LODs to include in the model (all reaming ones starting from Base LOD will be generated).")] + [EditorOrder(1120), DefaultValue(4), Limit(1, Model.MaxLODs), EditorDisplay("Level Of Detail", "LOD Count"), Tooltip("The amount of LODs to include in the model (all remaining ones starting from Base LOD will be generated).")] public int LODCount { get; set; } = 4; /// diff --git a/Source/Editor/GUI/Docking/MasterDockPanel.cs b/Source/Editor/GUI/Docking/MasterDockPanel.cs index f7acdbed4..a560de195 100644 --- a/Source/Editor/GUI/Docking/MasterDockPanel.cs +++ b/Source/Editor/GUI/Docking/MasterDockPanel.cs @@ -64,7 +64,7 @@ namespace FlaxEditor.GUI.Docking for (int i = 0; i < childPanels.Length; i++) childPanels[i].Dispose(); - // Delete reaming controls (except tabs proxy) + // Delete remaining controls (except tabs proxy) if (TabsProxy != null) TabsProxy.Parent = null; DisposeChildren(); diff --git a/Source/Editor/History/HistoryStack.cs b/Source/Editor/History/HistoryStack.cs index c7a63efc4..e2d828ce9 100644 --- a/Source/Editor/History/HistoryStack.cs +++ b/Source/Editor/History/HistoryStack.cs @@ -74,7 +74,7 @@ namespace FlaxEditor.History _reverseActions.PushBack(reverse[i]); } - // Cleanup reaming actions + // Cleanup remaining actions for (int i = _historyActionsLimit; i < history.Length; i++) { history[i].Dispose(); diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 7d581fa8a..7cced3e36 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -567,7 +567,7 @@ bool ScriptsBuilderService::Init() LOG(Warning, "Missing EditorTarget property in opened project, using deducted target name {0}", Editor::Project->EditorTarget); } - // Remove any reaming files from previous Editor run hot-reloads + // Remove any remaining files from previous Editor run hot-reloads const Char *target, *platform, *architecture, *configuration; ScriptsBuilder::GetBinariesConfiguration(target, platform, architecture, configuration); if (target) diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index 03e60f43c..bb320fb87 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -115,7 +115,7 @@ namespace FlaxEditor.Windows { var thirdPartyPanel = new Panel(ScrollBars.Vertical) { - Bounds = new Rectangle(0, topParentControl.Bottom + 4, Width, Height - topParentControl.Bottom - 24), + Bounds = new Rectangle(4, topParentControl.Bottom + 4, Width - 8, Height - topParentControl.Bottom - 24), Parent = this }; var thirdPartyEntries = new[] diff --git a/Source/Editor/Windows/Assets/SceneAnimationWindow.cs b/Source/Editor/Windows/Assets/SceneAnimationWindow.cs index cbed724bc..95421585d 100644 --- a/Source/Editor/Windows/Assets/SceneAnimationWindow.cs +++ b/Source/Editor/Windows/Assets/SceneAnimationWindow.cs @@ -490,7 +490,7 @@ namespace FlaxEditor.Windows.Assets } if (_player.IsStopped || _player.Time >= _options.EndTime) { - // End rendering but perform reaming copies of the staging textures so all data is captured (from GPU to CPU) + // End rendering but perform remaining copies of the staging textures so all data is captured (from GPU to CPU) _state = States.Staging; break; } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index a29869825..35bc91bbe 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -222,7 +222,7 @@ namespace FlaxEditor.Windows private void PlayingStateOnSceneDuplicating() { - // Remove reaming GUI controls so loaded scene can add own GUI + // Remove remaining GUI controls so loaded scene can add own GUI //_guiRoot.DisposeChildren(); // Show GUI @@ -231,7 +231,7 @@ namespace FlaxEditor.Windows private void PlayingStateOnSceneRestored() { - // Remove reaming GUI controls so loaded scene can add own GUI + // Remove remaining GUI controls so loaded scene can add own GUI //_guiRoot.DisposeChildren(); // Hide GUI diff --git a/Source/Engine/Content/Loading/ContentLoadingManager.cpp b/Source/Engine/Content/Loading/ContentLoadingManager.cpp index ac2dd8257..6e6ff7a65 100644 --- a/Source/Engine/Content/Loading/ContentLoadingManager.cpp +++ b/Source/Engine/Content/Loading/ContentLoadingManager.cpp @@ -208,7 +208,7 @@ void ContentLoadingManagerService::Dispose() MainThread = nullptr; ThisThread = nullptr; - // Cancel all reaming tasks (no chance to execute them) + // Cancel all remaining tasks (no chance to execute them) Tasks.CancelAll(); } diff --git a/Source/Engine/Core/Log.cpp b/Source/Engine/Core/Log.cpp index 2213672c9..cb73a79e1 100644 --- a/Source/Engine/Core/Log.cpp +++ b/Source/Engine/Core/Log.cpp @@ -51,18 +51,18 @@ bool Log::Logger::Init() { // Check if there are any files to delete const int32 maxLogFiles = 20; - int32 reaming = oldLogs.Count() - maxLogFiles + 1; - if (reaming > 0) + int32 remaining = oldLogs.Count() - maxLogFiles + 1; + if (remaining > 0) { Sorting::QuickSort(oldLogs.Get(), oldLogs.Count()); // Delete the oldest logs int32 i = 0; - while (reaming > 0) + while (remaining > 0) { FileSystem::DeleteFile(oldLogs[i++]); filesDeleted++; - reaming--; + remaining--; } } } diff --git a/Source/Engine/Core/ObjectsRemovalService.cpp b/Source/Engine/Core/ObjectsRemovalService.cpp index a36265646..f9d26f808 100644 --- a/Source/Engine/Core/ObjectsRemovalService.cpp +++ b/Source/Engine/Core/ObjectsRemovalService.cpp @@ -193,7 +193,7 @@ void ObjectsRemovalServiceService::Dispose() // Collect new objects ObjectsRemovalService::Flush(); - // Delete all reaming objects + // Delete all remaining objects { ScopeLock lock(PoolLocker); for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index ebf268957..140f1b4a0 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -463,7 +463,7 @@ void Engine::OnExit() LOG_FLUSH(); - // Kill all reaming threads + // Kill all remaining threads ThreadRegistry::KillEmAll(); // Cleanup diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index ce36a7347..4d1bd3e17 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -216,7 +216,7 @@ uint64 GPUContextDX12::Execute(bool waitForCompletion) ASSERT(_currentAllocator != nullptr); auto queue = _device->GetCommandQueue(); - // Flush reaming and buffered commands + // Flush remaining and buffered commands FlushState(); _currentState = nullptr; diff --git a/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h b/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h index 8351deb71..ea621c22a 100644 --- a/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h +++ b/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h @@ -8,7 +8,7 @@ #include "Engine/Platform/Win32/IncludeWindowsHeaders.h" #include "Engine/Platform/Windows/ComPtr.h" -// Helper define to dispose the COM object with reaming references counter checking +// Helper define to dispose the COM object with remaining references counter checking #define DX_SAFE_RELEASE_CHECK(x, refs) if(x) { auto res = (x)->Release(); (x) = nullptr; CHECK(res == refs); } #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp index c327fd32a..f9a39574c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/CmdBufferVulkan.cpp @@ -44,7 +44,7 @@ void CmdBufferVulkan::End() ASSERT(IsOutsideRenderPass()); #if GPU_ALLOW_PROFILE_EVENTS - // End reaming events + // End remaining events while (_eventsBegin--) vkCmdEndDebugUtilsLabelEXT(GetHandle()); #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index ea8fa6502..e8f770831 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -1155,7 +1155,7 @@ void GPUContextVulkan::FlushState() void GPUContextVulkan::Flush() { - // Flush reaming and buffered commands + // Flush remaining and buffered commands FlushState(); _currentState = nullptr; diff --git a/Source/Engine/Particles/ParticleManager.cpp b/Source/Engine/Particles/ParticleManager.cpp index 0e5640545..40cebd892 100644 --- a/Source/Engine/Particles/ParticleManager.cpp +++ b/Source/Engine/Particles/ParticleManager.cpp @@ -1172,7 +1172,7 @@ void ParticleManagerService::Update() // Update bounds after first system update updateBounds = true; } - // TODO: if using fixed timestep quantize the dt and accumulate reaming part for the next update? + // TODO: if using fixed timestep quantize the dt and accumulate remaining part for the next update? if (dt <= 1.0f / 240.0f) continue; dt *= effect->SimulationSpeed; diff --git a/Source/Engine/Platform/Base/WindowsManager.cpp b/Source/Engine/Platform/Base/WindowsManager.cpp index b49cbd058..75082c4f9 100644 --- a/Source/Engine/Platform/Base/WindowsManager.cpp +++ b/Source/Engine/Platform/Base/WindowsManager.cpp @@ -77,7 +77,7 @@ void WindowsManagerService::Update() void WindowsManagerService::Dispose() { - // Close reaming windows + // Close remaining windows WindowsManager::WindowsLocker.Lock(); auto windows = WindowsManager::Windows; for (auto& win : windows) diff --git a/Source/Engine/Render2D/FontAsset.cpp b/Source/Engine/Render2D/FontAsset.cpp index cf2571e4a..8b14b1cfc 100644 --- a/Source/Engine/Render2D/FontAsset.cpp +++ b/Source/Engine/Render2D/FontAsset.cpp @@ -55,7 +55,7 @@ void FontAsset::unload(bool isReloading) // Ensure to cleanup child font objects if (_fonts.HasItems()) { - LOG(Warning, "Font asset {0} is unloading but has {1} reaming font objects created", ToString(), _fonts.Count()); + LOG(Warning, "Font asset {0} is unloading but has {1} remaining font objects created", ToString(), _fonts.Count()); for (auto font : _fonts) { font->_asset = nullptr; diff --git a/Source/Engine/Utilities/RectPack.h b/Source/Engine/Utilities/RectPack.h index f020b0481..a8ffb0b7e 100644 --- a/Source/Engine/Utilities/RectPack.h +++ b/Source/Engine/Utilities/RectPack.h @@ -25,7 +25,7 @@ struct RectPack SizeType Width; SizeType Height; - // The reaming space amount inside this slot (updated on every insertion, initial it equal to width*height). + // The remaining space amount inside this slot (updated on every insertion, initial it equal to width*height). SizeType SpaceLeft; // True, if slot has been allocated, otherwise it's free. From edb103f8e729092a777a0bdb4d85556374629ebe Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 17 Jan 2021 12:13:48 +0100 Subject: [PATCH 095/419] Fix NearEqual (GPU Particle) missing implementation. --- Source/Engine/Visject/ShaderGraph.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 61c609307..c7db3b41b 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -380,6 +380,15 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) Value v2 = tryGetValue(node->GetBox(1), Value::Zero); value = writeFunction2(node, v1, v2, TEXT("atan2")); break; + } + // Near Equal + case 42: + { + Value v1 = tryGetValue(node->GetBox(0), Value::Zero); + Value v2 = tryGetValue(node->GetBox(1), Value::Zero).Cast(v1.Type); + Value epsilon = tryGetValue(node->GetBox(2), 2, Value::Zero); + value = writeLocal(ValueType::Bool, String::Format(TEXT("(distance({0},{1}) < {2})"), v1.Value, v2.Value, epsilon.Value), node); + break; } // Degrees case 43: @@ -911,7 +920,7 @@ void ShaderGenerator::ProcessGroupComparisons(Box* box, Node* node, Value& value const Value condition = tryGetValue(node->GetBox(0), Value::False).AsBool(); const Value onTrue = tryGetValue(node->GetBox(2), 1, Value::Zero); const Value onFalse = tryGetValue(node->GetBox(1), 0, Value::Zero).Cast(onTrue.Type); - value = writeLocal(onTrue.Type, String::Format(TEXT("({0}) ? ({1}) : ({2})"), condition.Value, onTrue.Value, onFalse.Value), node); + value = writeLocal(onTrue.Type, String::Format(TEXT("{0} ? {1} : {2}"), condition.Value, onTrue.Value, onFalse.Value), node); break; } } From 50f87c8bfb972fac830c5e5f5cef94009c997cf8 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 17 Jan 2021 12:36:53 +0100 Subject: [PATCH 096/419] Remove unused (). --- Source/Engine/Visject/ShaderGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index c7db3b41b..7b3c51f3e 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -387,7 +387,7 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) Value v1 = tryGetValue(node->GetBox(0), Value::Zero); Value v2 = tryGetValue(node->GetBox(1), Value::Zero).Cast(v1.Type); Value epsilon = tryGetValue(node->GetBox(2), 2, Value::Zero); - value = writeLocal(ValueType::Bool, String::Format(TEXT("(distance({0},{1}) < {2})"), v1.Value, v2.Value, epsilon.Value), node); + value = writeLocal(ValueType::Bool, String::Format(TEXT("distance({0},{1}) < {2}"), v1.Value, v2.Value, epsilon.Value), node); break; } // Degrees From 2ae78b9afde4268874b9232a7ae9d8cf6f00738c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 10:51:55 +0100 Subject: [PATCH 097/419] Fix navmesh tiles build --- Source/Engine/Navigation/NavMeshBuilder.cpp | 29 +++++++++++++++------ Source/Engine/Navigation/NavMeshRuntime.cpp | 1 + 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 8d0bbafeb..01441d831 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -590,18 +590,31 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B ScopeLock lock(runtime->Locker); - // Add tile data navMesh->IsDataDirty = true; - auto& tile = navMesh->Data.Tiles.AddOne(); - tile.PosX = x; - tile.PosY = y; - tile.Layer = layer; + NavMeshTileData* tile = nullptr; + for (int32 i = 0; i < navMesh->Data.Tiles.Count(); i++) + { + auto& e = navMesh->Data.Tiles[i]; + if (e.PosX == x && e.PosY == y && e.Layer == layer) + { + tile = &e; + break; + } + } + if (!tile) + { + // Add new tile + tile = &navMesh->Data.Tiles.AddOne(); + tile->PosX = x; + tile->PosY = y; + tile->Layer = layer; + } - // Copy data - tile.Data.Copy(navData, navDataSize); + // Copy data to the tile + tile->Data.Copy(navData, navDataSize); // Add tile to navmesh - runtime->AddTile(navMesh, tile); + runtime->AddTile(navMesh, *tile); } dtFree(navData); diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index d81f46e06..d576f3607 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -13,6 +13,7 @@ #define MAX_NODES 2048 #define USE_DATA_LINK 0 #define USE_NAV_MESH_ALLOC 1 +// TODO: try not using USE_NAV_MESH_ALLOC #define DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL 50.0f #define DEFAULT_NAV_QUERY_EXTENT_VERTICAL 250.0f From c1216a4318260085bc2ed960a05fdf8be6d64e37 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 11:42:17 +0100 Subject: [PATCH 098/419] Add `NavAreaProperties` --- Source/Engine/Navigation/Navigation.cpp | 5 +++ Source/Engine/Navigation/NavigationTypes.h | 40 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index 5e73f40c7..d5a516870 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -133,6 +133,11 @@ bool NavAgentMask::operator==(const NavAgentMask& other) const return Mask == other.Mask; } +bool NavAreaProperties::operator==(const NavAreaProperties& other) const +{ + return Name == other.Name && Id == other.Id && Math::NearEqual(Cost, other.Cost); +} + bool NavMeshProperties::operator==(const NavMeshProperties& other) const { return Name == other.Name && Quaternion::NearEqual(Rotation, other.Rotation, 0.001f) && Agent == other.Agent; diff --git a/Source/Engine/Navigation/NavigationTypes.h b/Source/Engine/Navigation/NavigationTypes.h index d02b2acc4..e604580f6 100644 --- a/Source/Engine/Navigation/NavigationTypes.h +++ b/Source/Engine/Navigation/NavigationTypes.h @@ -137,3 +137,43 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(NavMeshHit); /// API_FIELD() Vector3 Normal; }; + +/// +/// The navigation area properties container for navmesh building and navigation runtime. +/// +API_STRUCT() struct FLAXENGINE_API NavAreaProperties : ISerializable +{ +API_AUTO_SERIALIZATION(); +DECLARE_SCRIPTING_TYPE_MINIMAL(NavAreaProperties); + + /// + /// The area type name. Identifies different types of the areas. + /// + API_FIELD(Attributes="EditorOrder(0)") + String Name; + + /// + /// The area type color (for debugging). Alpha channel is used to blend with navmesh color (alpha 0 to use navmesh color only). + /// + API_FIELD(Attributes="EditorOrder(10)") + Color Color = Color::Red; + + /// + /// The area id. It must be unique for the project. Valid range 0-63. Value 0 is reserved for Null areas (empty, non-navigable areas). + /// + API_FIELD(Attributes="EditorOrder(20), Limit(0, 63)") + byte Id = 1; + + /// + /// The cost scale for the area traversal for agents. The higher the cost, the less likely agent wil choose the path that goes over it. For instance, areas that are harder to move like sand should have higher cost for proper path finding. + /// + API_FIELD(Attributes="EditorOrder(30), Limit(0, 100000, 0.1f)") + float Cost = 1; + + bool operator==(const NavAreaProperties& other) const; + + bool operator!=(const NavAreaProperties& other) const + { + return !operator==(other); + } +}; From ba050b9eaac2da84d4029e7f7970727c69f66f76 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 11:42:50 +0100 Subject: [PATCH 099/419] Add default spacing for CollectionEditor to 10 for cleaner UI when working with arrays and lists in Editor --- Source/Editor/CustomEditors/Editors/CollectionEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index ecbbef0f9..2e5aeade7 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -116,7 +116,7 @@ namespace FlaxEditor.CustomEditors.Editors // Try get CollectionAttribute for collection editor meta var attributes = Values.GetAttributes(); Type overrideEditorType = null; - float spacing = 0.0f; + float spacing = 10.0f; var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute); if (collection != null) { From fdc8e371c4c6b783ce6b77afa3f62d7386f9cc80 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 13:18:26 +0100 Subject: [PATCH 100/419] Add support for using NavAreas for navigation --- Source/Engine/Navigation/NavMeshBuilder.cpp | 28 +++++++++++++-- Source/Engine/Navigation/NavMeshRuntime.cpp | 17 ++++++++- Source/Engine/Navigation/NavMeshRuntime.h | 6 ++++ .../Engine/Navigation/NavModifierVolume.cpp | 14 ++++++++ Source/Engine/Navigation/NavModifierVolume.h | 15 +++++++- Source/Engine/Navigation/Navigation.cpp | 36 +++++++++++++++++++ .../Engine/Navigation/NavigationSettings.cs | 30 ++++++++++++++++ Source/Engine/Navigation/NavigationSettings.h | 11 ++++-- Source/Engine/Navigation/NavigationTypes.h | 6 ++-- .../recastnavigation/DetourNavMeshQuery.h | 1 + 10 files changed, 155 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index 01441d831..3026af66a 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -59,6 +59,7 @@ struct OffMeshLink struct Modifier { BoundingBox Bounds; + NavAreaProperties* NavArea; }; struct NavigationSceneRasterization @@ -300,6 +301,7 @@ struct NavigationSceneRasterization OrientedBoundingBox bounds = navModifierVolume->GetOrientedBox(); bounds.Transform(e.WorldToNavMesh); bounds.GetBoundingBox(modifier.Bounds); + modifier.NavArea = navModifierVolume->GetNavArea(); e.Modifiers->Add(modifier); } @@ -443,7 +445,8 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B // Mark areas for (auto& modifier : modifiers) { - rcMarkBoxArea(&context, &modifier.Bounds.Minimum.X, &modifier.Bounds.Maximum.X, RC_NULL_AREA, *compactHeightfield); + const unsigned char areaId = modifier.NavArea ? modifier.NavArea->Id : RC_NULL_AREA; + rcMarkBoxArea(&context, &modifier.Bounds.Minimum.X, &modifier.Bounds.Maximum.X, areaId, *compactHeightfield); } if (!rcBuildDistanceField(&context, *compactHeightfield)) @@ -564,7 +567,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B offMeshArea[i] = RC_WALKABLE_AREA; offMeshFlags[i] = 1; - // TODO: support navigation areas, navigation area type for off mesh links + // TODO: support navigation area type for off mesh links } params.offMeshConCount = linksCount; @@ -884,6 +887,27 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild { auto settings = NavigationSettings::Get(); + // Validate nav areas ids to be unique and in valid range + for (int32 i = 0; i < settings->NavAreas.Count(); i++) + { + auto& a = settings->NavAreas[i]; + if (a.Id > RC_WALKABLE_AREA) + { + LOG(Error, "Nav Area {0} uses invalid Id. Valid values are in range 0-63 only.", a.Name); + return; + } + + for (int32 j = i + 1; j < settings->NavAreas.Count(); j++) + { + auto& b = settings->NavAreas[j]; + if (a.Id == b.Id) + { + LOG(Error, "Nav Area {0} uses the same Id={1} as Nav Area {2}. Each area hast to have unique Id.", a.Name, a.Id, b.Name); + return; + } + } + } + // Sync navmeshes for (auto& navMeshProperties : settings->NavMeshes) { diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index d576f3607..abd706d25 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "NavMeshRuntime.h" +#include "NavigationSettings.h" #include "NavMesh.h" #include "Engine/Core/Log.h" #include "Engine/Core/Math/Matrix.h" @@ -18,6 +19,14 @@ #define DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL 50.0f #define DEFAULT_NAV_QUERY_EXTENT_VERTICAL 250.0f +namespace +{ + FORCE_INLINE void InitFilter(dtQueryFilter& filter) + { + Platform::MemoryCopy(filter.m_areaCost, NavMeshRuntime::NavAreasCosts, sizeof(NavMeshRuntime::NavAreasCosts)); + } +} + NavMeshRuntime::NavMeshRuntime(const NavMeshProperties& properties) : Properties(properties) { @@ -48,6 +57,7 @@ bool NavMeshRuntime::FindDistanceToWall(const Vector3& startPosition, NavMeshHit } dtQueryFilter filter; + InitFilter(filter); Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); Vector3 startPositionNavMesh; @@ -85,6 +95,7 @@ bool NavMeshRuntime::FindPath(const Vector3& startPosition, const Vector3& endPo } dtQueryFilter filter; + InitFilter(filter); Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); Vector3 startPositionNavMesh, endPositionNavMesh; @@ -153,6 +164,7 @@ bool NavMeshRuntime::TestPath(const Vector3& startPosition, const Vector3& endPo } dtQueryFilter filter; + InitFilter(filter); Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); Vector3 startPositionNavMesh, endPositionNavMesh; @@ -199,6 +211,7 @@ bool NavMeshRuntime::ProjectPoint(const Vector3& point, Vector3& result) const } dtQueryFilter filter; + InitFilter(filter); Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); Vector3 pointNavMesh; @@ -229,6 +242,7 @@ bool NavMeshRuntime::RayCast(const Vector3& startPosition, const Vector3& endPos } dtQueryFilter filter; + InitFilter(filter); Vector3 extent(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL); Vector3 startPositionNavMesh, endPositionNavMesh; @@ -513,7 +527,8 @@ void DrawPoly(NavMeshRuntime* navMesh, const Matrix& navMeshToWorld, const dtMes { const unsigned int ip = (unsigned int)(&poly - tile.polys); const dtPolyDetail& pd = tile.detailMeshes[ip]; - const Color color = navMesh->Properties.Color; + const Color& areaColor = NavMeshRuntime::NavAreasColors[poly.getArea()]; + const Color color = Color::Lerp(navMesh->Properties.Color, areaColor, areaColor.A); const float drawOffsetY = 10.0f + ((float)GetHash(color) / (float)MAX_uint32) * 10.0f; // Apply some offset to prevent Z-fighting for different navmeshes const Color fillColor = color * 0.5f; const Color edgesColor = Color::FromHSV(color.ToHSV() + Vector3(20.0f, 0, -0.1f), color.A); diff --git a/Source/Engine/Navigation/NavMeshRuntime.h b/Source/Engine/Navigation/NavMeshRuntime.h index 7da4be0e2..fd8dcf794 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.h +++ b/Source/Engine/Navigation/NavMeshRuntime.h @@ -41,6 +41,12 @@ public: // Gets the navigation mesh runtime for a given navmesh properties. static NavMeshRuntime* Get(const NavMeshProperties& navMeshProperties, bool createIfMissing = false); + // The lookup table that maps areaId of the navmesh to the current properties (applied by the NavigationSettings). Cached to improve runtime performance. + static float NavAreasCosts[64]; +#if COMPILE_WITH_DEBUG_DRAW + static Color NavAreasColors[64]; +#endif + private: dtNavMesh* _navMesh; diff --git a/Source/Engine/Navigation/NavModifierVolume.cpp b/Source/Engine/Navigation/NavModifierVolume.cpp index 65325195a..533fae199 100644 --- a/Source/Engine/Navigation/NavModifierVolume.cpp +++ b/Source/Engine/Navigation/NavModifierVolume.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "NavModifierVolume.h" +#include "NavigationSettings.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Serialization/Serialization.h" #if USE_EDITOR @@ -15,6 +16,17 @@ NavModifierVolume::NavModifierVolume(const SpawnParams& params) _size = 100.0f; } +NavAreaProperties* NavModifierVolume::GetNavArea() const +{ + auto settings = NavigationSettings::Get(); + for (auto& navArea : settings->NavAreas) + { + if (navArea.Name == AreaName) + return &navArea; + } + return nullptr; +} + void NavModifierVolume::Serialize(SerializeStream& stream, const void* otherObj) { // Base @@ -23,6 +35,7 @@ void NavModifierVolume::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_GET_OTHER_OBJ(NavModifierVolume); SERIALIZE_MEMBER(AgentsMask, AgentsMask.Mask); + SERIALIZE(AreaName); } void NavModifierVolume::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -31,6 +44,7 @@ void NavModifierVolume::Deserialize(DeserializeStream& stream, ISerializeModifie BoxVolume::Deserialize(stream, modifier); DESERIALIZE_MEMBER(AgentsMask, AgentsMask.Mask); + DESERIALIZE(AreaName); } #if USE_EDITOR diff --git a/Source/Engine/Navigation/NavModifierVolume.h b/Source/Engine/Navigation/NavModifierVolume.h index 3ca0d4b20..7ee70c116 100644 --- a/Source/Engine/Navigation/NavModifierVolume.h +++ b/Source/Engine/Navigation/NavModifierVolume.h @@ -16,9 +16,22 @@ public: /// /// The agent types used by this navmesh modifier volume (from navigation settings). Can be used to adjust navmesh for a certain set of agents. /// - API_FIELD(Attributes="EditorDisplay(\"Box Volume\"), EditorOrder(10)") + API_FIELD(Attributes="EditorDisplay(\"Navigation\"), EditorOrder(10)") NavAgentMask AgentsMask; + /// + /// The name of the nav area to apply within the modifiers volume. Nav area properties are picked from the Navigation Settings asset. + /// + API_FIELD(Attributes="EditorDisplay(\"Navigation\"), EditorOrder(20)") + String AreaName; + +public: + + /// + /// Gets the properties of the nav area used by this volume. Null if missing or invalid area name. + /// + NavAreaProperties* GetNavArea() const; + public: // [BoxVolume] diff --git a/Source/Engine/Navigation/Navigation.cpp b/Source/Engine/Navigation/Navigation.cpp index d5a516870..9ff3498d7 100644 --- a/Source/Engine/Navigation/Navigation.cpp +++ b/Source/Engine/Navigation/Navigation.cpp @@ -92,6 +92,12 @@ NavMeshRuntime* NavMeshRuntime::Get(const NavMeshProperties& navMeshProperties, return result; } +static_assert(ARRAY_COUNT(NavMeshRuntime::NavAreasCosts) == DT_MAX_AREAS, "Invalid nav areas amount limit."); +float NavMeshRuntime::NavAreasCosts[64]; +#if COMPILE_WITH_DEBUG_DRAW +Color NavMeshRuntime::NavAreasColors[64]; +#endif + bool NavAgentProperties::operator==(const NavAgentProperties& other) const { return Math::NearEqual(Radius, other.Radius) && Math::NearEqual(Height, other.Height) && Math::NearEqual(StepHeight, other.StepHeight) && Math::NearEqual(MaxSlopeAngle, other.MaxSlopeAngle); @@ -176,13 +182,42 @@ void* rcAllocDefault(size_t size, rcAllocHint) NavigationSettings::NavigationSettings() { + // Init navmeshes NavMeshes.Resize(1); auto& navMesh = NavMeshes[0]; navMesh.Name = TEXT("Default"); + + // Init nav areas + NavAreas.Resize(2); + auto& areaNull = NavAreas[0]; + areaNull.Name = TEXT("Null"); + areaNull.Color = Color::Transparent; + areaNull.Id = 0; + areaNull.Cost = MAX_float; + auto& areaWalkable = NavAreas[1]; + areaWalkable.Name = TEXT("Walkable"); + areaWalkable.Color = Color::Transparent; + areaWalkable.Id = 63; + areaWalkable.Cost = 1; } IMPLEMENT_SETTINGS_GETTER(NavigationSettings, Navigation); +void NavigationSettings::Apply() +{ + // Cache areas properties + for (auto& area : NavAreas) + { + if (area.Id < DT_MAX_AREAS) + { + NavMeshRuntime::NavAreasCosts[area.Id] = area.Cost; +#if USE_EDITOR + NavMeshRuntime::NavAreasColors[area.Id] = area.Color; +#endif + } + } +} + void NavigationSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) { DESERIALIZE(AutoAddMissingNavMeshes); @@ -219,6 +254,7 @@ void NavigationSettings::Deserialize(DeserializeStream& stream, ISerializeModifi navMesh.Agent.StepHeight = WalkableMaxClimb; navMesh.Agent.MaxSlopeAngle = WalkableMaxSlopeAngle; } + DESERIALIZE(NavAreas); } bool NavigationService::Init() diff --git a/Source/Engine/Navigation/NavigationSettings.cs b/Source/Engine/Navigation/NavigationSettings.cs index 4127ebe6b..8a97e4adc 100644 --- a/Source/Engine/Navigation/NavigationSettings.cs +++ b/Source/Engine/Navigation/NavigationSettings.cs @@ -7,6 +7,36 @@ namespace FlaxEditor.Content.Settings { partial class NavigationSettings { + /// + /// Initializes a new instance of the class. + /// + public NavigationSettings() + { + // Init navmeshes + NavMeshes = new NavMeshProperties[1]; + ref var navMesh = ref NavMeshes[0]; + navMesh.Name = "Default"; + navMesh.Color = Color.Green; + navMesh.Rotation = Quaternion.Identity; + navMesh.Agent.Radius = 34.0f; + navMesh.Agent.Height = 144.0f; + navMesh.Agent.StepHeight = 35.0f; + navMesh.Agent.MaxSlopeAngle = 60.0f; + + // Init nav areas + NavAreas = new NavAreaProperties[2]; + ref var areaNull = ref NavAreas[0]; + areaNull.Name = "Null"; + areaNull.Color = Color.Transparent; + areaNull.Id = 0; + areaNull.Cost = float.MaxValue; + ref var areaWalkable = ref NavAreas[1]; + areaWalkable.Name = "Walkable"; + areaWalkable.Color = Color.Transparent; + areaWalkable.Id = 63; + areaWalkable.Cost = 1; + } + // [Deprecated on 12.01.2021, expires on 12.01.2022] private void UpgradeToNavMeshes() { diff --git a/Source/Engine/Navigation/NavigationSettings.h b/Source/Engine/Navigation/NavigationSettings.h index e225a2f07..0b35d5153 100644 --- a/Source/Engine/Navigation/NavigationSettings.h +++ b/Source/Engine/Navigation/NavigationSettings.h @@ -9,7 +9,7 @@ /// /// The navigation system settings container. /// -API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API NavigationSettings : public SettingsBase +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class FLAXENGINE_API NavigationSettings : public SettingsBase { DECLARE_SCRIPTING_TYPE_MINIMAL(NavigationSettings); public: @@ -87,9 +87,15 @@ public: /// /// The configuration for navmeshes. /// - API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Agents\")") + API_FIELD(Attributes="EditorOrder(1000), EditorDisplay(\"Agents\", EditorDisplayAttribute.InlineStyle)") Array NavMeshes; + /// + /// The configuration for nav areas. + /// + API_FIELD(Attributes="EditorOrder(2000), EditorDisplay(\"Areas\", EditorDisplayAttribute.InlineStyle)") + Array NavAreas; + public: NavigationSettings(); @@ -100,5 +106,6 @@ public: static NavigationSettings* Get(); // [SettingsBase] + void Apply() override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override; }; diff --git a/Source/Engine/Navigation/NavigationTypes.h b/Source/Engine/Navigation/NavigationTypes.h index e604580f6..11ebaeb1a 100644 --- a/Source/Engine/Navigation/NavigationTypes.h +++ b/Source/Engine/Navigation/NavigationTypes.h @@ -156,18 +156,18 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(NavAreaProperties); /// The area type color (for debugging). Alpha channel is used to blend with navmesh color (alpha 0 to use navmesh color only). /// API_FIELD(Attributes="EditorOrder(10)") - Color Color = Color::Red; + Color Color = Color::Transparent; /// /// The area id. It must be unique for the project. Valid range 0-63. Value 0 is reserved for Null areas (empty, non-navigable areas). /// API_FIELD(Attributes="EditorOrder(20), Limit(0, 63)") - byte Id = 1; + byte Id; /// /// The cost scale for the area traversal for agents. The higher the cost, the less likely agent wil choose the path that goes over it. For instance, areas that are harder to move like sand should have higher cost for proper path finding. /// - API_FIELD(Attributes="EditorOrder(30), Limit(0, 100000, 0.1f)") + API_FIELD(Attributes="EditorOrder(30), Limit(0, float.MaxValue, 0.1f)") float Cost = 1; bool operator==(const NavAreaProperties& other) const; diff --git a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.h b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.h index 1c23e4857..d9f940e2d 100644 --- a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.h +++ b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.h @@ -34,6 +34,7 @@ /// @ingroup detour class dtQueryFilter { +public: float m_areaCost[DT_MAX_AREAS]; ///< Cost per area type. (Used by default implementation.) unsigned short m_includeFlags; ///< Flags for polygons that can be visited. (Used by default implementation.) unsigned short m_excludeFlags; ///< Flags for polygons that should not be visted. (Used by default implementation.) From 2bfcc15f66c776b38c78fbcd44d330471c78a955 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 15:00:35 +0100 Subject: [PATCH 101/419] Add invoking `BoxVolume::OnBoundsChanged` after transform changed --- Source/Engine/Level/Actors/BoxVolume.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Level/Actors/BoxVolume.cpp b/Source/Engine/Level/Actors/BoxVolume.cpp index 6235244bc..91912fa69 100644 --- a/Source/Engine/Level/Actors/BoxVolume.cpp +++ b/Source/Engine/Level/Actors/BoxVolume.cpp @@ -132,8 +132,10 @@ void BoxVolume::OnTransformChanged() // Base Actor::OnTransformChanged(); + const auto prevBounds = _box; OrientedBoundingBox::CreateCentered(Vector3::Zero, _size, _bounds); _bounds.Transform(_transform.GetWorld()); _bounds.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); + OnBoundsChanged(prevBounds); } From f80c33f381306843ac6028848b987104aeffaf03 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 15:07:01 +0100 Subject: [PATCH 102/419] Add Actor.HasStaticFlag --- Source/Engine/Level/Actor.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 88da10f0e..53f8fe163 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -367,6 +367,14 @@ public: /// The value to set. API_PROPERTY() virtual void SetStaticFlags(StaticFlags value); + /// + /// Returns true if object has given flag(s) set. + /// + FORCE_INLINE bool HasStaticFlag(StaticFlags flag) const + { + return (_staticFlags & flag) == (int)flag; + } + /// /// Adds the actor static flags. /// From e0f1f18998c5608bbe3e77206eaa9ab1696d6346 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 15:07:39 +0100 Subject: [PATCH 103/419] Move Actor static flags helper methods to be manually implemented (less bindings) --- Source/Engine/Level/Actor.cs | 60 ++++++++++++++++++++++++++++++++++++ Source/Engine/Level/Actor.h | 10 +++--- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Level/Actor.cs b/Source/Engine/Level/Actor.cs index 674842a65..260fef44e 100644 --- a/Source/Engine/Level/Actor.cs +++ b/Source/Engine/Level/Actor.cs @@ -6,6 +6,66 @@ namespace FlaxEngine { partial class Actor : ITransformable, ISceneObject { + /// + /// Returns true if object is fully static on the scene, otherwise false. + /// + [Unmanaged] + [Tooltip("Returns true if object is fully static on the scene, otherwise false.")] + public bool IsStatic => StaticFlags == FlaxEngine.StaticFlags.FullyStatic; + + /// + /// Returns true if object has static transform. + /// + [Unmanaged] + [Tooltip("Returns true if object has static transform.")] + public bool IsTransformStatic => (StaticFlags & StaticFlags.Transform) == StaticFlags.Transform; + + /// + /// Adds the actor static flags. + /// + /// The flags to add. + [Unmanaged] + [Tooltip("Adds the actor static flags.")] + public void AddStaticFlags(StaticFlags flags) + { + StaticFlags |= flags; + } + + /// + /// Removes the actor static flags. + /// + /// The flags to remove. + [Unmanaged] + [Tooltip("Removes the actor static flags.")] + public void RemoveStaticFlags(StaticFlags flags) + { + StaticFlags &= ~flags; + } + + /// + /// Sets a single static flag to the desire value. + /// + /// The flag to change. + /// The target value of the flag. + [Unmanaged] + [Tooltip("Sets a single static flag to the desire value.")] + public void SetStaticFlag(StaticFlags flag, bool value) + { + StaticFlags = StaticFlags & ~flag | (value ? flag : StaticFlags.None); + } + + /// + /// Returns true if object has given flag(s) set. + /// + /// The flag(s) to check. + /// True if has flag(s) set, otherwise false. + [Unmanaged] + [Tooltip("Returns true if object has given flag(s) set..")] + public bool HasStaticFlag(StaticFlags flag) + { + return (StaticFlags & flag) == flag; + } + /// /// The rotation as Euler angles in degrees. /// diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 53f8fe163..da08bc498 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -339,7 +339,7 @@ public: /// /// Returns true if object is fully static on the scene, otherwise false. /// - API_PROPERTY() FORCE_INLINE bool IsStatic() const + FORCE_INLINE bool IsStatic() const { return _staticFlags == StaticFlags::FullyStatic; } @@ -347,7 +347,7 @@ public: /// /// Returns true if object has static transform. /// - API_PROPERTY() FORCE_INLINE bool IsTransformStatic() const + FORCE_INLINE bool IsTransformStatic() const { return (_staticFlags & StaticFlags::Transform) != 0; } @@ -379,7 +379,7 @@ public: /// Adds the actor static flags. /// /// The flags to add. - API_FUNCTION() FORCE_INLINE void AddStaticFlags(StaticFlags flags) + FORCE_INLINE void AddStaticFlags(StaticFlags flags) { SetStaticFlags(_staticFlags | flags); } @@ -388,7 +388,7 @@ public: /// Removes the actor static flags. /// /// The flags to remove. - API_FUNCTION() FORCE_INLINE void RemoveStaticFlags(StaticFlags flags) + FORCE_INLINE void RemoveStaticFlags(StaticFlags flags) { SetStaticFlags(static_cast(_staticFlags & ~flags)); } @@ -398,7 +398,7 @@ public: /// /// The flag to change. /// The target value of the flag. - API_FUNCTION() FORCE_INLINE void SetStaticFlag(StaticFlags flag, bool value) + FORCE_INLINE void SetStaticFlag(StaticFlags flag, bool value) { SetStaticFlags(static_cast(_staticFlags & ~flag) | (value ? flag : StaticFlags::None)); } From d19c31555b457f8e9beaee82981c51f66989390a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 15:08:00 +0100 Subject: [PATCH 104/419] Add support for dynamic updating navmesh when moving NavModifierVolume --- .../Engine/Navigation/NavModifierVolume.cpp | 26 +++++++++++++++---- Source/Engine/Navigation/NavModifierVolume.h | 2 +- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Navigation/NavModifierVolume.cpp b/Source/Engine/Navigation/NavModifierVolume.cpp index 533fae199..e72393a03 100644 --- a/Source/Engine/Navigation/NavModifierVolume.cpp +++ b/Source/Engine/Navigation/NavModifierVolume.cpp @@ -2,12 +2,12 @@ #include "NavModifierVolume.h" #include "NavigationSettings.h" +#include "NavMeshBuilder.h" #include "Engine/Level/Scene/Scene.h" #include "Engine/Serialization/Serialization.h" #if USE_EDITOR #include "Editor/Editor.h" #include "Editor/Managed/ManagedEditor.h" -#include "NavMeshBuilder.h" #endif NavModifierVolume::NavModifierVolume(const SpawnParams& params) @@ -47,19 +47,35 @@ void NavModifierVolume::Deserialize(DeserializeStream& stream, ISerializeModifie DESERIALIZE(AreaName); } -#if USE_EDITOR - void NavModifierVolume::OnBoundsChanged(const BoundingBox& prevBounds) { +#if COMPILE_WITH_NAV_MESH_BUILDER // Auto-rebuild modified navmesh area - if (IsDuringPlay() && IsActiveInHierarchy() && !Editor::IsPlayMode && Editor::Managed->CanAutoBuildNavMesh()) + if ( + IsDuringPlay() && IsActiveInHierarchy() && HasStaticFlag(StaticFlags::Navigation) && + ( + // Build at runtime for dynamic modifiers + !HasStaticFlag(StaticFlags::Transform) +#if USE_EDITOR + // Build in editor when using auto-rebuild option + || (!Editor::IsPlayMode && Editor::Managed->CanAutoBuildNavMesh()) +#endif + )) { BoundingBox dirtyBounds; BoundingBox::Merge(prevBounds, _box, dirtyBounds); - NavMeshBuilder::Build(GetScene(), dirtyBounds, ManagedEditor::ManagedEditorOptions.AutoRebuildNavMeshTimeoutMs); +#if USE_EDITOR + const float timeoutMs = ManagedEditor::ManagedEditorOptions.AutoRebuildNavMeshTimeoutMs; +#else + const float timeoutMs = 0.0f; +#endif + NavMeshBuilder::Build(GetScene(), dirtyBounds, timeoutMs); } +#endif } +#if USE_EDITOR + Color NavModifierVolume::GetWiresColor() { return Color::Red; diff --git a/Source/Engine/Navigation/NavModifierVolume.h b/Source/Engine/Navigation/NavModifierVolume.h index 7ee70c116..9b6a3e764 100644 --- a/Source/Engine/Navigation/NavModifierVolume.h +++ b/Source/Engine/Navigation/NavModifierVolume.h @@ -41,8 +41,8 @@ public: protected: // [BoxVolume] -#if USE_EDITOR void OnBoundsChanged(const BoundingBox& prevBounds) override; +#if USE_EDITOR Color GetWiresColor() override; #endif }; From ef240ce9860b9c8fcab5f5927450effda4bf7492 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 15:53:15 +0100 Subject: [PATCH 105/419] Add culling for decals (via DrawMinScreenSize property) --- Source/Engine/Level/Actors/Decal.cpp | 10 ++++++++++ Source/Engine/Level/Actors/Decal.h | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actors/Decal.cpp b/Source/Engine/Level/Actors/Decal.cpp index 92652ea7d..c7e8475b3 100644 --- a/Source/Engine/Level/Actors/Decal.cpp +++ b/Source/Engine/Level/Actors/Decal.cpp @@ -7,6 +7,7 @@ #include "Engine/Level/Scene/SceneRendering.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Graphics/RenderTask.h" +#include "Engine/Graphics/RenderTools.h" Decal::Decal(const SpawnParams& params) : Actor(params) @@ -66,6 +67,13 @@ void Decal::Draw(RenderContext& renderContext) Material->IsLoaded() && Material->IsDecal()) { + const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); + const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(_sphere.Center, _sphere.Radius, *lodView) * renderContext.View.ModelLODDistanceFactorSqrt; + + // Check if decal is being culled + if (Math::Square(DrawMinScreenSize * 0.5f) > screenRadiusSquared) + return; + renderContext.List->Decals.Add(this); } } @@ -80,6 +88,7 @@ void Decal::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE(Material); SERIALIZE_MEMBER(Size, _size); SERIALIZE(SortOrder); + SERIALIZE(DrawMinScreenSize); } void Decal::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -90,6 +99,7 @@ void Decal::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) DESERIALIZE(Material); DESERIALIZE_MEMBER(Size, _size); DESERIALIZE(SortOrder); + DESERIALIZE(DrawMinScreenSize); } bool Decal::IntersectsItself(const Ray& ray, float& distance, Vector3& normal) diff --git a/Source/Engine/Level/Actors/Decal.h b/Source/Engine/Level/Actors/Decal.h index 41a5addea..78a92bfd6 100644 --- a/Source/Engine/Level/Actors/Decal.h +++ b/Source/Engine/Level/Actors/Decal.h @@ -30,9 +30,15 @@ public: /// /// The decal rendering order. The higher values are render later (on top). /// - API_FIELD(Attributes="EditorOrder(20), DefaultValue(0), EditorDisplay(\"Decal\")") + API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"Decal\")") int32 SortOrder = 0; + /// + /// The minimum screen size for the decal drawing. If the decal size on the screen is smaller than this value then decal will be culled. Set it to higher value to make culling more aggressive. + /// + API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Decal\")") + float DrawMinScreenSize = 0.02f; + /// /// Gets the decal bounds size (in local space). /// From c4e6d013c11210c157b5931e71c1c72b98c123eb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 18 Jan 2021 22:47:31 +0100 Subject: [PATCH 106/419] Add more splash screen quotes --- Source/Editor/Windows/SplashScreen.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index 98578950f..1e4ff0af0 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -110,6 +110,9 @@ const Char* SplashScreenQuotes[] = TEXT("You have my bow.\nAnd my axe!"), TEXT("To the bridge of Khazad-dum."), TEXT("One ring to rule them all.\nOne ring to find them."), + TEXT("Ladies and gentelman, we got him"), + TEXT("Cyberpunk of game engines"), + TEXT("That's what she said"), }; SplashScreen::~SplashScreen() From bd7a681700160a43915b35e24dc6396eb0402aec Mon Sep 17 00:00:00 2001 From: stefnotch Date: Tue, 19 Jan 2021 21:08:26 +0100 Subject: [PATCH 107/419] Add more descriptive error message --- Source/Editor/Cooker/Steps/ValidateStep.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Cooker/Steps/ValidateStep.cpp b/Source/Editor/Cooker/Steps/ValidateStep.cpp index 219301544..fdebca0a7 100644 --- a/Source/Editor/Cooker/Steps/ValidateStep.cpp +++ b/Source/Editor/Cooker/Steps/ValidateStep.cpp @@ -60,7 +60,7 @@ bool ValidateStep::Perform(CookingData& data) AssetInfo info; if (!Content::GetAssetInfo(GameSettings::FirstScene, info)) { - data.Error(TEXT("Missing first scene.")); + data.Error(TEXT("Missing first scene. Set it in the game settings.")); return true; } } From 0a0c856a64c110dd203203a9058ebf69ffae8070 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 19 Jan 2021 21:10:45 +0100 Subject: [PATCH 108/419] Add more splash screen quotes --- Source/Editor/Windows/SplashScreen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index 1e4ff0af0..b05c73761 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -113,6 +113,8 @@ const Char* SplashScreenQuotes[] = TEXT("Ladies and gentelman, we got him"), TEXT("Cyberpunk of game engines"), TEXT("That's what she said"), + TEXT("Compiling Shaders (93,788)"), + TEXT("Hi There"), }; SplashScreen::~SplashScreen() From 75d197cb847065ae5e2ec4ad6240de1836a1197f Mon Sep 17 00:00:00 2001 From: intolerantape Date: Tue, 19 Jan 2021 23:02:33 -0800 Subject: [PATCH 109/419] Spelling fixes. --- Source/Editor/Surface/AnimGraphSurface.cs | 4 ++-- Source/Editor/Surface/Archetypes/Animation.cs | 4 ++-- Source/Engine/Animations/Graph/AnimGroup.Animation.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/AnimGraphSurface.cs b/Source/Editor/Surface/AnimGraphSurface.cs index 8621ed257..abffc4432 100644 --- a/Source/Editor/Surface/AnimGraphSurface.cs +++ b/Source/Editor/Surface/AnimGraphSurface.cs @@ -65,8 +65,8 @@ namespace FlaxEditor.Surface NodeElementArchetype.Factory.Output(0, "Length", typeof(float), 0), NodeElementArchetype.Factory.Output(1, "Time", typeof(float), 1), NodeElementArchetype.Factory.Output(2, "Normalized Time", typeof(float), 2), - NodeElementArchetype.Factory.Output(3, "Reaming Time", typeof(float), 3), - NodeElementArchetype.Factory.Output(4, "Reaming Normalized Time", typeof(float), 4), + NodeElementArchetype.Factory.Output(3, "Remaining Time", typeof(float), 3), + NodeElementArchetype.Factory.Output(4, "Remaining Normalized Time", typeof(float), 4), } }, } diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index b27fb9bd6..2ec196e13 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -764,8 +764,8 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "Length", typeof(float), 0), NodeElementArchetype.Factory.Output(1, "Time", typeof(float), 1), NodeElementArchetype.Factory.Output(2, "Normalized Time", typeof(float), 2), - NodeElementArchetype.Factory.Output(3, "Reaming Time", typeof(float), 3), - NodeElementArchetype.Factory.Output(4, "Reaming Normalized Time", typeof(float), 4), + NodeElementArchetype.Factory.Output(3, "Remaining Time", typeof(float), 3), + NodeElementArchetype.Factory.Output(4, "Remaining Normalized Time", typeof(float), 4), } }, new NodeArchetype diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 8f43506d2..1a26318a1 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1489,11 +1489,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu case 2: value = transitionsData.Position / transitionsData.Length; break; - // Reaming Time + // Remaining Time case 3: value = transitionsData.Length - transitionsData.Position; break; - // Reaming Normalized Time + // Remaining Normalized Time case 4: value = 1.0f - (transitionsData.Position / transitionsData.Length); break; From f8bf87e0b100778deb437b037b3d953e636c77dc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Jan 2021 09:14:30 +0100 Subject: [PATCH 110/419] Fixes --- Source/Engine/Navigation/NavMeshRuntime.cpp | 8 +++++--- .../Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index abd706d25..3d112a39e 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -644,7 +644,10 @@ void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData if (tileRef) { // Remove any existing tile at that location - _navMesh->removeTile(tileRef, nullptr, nullptr); + if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr))) + { + LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name); + } // Reuse tile data container for (int32 i = 0; i < _tiles.Count(); i++) @@ -657,12 +660,11 @@ void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData } } } - else + if (!tile) { // Add tile tile = &_tiles.AddOne(); } - ASSERT(tile); // Copy tile properties tile->NavMesh = navMesh; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index a25e00fbb..8b679fb7a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -402,7 +402,7 @@ namespace Flax.Build.Bindings return false; // Value constructors (eg. Vector2(1, 2)) - // TODO: support value constructores for default value attribute + // TODO: support value constructors for default value attribute if (value.Contains('(') && value.Contains(')')) return false; @@ -1096,7 +1096,7 @@ namespace Flax.Build.Bindings Utilities.WriteFileIfChanged(bindings.GeneratedCSharpFilePath, contents.ToString()); // Ensure that generated file is included into build - if (useBindings && moduleBuildInfo != null && !moduleBuildInfo.SourceFiles.Contains(bindings.GeneratedCSharpFilePath)) + if (!moduleBuildInfo.SourceFiles.Contains(bindings.GeneratedCSharpFilePath)) { moduleBuildInfo.SourceFiles.Add(bindings.GeneratedCSharpFilePath); } From 9ae79b830739f234f3f968d858c88524773eb8a2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Jan 2021 11:13:09 +0100 Subject: [PATCH 111/419] Add IFunctionDependantNode for Visject nodes --- Source/Editor/Surface/Archetypes/Function.cs | 52 ++++++++++++++++++- .../Editor/Surface/IFunctionDependantNode.cs | 31 +++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 Source/Editor/Surface/IFunctionDependantNode.cs diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 7c4963072..b7584a2b8 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -1387,6 +1387,9 @@ namespace FlaxEditor.Surface.Archetypes } } + /// + /// The cached signature. This might not be loaded if called from other not initialization (eg. node initialized before this function node). Then use GetSignature method. + /// internal Signature _signature; /// @@ -1395,6 +1398,13 @@ namespace FlaxEditor.Surface.Archetypes { } + public void GetSignature(out Signature signature) + { + if (_signature.Node == null) + LoadSignature(); + signature = _signature; + } + private void SaveSignature() { using (var stream = new MemoryStream()) @@ -1568,6 +1578,13 @@ namespace FlaxEditor.Surface.Archetypes // Update node interface UpdateUI(); + + // Send event + for (int i = 0; i < Surface.Nodes.Count; i++) + { + if (Surface.Nodes[i] is IFunctionsDependantNode node) + node.OnFunctionEdited(this); + } }; editor.Show(this, Vector2.Zero); } @@ -1622,6 +1639,13 @@ namespace FlaxEditor.Surface.Archetypes base.OnLoaded(); LoadSignature(); + + // Send event + for (int i = 0; i < Surface.Nodes.Count; i++) + { + if (Surface.Nodes[i] is IFunctionsDependantNode node) + node.OnFunctionCreated(this); + } } /// @@ -1639,6 +1663,26 @@ namespace FlaxEditor.Surface.Archetypes // Start editing OnEditSignature(); + + // Send event + for (int i = 0; i < Surface.Nodes.Count; i++) + { + if (Surface.Nodes[i] is IFunctionsDependantNode node) + node.OnFunctionCreated(this); + } + } + + /// + public override void OnDeleted() + { + // Send event + for (int i = 0; i < Surface.Nodes.Count; i++) + { + if (Surface.Nodes[i] is IFunctionsDependantNode node) + node.OnFunctionDeleted(this); + } + + base.OnDeleted(); } /// @@ -1647,6 +1691,13 @@ namespace FlaxEditor.Surface.Archetypes base.OnValuesChanged(); LoadSignature(); + + // Send event + for (int i = 0; i < Surface.Nodes.Count; i++) + { + if (Surface.Nodes[i] is IFunctionsDependantNode node) + node.OnFunctionEdited(this); + } } /// @@ -1752,7 +1803,6 @@ namespace FlaxEditor.Surface.Archetypes { private bool _isTypesChangedEventRegistered; - /// public SetFieldNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) : base(id, context, nodeArch, groupArch) { diff --git a/Source/Editor/Surface/IFunctionDependantNode.cs b/Source/Editor/Surface/IFunctionDependantNode.cs new file mode 100644 index 000000000..e66c10e92 --- /dev/null +++ b/Source/Editor/Surface/IFunctionDependantNode.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using FlaxEngine; + +namespace FlaxEditor.Surface +{ + /// + /// Interface for surface nodes that depend on surface function nodes collection. + /// + [HideInEditor] + public interface IFunctionsDependantNode + { + /// + /// On function created. + /// + /// The function node. + void OnFunctionCreated(SurfaceNode node); + + /// + /// On function signature changed (new name or parameters change). + /// + /// The function node. + void OnFunctionEdited(SurfaceNode node); + + /// + /// On function removed. + /// + /// The function node. + void OnFunctionDeleted(SurfaceNode node); + } +} From ea489fbf2a57f9bb8f8489f04cdcb6d85bd72509 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Jan 2021 13:11:45 +0100 Subject: [PATCH 112/419] Fix crash in motion blur tile size calculation --- Source/Engine/Renderer/MotionBlurPass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index 609712e1f..46b2ccd8f 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -285,7 +285,7 @@ void MotionBlurPass::Render(RenderContext& renderContext, GPUTexture*& input, GP PROFILE_GPU_CPU("Motion Blur"); // Setup shader inputs - const int32 maxBlurSize = (int32)((float)motionVectorsHeight * 0.05f); + const int32 maxBlurSize = Math::Max((int32)((float)motionVectorsHeight * 0.05f), 1); const int32 tileSize = Math::AlignUp(maxBlurSize, 8); const float timeScale = renderContext.Task->View.IsOfflinePass ? 1.0f : 1.0f / Time::Draw.UnscaledDeltaTime.GetTotalSeconds() / 60.0f; // 60fps as a reference Data data; From ef5e98efaa7fc3552ba43efaf3046dcc4b10cfe8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Jan 2021 13:12:11 +0100 Subject: [PATCH 113/419] Fix assertion during engine shutdown after crash during rendering --- Source/Engine/Graphics/Graphics.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Engine/Graphics/Graphics.cpp b/Source/Engine/Graphics/Graphics.cpp index 21b506ef1..69170dca0 100644 --- a/Source/Engine/Graphics/Graphics.cpp +++ b/Source/Engine/Graphics/Graphics.cpp @@ -49,6 +49,9 @@ GraphicsService GraphicsServiceInstance; void Graphics::DisposeDevice() { + // Clean any danging pointer to last task (might stay if engine is disposing after crash) + GPUDevice::Instance->CurrentTask = nullptr; + if (GPUDevice::Instance) { GPUDevice::Instance->Dispose(); From 8dce3c0fb76b7dc35abe9fd8cdc81605cb315d2e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Jan 2021 13:38:42 +0100 Subject: [PATCH 114/419] Fix missing animated model parameter error --- Source/Engine/Level/Actors/AnimatedModel.cpp | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index cf3e71f13..3d6450716 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -146,8 +146,32 @@ void AnimatedModel::GetNodeTransformation(const StringView& nodeName, Matrix& no GetNodeTransformation(SkinnedModel ? SkinnedModel->FindNode(nodeName) : -1, nodeTransformation, worldSpace); } +#define CHECK_ANIM_GRAPH_PARAM_ACCESS() \ + if (!AnimationGraph) \ + { \ + LOG(Warning, "Missing animation graph for animated model '{0}'", ToString()); \ + return; \ + } \ + if (AnimationGraph->WaitForLoaded()) \ + { \ + LOG(Warning, "Failed to load animation graph for animated model '{0}'", ToString()); \ + return; \ + } +#define CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(result) \ + if (!AnimationGraph) \ + { \ + LOG(Warning, "Missing animation graph for animated model '{0}'", ToString()); \ + return result; \ + } \ + if (AnimationGraph->WaitForLoaded()) \ + { \ + LOG(Warning, "Failed to load animation graph for animated model '{0}'", ToString()); \ + return result; \ + } + AnimGraphParameter* AnimatedModel::GetParameter(const StringView& name) { + CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(nullptr); for (auto& param : GraphInstance.Parameters) { if (param.Name == name) @@ -159,6 +183,7 @@ AnimGraphParameter* AnimatedModel::GetParameter(const StringView& name) Variant AnimatedModel::GetParameterValue(const StringView& name) { + CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(Variant::Null); for (auto& param : GraphInstance.Parameters) { if (param.Name == name) @@ -170,6 +195,7 @@ Variant AnimatedModel::GetParameterValue(const StringView& name) void AnimatedModel::SetParameterValue(const StringView& name, const Variant& value) { + CHECK_ANIM_GRAPH_PARAM_ACCESS(); for (auto& param : GraphInstance.Parameters) { if (param.Name == name) @@ -183,6 +209,7 @@ void AnimatedModel::SetParameterValue(const StringView& name, const Variant& val Variant AnimatedModel::GetParameterValue(const Guid& id) { + CHECK_ANIM_GRAPH_PARAM_ACCESS_RESULT(Variant::Null); for (auto& param : GraphInstance.Parameters) { if (param.Identifier == id) @@ -194,6 +221,7 @@ Variant AnimatedModel::GetParameterValue(const Guid& id) void AnimatedModel::SetParameterValue(const Guid& id, const Variant& value) { + CHECK_ANIM_GRAPH_PARAM_ACCESS(); for (auto& param : GraphInstance.Parameters) { if (param.Identifier == id) From 8680aba8971e06248a60e937d0013e312178b2e2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 20 Jan 2021 13:45:49 +0100 Subject: [PATCH 115/419] Fix missing public empty constructor for UICanvas --- Source/Engine/UI/UICanvas.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index f07910975..e0b6425de 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -228,7 +228,10 @@ namespace FlaxEngine } } - private UICanvas() + /// + /// Initializes a new instance of the class. + /// + public UICanvas() { _guiRoot = new CanvasRootControl(this); _guiRoot.IsLayoutLocked = false; From 57d7508e25536807b944267eeb518cab00999134 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Jan 2021 14:26:22 +0100 Subject: [PATCH 116/419] Fix some editor UI controls visible in Visual Scripting --- Source/Editor/GUI/CurveEditor.cs | 1 + Source/Editor/GUI/Tabs/Tabs.cs | 1 + Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs | 1 + Source/Editor/GUI/Timeline/Timeline.cs | 1 + Source/Editor/Progress/ProgressHandler.cs | 1 + 5 files changed, 5 insertions(+) diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 8840a7f06..3e37b591c 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -16,6 +16,7 @@ namespace FlaxEditor.GUI /// /// The base class for editors. Allows to use generic curve editor without type information at compile-time. /// + [HideInEditor] public abstract class CurveEditorBase : ContainerControl { /// diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index 377c6827d..5b1317929 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -11,6 +11,7 @@ namespace FlaxEditor.GUI.Tabs /// Represents control which contains collection of . /// /// + [HideInEditor] public class Tabs : ContainerControl { /// diff --git a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs index abd3e78a8..2e26b9045 100644 --- a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs +++ b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs @@ -14,6 +14,7 @@ namespace FlaxEditor.GUI /// The generic keyframes animation editor control. /// /// + [HideInEditor] public class KeyframesEditor : ContainerControl { /// diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index 149f4a3c8..e0e4083c1 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -20,6 +20,7 @@ namespace FlaxEditor.GUI.Timeline /// The timeline control that contains tracks section and headers. Can be used to create time-based media interface for camera tracks editing, audio mixing and events tracking. /// /// + [HideInEditor] public class Timeline : ContainerControl { private static readonly KeyValuePair[] FPSValues = diff --git a/Source/Editor/Progress/ProgressHandler.cs b/Source/Editor/Progress/ProgressHandler.cs index da2782d16..f008a5a03 100644 --- a/Source/Editor/Progress/ProgressHandler.cs +++ b/Source/Editor/Progress/ProgressHandler.cs @@ -8,6 +8,7 @@ namespace FlaxEditor.Progress /// /// Base class for all editor handlers used to report actions progress to the user. /// + [HideInEditor] public abstract class ProgressHandler { /// From d93a2803cc9202caecf286d22f859675f94f0a26 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Jan 2021 14:26:52 +0100 Subject: [PATCH 117/419] Fix MAssembly classes dictionary cache allocation to be during assembly load --- Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp b/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp index 50a8b689e..6c8e8f562 100644 --- a/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MAssembly.Mono.cpp @@ -29,7 +29,6 @@ MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name, const MAssembl , _isDependency(false) , _isFileLocked(false) , _hasCachedClasses(false) - , _classes(options.DictionaryInitialSize) , _reloadCount(0) , _name(name) , _options(options) @@ -342,6 +341,8 @@ void MAssembly::OnLoading() Loading(this); _isLoading = true; + if (_classes.Capacity() == 0) + _classes.EnsureCapacity(_options.DictionaryInitialSize); // Pick a domain if (_domain == nullptr) From 178f0b9e3a923691176cb18e1b558a8c46b38d7d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Jan 2021 14:27:26 +0100 Subject: [PATCH 118/419] Fix Dictionary to call ctor/dtor for Buckets when needed --- Source/Engine/Core/Collections/Dictionary.h | 115 ++++++-------------- 1 file changed, 36 insertions(+), 79 deletions(-) diff --git a/Source/Engine/Core/Collections/Dictionary.h b/Source/Engine/Core/Collections/Dictionary.h index e61cfc4d9..99bad0bef 100644 --- a/Source/Engine/Core/Collections/Dictionary.h +++ b/Source/Engine/Core/Collections/Dictionary.h @@ -13,7 +13,6 @@ template API_CLASS(InBuild) class Dictionary { friend Dictionary; - public: /// @@ -23,94 +22,73 @@ public: { friend Dictionary; - public: - enum State : byte { - Empty, - Deleted, - Occupied, + Empty = 0, + Deleted = 1, + Occupied = 2, }; public: - /// The key. KeyType Key; /// The value. ValueType Value; private: - State _state; - public: - - Bucket() - : _state(Empty) - { - } - - ~Bucket() - { - } - - public: - void Free() { + if (_state == Occupied) + { + Memory::DestructItem(&Key); + Memory::DestructItem(&Value); + } _state = Empty; } void Delete() { _state = Deleted; + Memory::DestructItem(&Key); + Memory::DestructItem(&Value); } template void Occupy(const KeyComparableType& key) { - Key = key; + Memory::ConstructItems(&Key, &key, 1); + Memory::ConstructItem(&Value); _state = Occupied; } template void Occupy(const KeyComparableType& key, const ValueType& value) { - Key = key; - Value = value; + Memory::ConstructItems(&Key, &key, 1); + Memory::ConstructItems(&Value, &value, 1); _state = Occupied; } template void Occupy(const KeyComparableType& key, ValueType&& value) { - Key = key; - Value = MoveTemp(value); + Memory::ConstructItems(&Key, &key, 1); + Memory::MoveItems(&Value, &value, 1); _state = Occupied; } - public: - FORCE_INLINE bool IsEmpty() const { return _state == Empty; } - FORCE_INLINE bool IsNotEmpty() const - { - return _state != Empty; - } - FORCE_INLINE bool IsDeleted() const { return _state == Deleted; } - FORCE_INLINE bool IsNotDeleted() const - { - return _state != Deleted; - } - FORCE_INLINE bool IsOccupied() const { return _state == Occupied; @@ -182,7 +160,6 @@ public: /// /// Gets the amount of the elements in the collection. /// - /// The amount of elements in the collection. FORCE_INLINE int32 Count() const { return _elementsCount; @@ -191,7 +168,6 @@ public: /// /// Gets the amount of the elements that can be contained by the collection. /// - /// The capacity of the collection. FORCE_INLINE int32 Capacity() const { return _tableSize; @@ -200,7 +176,6 @@ public: /// /// Returns true if collection is empty. /// - /// True if is empty, otherwise false. FORCE_INLINE bool IsEmpty() const { return _elementsCount == 0; @@ -209,7 +184,6 @@ public: /// /// Returns true if collection has one or more elements. /// - /// True if isn't empty, otherwise false. FORCE_INLINE bool HasItems() const { return _elementsCount != 0; @@ -218,12 +192,11 @@ public: public: /// - /// The dictionary collection iterator. + /// The Dictionary collection iterator. /// struct Iterator { friend Dictionary; - private: Dictionary& _collection; @@ -254,7 +227,6 @@ public: /// /// Checks if iterator is in the end of the collection. /// - /// True if is in the end, otherwise false. FORCE_INLINE bool IsEnd() const { return _index == _collection._tableSize; @@ -263,7 +235,6 @@ public: /// /// Checks if iterator is not in the end of the collection. /// - /// True if is not in the end, otherwise false. FORCE_INLINE bool IsNotEnd() const { return _index != _collection._tableSize; @@ -286,7 +257,7 @@ public: return _index >= 0 && _index < _collection._tableSize; } - FORCE_INLINE bool operator !() const + FORCE_INLINE bool operator!() const { return !(bool)*this; } @@ -316,7 +287,7 @@ public: return *this; } - Iterator operator++(int) + Iterator operator++(int) const { Iterator i = *this; ++i; @@ -335,7 +306,7 @@ public: return *this; } - Iterator operator--(int) + Iterator operator--(int) const { Iterator i = *this; --i; @@ -428,7 +399,6 @@ public: if (pos.ObjectIndex == -1) return false; - result = _table[pos.ObjectIndex].Value; return true; } @@ -449,7 +419,6 @@ public: if (pos.ObjectIndex == -1) return nullptr; - return &_table[pos.ObjectIndex].Value; } @@ -463,11 +432,8 @@ public: if (_table) { // Free all buckets - // Note: this will not clear allocated objects space! for (int32 i = 0; i < _tableSize; i++) - { _table[i].Free(); - } _elementsCount = _deletedCount = 0; } } @@ -492,16 +458,14 @@ public: /// Enables preserving collection contents during resizing. void SetCapacity(int32 capacity, bool preserveContents = true) { - // Validate input - ASSERT(capacity >= 0); - // Check if capacity won't change if (capacity == Capacity()) return; // Cache previous state + ASSERT(capacity >= 0); Bucket* oldTable = _table; - int32 oldTableSize = _tableSize; + const int32 oldTableSize = _tableSize; // Clear elements counters const int32 oldElementsCount = _elementsCount; @@ -523,10 +487,10 @@ public: } // Allocate new table - _table = NewArray(capacity); + _table = (Bucket*)Allocator::Allocate(capacity * sizeof(Bucket)); _tableSize = capacity; - - // Check if preserve content + for (int32 i = 0; i < capacity; i++) + _table[i]._state = Bucket::Empty; if (oldElementsCount != 0 && preserveContents) { // Try to preserve all pairs in the collection @@ -548,7 +512,9 @@ public: // Delete old table if (oldTable) { - DeleteArray(oldTable, oldTableSize); + for (int32 i = 0; i < oldTableSize; i++) + oldTable[i].Free(); + Allocator::Free(oldTable); } } @@ -571,11 +537,10 @@ public: if (Capacity() >= minCapacity) return; - // TODO: improve this, better collection growing and shrinking on remove - int32 num = Capacity() == 0 ? DICTIONARY_DEFAULT_CAPACITY : Capacity() * 2; - if (num < minCapacity) - num = minCapacity; - SetCapacity(num); + int32 capacity = Capacity() == 0 ? DICTIONARY_DEFAULT_CAPACITY : Capacity() * 2; + if (capacity < minCapacity) + capacity = minCapacity; + SetCapacity(capacity); } /// @@ -676,7 +641,6 @@ public: _deletedCount++; return true; } - return false; } @@ -733,9 +697,7 @@ public: for (int32 i = 0; i < _tableSize; i++) { if (_table[i].IsOccupied() && _table[i].Key == key) - { return Iterator(*this, i); - } } } return End(); @@ -754,7 +716,6 @@ public: FindPositionResult pos; FindPosition(key, pos); - return pos.ObjectIndex != -1; } @@ -809,12 +770,9 @@ public: void Clone(const Dictionary& other) { Clear(); - SetCapacity(other.Capacity(), false); - for (auto i = other.Begin(); i != other.End(); ++i) Add(i); - ASSERT(Count() == other.Count()); ASSERT(Capacity() == other.Capacity()); } @@ -909,14 +867,13 @@ protected: void FindPosition(const KeyComparableType& key, FindPositionResult& result) const { ASSERT(_table); - const int32 tableSizeMinusOne = _tableSize - 1; int32 bucketIndex = GetHash(key) & tableSizeMinusOne; int32 insertPos = -1; - int32 numChecks = 0; + int32 checksCount = 0; result.FreeSlotIndex = -1; - while (numChecks < _tableSize) + while (checksCount < _tableSize) { // Empty bucket if (_table[bucketIndex].IsEmpty()) @@ -941,8 +898,8 @@ protected: return; } - numChecks++; - bucketIndex = (bucketIndex + DICTIONARY_PROB_FUNC(_tableSize, numChecks)) & tableSizeMinusOne; + checksCount++; + bucketIndex = (bucketIndex + DICTIONARY_PROB_FUNC(_tableSize, checksCount)) & tableSizeMinusOne; } result.ObjectIndex = -1; From a4763f630e9636da94c58201b86351562a69d827 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Jan 2021 14:28:02 +0100 Subject: [PATCH 119/419] Fix right-click context menu for Visject Nodes --- Source/Editor/Surface/SurfaceNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index cf39384ce..972f9e678 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -984,7 +984,7 @@ namespace FlaxEditor.Surface } // Secondary Context Menu - if (button == MouseButton.Right) + if (button == MouseButton.Right && false) { if (!IsSelected) Surface.Select(this); From c2f745397a957349aa2e3e053092a1f8ed5dbce3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Jan 2021 14:28:44 +0100 Subject: [PATCH 120/419] Fix ResizeAuto in SurfaceNode to include custom controls --- Source/Editor/Surface/SurfaceNode.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 972f9e678..8726359e9 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -185,9 +185,9 @@ namespace FlaxEditor.Surface var rightWidth = 40.0f; var boxLabelFont = Style.Current.FontSmall; var titleLabelFont = Style.Current.FontLarge; - for (int i = 0; i < Elements.Count; i++) + for (int i = 0; i < Children.Count; i++) { - if (Elements[i] is InputBox inputBox) + if (Children[i] is InputBox inputBox) { var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) @@ -195,15 +195,23 @@ namespace FlaxEditor.Surface leftWidth = Mathf.Max(leftWidth, boxWidth); leftHeight = Mathf.Max(leftHeight, inputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } - else if (Elements[i] is OutputBox outputBox) + else if (Children[i] is OutputBox outputBox) { rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } - else if (Elements[i] is Control control) + else if (Children[i] is Control control) { - width = Mathf.Max(width, control.Width + 10); - height = Mathf.Max(height, control.Height + 10); + if (control.AnchorPreset == AnchorPresets.TopLeft) + { + width = Mathf.Max(width, control.Right + 4 - Constants.NodeMarginX); + height = Mathf.Max(height, control.Bottom + 4 - Constants.NodeMarginY - Constants.NodeHeaderSize); + } + else + { + width = Mathf.Max(width, control.Width + 4); + height = Mathf.Max(height, control.Height + 4); + } } } width = Mathf.Max(width, leftWidth + rightWidth + 10); From 4042d466d3b8c1e15f53115ac6118477d3d11c58 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Jan 2021 14:40:53 +0100 Subject: [PATCH 121/419] Add support for Events in Scripting API reflection in Editor --- .../Editor/Content/Items/VisualScriptItem.cs | 6 +++++ Source/Editor/Scripting/ScriptType.cs | 24 +++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs index 86be2e3de..fd23887ec 100644 --- a/Source/Editor/Content/Items/VisualScriptItem.cs +++ b/Source/Editor/Content/Items/VisualScriptItem.cs @@ -50,6 +50,9 @@ namespace FlaxEditor.Content /// public bool IsMethod => false; + /// + public bool IsEvent => false; + /// public bool HasGet => true; @@ -174,6 +177,9 @@ namespace FlaxEditor.Content /// public bool IsMethod => true; + /// + public bool IsEvent => false; + /// public bool HasGet => false; diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index 038c1b745..7f49bdc75 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -138,6 +138,11 @@ namespace FlaxEditor.Scripting /// public bool IsMethod => _managed is MethodInfo || (_custom?.IsMethod ?? false); + /// + /// Gets a value indicating whether this member is an event. + /// + public bool IsEvent => _managed is EventInfo || (_custom?.IsEvent ?? false); + /// /// Gets a value indicating whether this member value can be gathered (via getter method or directly from the field). /// @@ -383,7 +388,7 @@ namespace FlaxEditor.Scripting } /// - /// Gets the method parameters metadata. + /// Gets the method parameters metadata (or event delegate signature parameters). /// public Parameter[] GetParameters() { @@ -416,6 +421,11 @@ namespace FlaxEditor.Scripting } return result; } + if (_managed is EventInfo eventInfo) + { + var invokeMethod = eventInfo.EventHandlerType.GetMethod("Invoke"); + return new ScriptMemberInfo(invokeMethod).GetParameters(); + } return _custom.GetParameters(); } @@ -634,6 +644,11 @@ namespace FlaxEditor.Scripting /// public static readonly ScriptType Null; + /// + /// A that is System.Void. + /// + public static readonly ScriptType Void = new ScriptType(typeof(void)); + /// /// Gets the type of the script as . /// @@ -1463,6 +1478,11 @@ namespace FlaxEditor.Scripting /// bool IsMethod { get; } + /// + /// Gets a value indicating whether this member is an event. + /// + bool IsEvent { get; } + /// /// Gets a value indicating whether this member value can be gathered (via getter method or directly from the field). /// @@ -1504,7 +1524,7 @@ namespace FlaxEditor.Scripting object[] GetAttributes(bool inherit); /// - /// Gets the method parameters metadata. + /// Gets the method parameters metadata (or event delegate signature parameters). /// ScriptMemberInfo.Parameter[] GetParameters(); From cbbfb166285b3ea932f6061209726c334728cd7d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Jan 2021 10:55:13 +0100 Subject: [PATCH 122/419] Add scripting API events in Visual Script --- Source/Editor/Scripting/ScriptType.cs | 8 +- Source/Editor/Surface/Archetypes/Function.cs | 307 +++++++++++++++++- Source/Editor/Surface/SurfaceNode.cs | 9 +- Source/Editor/Surface/SurfaceUtils.cs | 5 + .../Editor/Surface/VisjectSurfaceContext.cs | 9 +- Source/Editor/Surface/VisualScriptSurface.cs | 65 +++- Source/Engine/Content/Assets/VisualScript.cpp | 211 +++++++++++- Source/Engine/Content/Assets/VisualScript.h | 23 +- Source/Engine/Scripting/BinaryModule.cpp | 4 + Source/Engine/Scripting/Events.h | 36 ++ .../Bindings/BindingsGenerator.Cpp.cs | 69 +++- 11 files changed, 708 insertions(+), 38 deletions(-) create mode 100644 Source/Engine/Scripting/Events.h diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index 7f49bdc75..53c84a1b5 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -53,6 +53,8 @@ namespace FlaxEditor.Scripting return fieldInfo.IsPublic; if (_managed is PropertyInfo propertyInfo) return (propertyInfo.GetMethod == null || propertyInfo.GetMethod.IsPublic) && (propertyInfo.SetMethod == null || propertyInfo.SetMethod.IsPublic); + if (_managed is EventInfo eventInfo) + return eventInfo.GetAddMethod().IsPublic; if (_custom != null) return _custom.IsPublic; return false; @@ -72,6 +74,8 @@ namespace FlaxEditor.Scripting return fieldInfo.IsStatic; if (_managed is PropertyInfo propertyInfo) return (propertyInfo.GetMethod == null || propertyInfo.GetMethod.IsStatic) && (propertyInfo.SetMethod == null || propertyInfo.SetMethod.IsStatic); + if (_managed is EventInfo eventInfo) + return eventInfo.GetAddMethod().IsStatic; if (_custom != null) return _custom.IsStatic; return false; @@ -178,7 +182,7 @@ namespace FlaxEditor.Scripting } /// - /// Gets the method parameters count (valid for methods only). + /// Gets the method parameters count (valid for methods and events only). /// public int ParametersCount { @@ -186,6 +190,8 @@ namespace FlaxEditor.Scripting { if (_managed is MethodInfo methodInfo) return methodInfo.GetParameters().Length; + if (_managed is EventInfo eventInfo) + return eventInfo.EventHandlerType.GetMethod("Invoke").GetParameters().Length; if (_custom != null) return _custom.ParametersCount; return 0; diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index b7584a2b8..340d234ab 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -1180,6 +1181,12 @@ namespace FlaxEditor.Surface.Archetypes [TypeReference(typeof(object), nameof(IsTypeValid))] public ScriptType Type; + public Parameter(ref ScriptMemberInfo.Parameter param) + { + Name = param.Name; + Type = param.Type; + } + private static bool IsTypeValid(ScriptType type) { return SurfaceUtils.IsValidVisualScriptFunctionType(type) && !type.IsVoid; @@ -1654,6 +1661,7 @@ namespace FlaxEditor.Surface.Archetypes base.OnSpawned(); // Setup initial signature + var defaultSignature = _signature.Node == null; CheckFunctionName(ref _signature.Name); if (_signature.ReturnType == ScriptType.Null) _signature.ReturnType = new ScriptType(typeof(void)); @@ -1661,8 +1669,11 @@ namespace FlaxEditor.Surface.Archetypes SaveSignature(); UpdateUI(); - // Start editing - OnEditSignature(); + if (defaultSignature) + { + // Start editing + OnEditSignature(); + } // Send event for (int i = 0; i < Surface.Nodes.Count; i++) @@ -1890,6 +1901,254 @@ namespace FlaxEditor.Surface.Archetypes } } + private abstract class EventBaseNode : SurfaceNode, IFunctionsDependantNode + { + private ComboBoxElement _combobox; + private Image _helperButton; + private bool _isBind; + private bool _isUpdateLocked = true; + private List _tooltips = new List(); + private List _functionNodesIds = new List(); + private ScriptMemberInfo.Parameter[] _signature; + + protected EventBaseNode(bool isBind, uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(id, context, nodeArch, groupArch) + { + _isBind = isBind; + } + + private bool IsValidFunctionSignature(ref VisualScriptFunctionNode.Signature sig) + { + if (!sig.ReturnType.IsVoid || sig.Parameters == null || sig.Parameters.Length != _signature.Length) + return false; + for (int i = 0; i < _signature.Length; i++) + { + if (_signature[i].Type != sig.Parameters[i].Type) + return false; + } + return true; + } + + private void UpdateUI() + { + if (_isUpdateLocked) + return; + _isUpdateLocked = true; + if (_combobox == null) + { + _combobox = (ComboBoxElement)_children[4]; + _combobox.TooltipText = _isBind ? "Select the function to call when the event occurs" : "Select the function to unbind from the event"; + _combobox.SelectedIndexChanged += OnSelectedChanged; + _helperButton = new Image + { + Location = _combobox.UpperRight + new Vector2(4, 3), + Size = new Vector2(12.0f), + Parent = this, + }; + _helperButton.Clicked += OnHelperButtonClicked; + } + int toSelect = -1; + var handlerFunctionNodeId = Convert.ToUInt32(Values[2]); + _combobox.ClearItems(); + _tooltips.Clear(); + _functionNodesIds.Clear(); + var nodes = Surface.Nodes; + var count = _signature != null ? nodes.Count : 0; + for (int i = 0; i < count; i++) + { + if (nodes[i] is VisualScriptFunctionNode functionNode) + { + // Get if function signature matches the event signature + functionNode.GetSignature(out var functionSig); + if (IsValidFunctionSignature(ref functionSig)) + { + if (functionNode.ID == handlerFunctionNodeId) + toSelect = _functionNodesIds.Count; + _functionNodesIds.Add(functionNode.ID); + _tooltips.Add(functionNode.TooltipText); + _combobox.AddItem(functionSig.ToString()); + } + } + } + _combobox.Tooltips = _tooltips.Count != 0 ? _tooltips.ToArray() : null; + _combobox.Enabled = _tooltips.Count != 0; + _combobox.SelectedIndex = toSelect; + if (toSelect != -1) + { + _helperButton.Brush = new SpriteBrush(Editor.Instance.Icons.Search12); + _helperButton.Color = Color.White; + _helperButton.TooltipText = "Navigate to the handler function"; + } + else if (_isBind) + { + _helperButton.Brush = new SpriteBrush(Editor.Instance.Icons.Add48); + _helperButton.Color = Color.Red; + _helperButton.TooltipText = "Add new handler function and bind it to this event"; + _helperButton.Enabled = _signature != null; + } + else + { + _helperButton.Enabled = false; + } + ResizeAuto(); + _isUpdateLocked = false; + } + + private void OnHelperButtonClicked(Image img, MouseButton mouseButton) + { + if (mouseButton != MouseButton.Left) + return; + if (_combobox.SelectedIndex != -1) + { + // Focus selected function + var handlerFunctionNodeId = Convert.ToUInt32(Values[2]); + var handlerFunctionNode = Surface.FindNode(handlerFunctionNodeId); + Surface.FocusNode(handlerFunctionNode); + } + else if (_isBind) + { + // Create new function that matches the event signature + var surfaceBounds = Surface.AllNodesBounds; + Surface.ShowArea(new Rectangle(surfaceBounds.BottomLeft, new Vector2(200, 150)).MakeExpanded(400.0f)); + var node = Surface.Context.SpawnNode(16, 6, surfaceBounds.BottomLeft + new Vector2(0, 50), null, OnBeforeSpawnedNewHandler); + Surface.Select(node); + + // Bind this function + SetValue(2, node.ID); + } + } + + private void OnBeforeSpawnedNewHandler(SurfaceNode node) + { + // Initialize signature to match the event + var functionNode = (VisualScriptFunctionNode)node; + functionNode._signature = new VisualScriptFunctionNode.Signature + { + Name = "On" + (string)Values[1], + IsStatic = false, + IsVirtual = false, + Node = functionNode, + ReturnType = ScriptType.Void, + Parameters = new VisualScriptFunctionNode.Parameter[_signature.Length], + }; + for (int i = 0; i < _signature.Length; i++) + functionNode._signature.Parameters[i] = new VisualScriptFunctionNode.Parameter(ref _signature[i]); + } + + private void OnSelectedChanged(ComboBox cb) + { + if (_isUpdateLocked) + return; + var handlerFunctionNodeId = Convert.ToUInt32(Values[2]); + var selectedID = cb.SelectedIndex != -1 ? _functionNodesIds[cb.SelectedIndex] : 0u; + if (selectedID != handlerFunctionNodeId) + { + SetValue(2, selectedID); + UpdateUI(); + } + } + + public void OnFunctionCreated(SurfaceNode node) + { + UpdateUI(); + } + + public void OnFunctionEdited(SurfaceNode node) + { + UpdateUI(); + } + + public void OnFunctionDeleted(SurfaceNode node) + { + // Deselect if that function was selected + var handlerFunctionNodeId = Convert.ToUInt32(Values[2]); + if (node.ID == handlerFunctionNodeId) + _combobox.SelectedIndex = -1; + + UpdateUI(); + } + + public override void OnSurfaceLoaded() + { + base.OnSurfaceLoaded(); + + // Find reflection information about event + _signature = null; + var isStatic = false; + var eventName = (string)Values[1]; + var eventType = TypeUtils.GetType((string)Values[0]); + var member = eventType.GetMember(eventName, MemberTypes.Event, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); + if (member && SurfaceUtils.IsValidVisualScriptEvent(member)) + { + isStatic = member.IsStatic; + _signature = member.GetParameters(); + TooltipText = SurfaceUtils.GetVisualScriptMemberInfoDescription(member); + } + + // Setup instance box (static events don't need it) + var instanceBox = GetBox(1); + instanceBox.Visible = !isStatic; + if (isStatic) + instanceBox.RemoveConnections(); + else + instanceBox.CurrentType = eventType; + + _isUpdateLocked = false; + UpdateUI(); + } + + public override void OnValuesChanged() + { + base.OnValuesChanged(); + + UpdateUI(); + } + + /// + public override void OnDestroy() + { + _combobox = null; + _helperButton = null; + _tooltips.Clear(); + _tooltips = null; + _functionNodesIds.Clear(); + _functionNodesIds = null; + _signature = null; + + base.OnDestroy(); + } + } + + private sealed class BindEventNode : EventBaseNode + { + public BindEventNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(true, id, context, nodeArch, groupArch) + { + } + + public override void OnSurfaceLoaded() + { + Title = "Bind " + (string)Values[1]; + + base.OnSurfaceLoaded(); + } + } + + private sealed class UnbindEventNode : EventBaseNode + { + public UnbindEventNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(false, id, context, nodeArch, groupArch) + { + } + + public override void OnSurfaceLoaded() + { + Title = "Unbind " + (string)Values[1]; + + base.OnSurfaceLoaded(); + } + } + /// /// The nodes for that group. /// @@ -2031,6 +2290,50 @@ namespace FlaxEditor.Surface.Archetypes null, // Default value }, }, + new NodeArchetype + { + TypeID = 9, + Create = (id, context, arch, groupArch) => new BindEventNode(id, context, arch, groupArch), + Title = string.Empty, + Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, + Size = new Vector2(260, 60), + DefaultValues = new object[] + { + string.Empty, // Event type + string.Empty, // Event name + (uint)0, // Handler function nodeId + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), + NodeElementArchetype.Factory.Input(2, "Instance", true, typeof(object), 1), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 2, true), + NodeElementArchetype.Factory.Text(2, 20, "Handler function:"), + NodeElementArchetype.Factory.ComboBox(100, 20, 140), + } + }, + new NodeArchetype + { + TypeID = 10, + Create = (id, context, arch, groupArch) => new UnbindEventNode(id, context, arch, groupArch), + Title = string.Empty, + Flags = NodeFlags.VisualScriptGraph | NodeFlags.NoSpawnViaGUI, + Size = new Vector2(260, 60), + DefaultValues = new object[] + { + string.Empty, // Event type + string.Empty, // Event name + (uint)0, // Handler function nodeId + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), + NodeElementArchetype.Factory.Input(2, "Instance", true, typeof(object), 1), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 2, true), + NodeElementArchetype.Factory.Text(2, 20, "Handler function:"), + NodeElementArchetype.Factory.ComboBox(100, 20, 140), + } + }, }; } } diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 8726359e9..ae094c2be 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -187,7 +187,10 @@ namespace FlaxEditor.Surface var titleLabelFont = Style.Current.FontLarge; for (int i = 0; i < Children.Count; i++) { - if (Children[i] is InputBox inputBox) + var child = Children[i]; + if (!child.Visible) + continue; + if (child is InputBox inputBox) { var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) @@ -195,12 +198,12 @@ namespace FlaxEditor.Surface leftWidth = Mathf.Max(leftWidth, boxWidth); leftHeight = Mathf.Max(leftHeight, inputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } - else if (Children[i] is OutputBox outputBox) + else if (child is OutputBox outputBox) { rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } - else if (Children[i] is Control control) + else if (child is Control control) { if (control.AnchorPreset == AnchorPresets.TopLeft) { diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index 18f2e9693..74f1d6130 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -406,6 +406,11 @@ namespace FlaxEditor.Surface return member.IsField && IsValidVisualScriptType(member.ValueType); } + internal static bool IsValidVisualScriptEvent(ScriptMemberInfo member) + { + return member.IsEvent && member.HasAttribute(typeof(UnmanagedAttribute)); + } + internal static bool IsValidVisualScriptType(ScriptType scriptType) { if (scriptType.IsGenericType || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true)) diff --git a/Source/Editor/Surface/VisjectSurfaceContext.cs b/Source/Editor/Surface/VisjectSurfaceContext.cs index d436cd91e..81b72bf4e 100644 --- a/Source/Editor/Surface/VisjectSurfaceContext.cs +++ b/Source/Editor/Surface/VisjectSurfaceContext.cs @@ -331,12 +331,13 @@ namespace FlaxEditor.Surface /// The node archetype ID. /// The location. /// The custom values array. Must match node archetype size. Pass null to use default values. + /// The custom callback action to call after node creation but just before invoking spawn event. Can be used to initialize custom node data. /// Created node. - public SurfaceNode SpawnNode(ushort groupID, ushort typeID, Vector2 location, object[] customValues = null) + public SurfaceNode SpawnNode(ushort groupID, ushort typeID, Vector2 location, object[] customValues = null, Action beforeSpawned = null) { if (NodeFactory.GetArchetype(_surface.NodeArchetypes, groupID, typeID, out var groupArchetype, out var nodeArchetype)) { - return SpawnNode(groupArchetype, nodeArchetype, location, customValues); + return SpawnNode(groupArchetype, nodeArchetype, location, customValues, beforeSpawned); } return null; } @@ -348,8 +349,9 @@ namespace FlaxEditor.Surface /// The node archetype. /// The location. /// The custom values array. Must match node archetype size. Pass null to use default values. + /// The custom callback action to call after node creation but just before invoking spawn event. Can be used to initialize custom node data. /// Created node. - public SurfaceNode SpawnNode(GroupArchetype groupArchetype, NodeArchetype nodeArchetype, Vector2 location, object[] customValues = null) + public SurfaceNode SpawnNode(GroupArchetype groupArchetype, NodeArchetype nodeArchetype, Vector2 location, object[] customValues = null, Action beforeSpawned = null) { if (groupArchetype == null || nodeArchetype == null) throw new ArgumentNullException(); @@ -387,6 +389,7 @@ namespace FlaxEditor.Surface } node.Location = location; OnControlLoaded(node); + beforeSpawned?.Invoke(node); node.OnSurfaceLoaded(); OnControlSpawned(node); diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index ccabc8f2f..335860c52 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -2,6 +2,11 @@ //#define DEBUG_INVOKE_METHODS_SEARCHING //#define DEBUG_FIELDS_SEARCHING +//#define DEBUG_EVENTS_SEARCHING + +#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING || DEBUG_EVENTS_SEARCHING +#define DEBUG_SEARCH_TIME +#endif using System; using System.Collections.Generic; @@ -103,7 +108,7 @@ namespace FlaxEditor.Surface private static void OnActiveContextMenuShowAsync() { Profiler.BeginEvent("Setup Visual Script Context Menu (async)"); -#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING +#if DEBUG_SEARCH_TIME var searchStartTime = DateTime.Now; var searchHitsCount = 0; #endif @@ -338,13 +343,65 @@ namespace FlaxEditor.Surface } } } + else if (member.IsEvent) + { + var name = member.Name; + + // Skip if searching by name doesn't return a match + var members = scriptType.GetMembers(name, MemberTypes.Event, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); + if (!members.Contains(member)) + continue; + + // Check if field is valid for Visual Script usage + if (SurfaceUtils.IsValidVisualScriptEvent(member)) + { + var groupKey = new KeyValuePair(scriptTypeName, 16); + if (!_cache.TryGetValue(groupKey, out var group)) + { + group = new GroupArchetype + { + GroupID = groupKey.Value, + Name = groupKey.Key, + Color = new Color(109, 160, 24), + Tag = _version, + Archetypes = new List(), + }; + _cache.Add(groupKey, group); + } + + // Add Bind event node + var bindNode = (NodeArchetype)Archetypes.Function.Nodes[8].Clone(); + bindNode.DefaultValues[0] = scriptTypeTypeName; + bindNode.DefaultValues[1] = name; + bindNode.Flags &= ~NodeFlags.NoSpawnViaGUI; + bindNode.Title = "Bind " + name; + bindNode.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member); + bindNode.SubTitle = string.Format(" (in {0})", scriptTypeName); + ((IList)group.Archetypes).Add(bindNode); + + // Add Unbind event node + var unbindNode = (NodeArchetype)Archetypes.Function.Nodes[9].Clone(); + unbindNode.DefaultValues[0] = scriptTypeTypeName; + unbindNode.DefaultValues[1] = name; + unbindNode.Flags &= ~NodeFlags.NoSpawnViaGUI; + unbindNode.Title = "Unbind " + name; + unbindNode.Description = bindNode.Description; + unbindNode.SubTitle = bindNode.SubTitle; + ((IList)group.Archetypes).Add(unbindNode); + +#if DEBUG_EVENTS_SEARCHING + Editor.LogWarning(scriptTypeTypeName + " -> " + member.GetSignature()); + searchHitsCount++; +#endif + } + } } } // Add group to context menu (on a main thread) FlaxEngine.Scripting.InvokeOnUpdate(() => { -#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING +#if DEBUG_SEARCH_TIME var addStartTime = DateTime.Now; #endif lock (_locker) @@ -352,12 +409,12 @@ namespace FlaxEditor.Surface _taskContextMenu.AddGroups(_cache.Values); _taskContextMenu = null; } -#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING +#if DEBUG_SEARCH_TIME Editor.LogError($"Added items to VisjectCM in: {(DateTime.Now - addStartTime).TotalMilliseconds} ms"); #endif }); -#if DEBUG_INVOKE_METHODS_SEARCHING || DEBUG_FIELDS_SEARCHING +#if DEBUG_SEARCH_TIME Editor.LogError($"Collected {searchHitsCount} items in: {(DateTime.Now - searchStartTime).TotalMilliseconds} ms"); #endif Profiler.EndEvent(); diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp index 729ee0a5e..da1b97687 100644 --- a/Source/Engine/Content/Assets/VisualScript.cpp +++ b/Source/Engine/Content/Assets/VisualScript.cpp @@ -6,6 +6,7 @@ #include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Scripting/MException.h" #include "Engine/Scripting/Scripting.h" +#include "Engine/Scripting/Events.h" #include "Engine/Scripting/ManagedCLR/MClass.h" #include "Engine/Scripting/ManagedCLR/MMethod.h" #include "Engine/Scripting/ManagedCLR/MField.h" @@ -347,7 +348,7 @@ void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& v const auto instanceParams = stack.Stack->Script->_instances.Find(stack.Stack->Instance->GetID()); if (param && instanceParams) { - value = instanceParams->Value[paramIndex]; + value = instanceParams->Value.Params[paramIndex]; } else { @@ -371,7 +372,7 @@ void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& v const auto instanceParams = stack.Stack->Script->_instances.Find(stack.Stack->Instance->GetID()); if (param && instanceParams) { - instanceParams->Value[paramIndex] = tryGetValue(node->GetBox(1), 1, Value::Zero); + instanceParams->Value.Params[paramIndex] = tryGetValue(node->GetBox(1), 1, Value::Zero); } else { @@ -1004,6 +1005,125 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& if (returnedImpulse && returnedImpulse->HasConnection()) eatBox(node, returnedImpulse->FirstConnection()); break; + } + // Bind/Unbind + case 9: + case 10: + { + const bool bind = node->TypeID == 9; + auto& stack = ThreadStacks.Get(); + if (!stack.Stack->Instance) + { + // TODO: add support for binding to events in static Visual Script + LOG(Error, "Cannot bind to event in static Visual Script."); + PrintStack(LogType::Error); + break; + } + const auto object = stack.Stack->Instance; + + // Find method to bind + VisualScriptGraphNode* methodNode = nullptr; + const auto graph = stack.Stack && stack.Stack->Script ? &stack.Stack->Script->Graph : nullptr; + if (graph) + methodNode = graph->GetNode((uint32)node->Values[2]); + if (!methodNode) + { + LOG(Error, "Missing function handler to bind to the event."); + PrintStack(LogType::Error); + break; + } + VisualScript::Method* method = nullptr; + for (auto& m : stack.Stack->Script->_methods) + { + if (m.Node == methodNode) + { + method = &m; + break; + } + } + if (!method) + { + LOG(Error, "Missing method to bind to the event."); + PrintStack(LogType::Error); + break; + } + + // Find event + const StringView eventTypeName(node->Values[0]); + const StringView eventName(node->Values[1]); + const StringAsANSI<100> eventTypeNameAnsi(eventTypeName.Get(), eventTypeName.Length()); + const ScriptingTypeHandle eventType = Scripting::FindScriptingType(StringAnsiView(eventTypeNameAnsi.Get(), eventTypeName.Length())); + + // Find event binding callback + auto eventBinder = ScriptingEvents::EventsTable.TryGet(Pair(eventType, eventName)); + if (!eventBinder) + { + LOG(Error, "Cannot bind to missing event {0} from type {1}.", eventName, eventTypeName); + PrintStack(LogType::Error); + break; + } + + // Evaluate object instance + const auto box = node->GetBox(1); + Variant instance; + if (box->HasConnection()) + instance = eatBox(node, box->FirstConnection()); + else + instance.SetObject(object); + if (!instance.AsObject) + { + LOG(Error, "Cannot bind event to null object."); + PrintStack(LogType::Error); + break; + } + // TODO: check if instance is of event type (including inheritance) + + // Add Visual Script method to the event bindings table + const auto& type = object->GetType(); + Guid id; + if (Guid::Parse(type.Fullname, id)) + break; + if (const auto visualScript = (VisualScript*)Content::GetAsset(id)) + { + if (auto i = visualScript->GetScriptInstance(object)) + { + VisualScript::EventBinding* eventBinding = nullptr; + for (auto& b : i->EventBindings) + { + if (b.Type == eventType && b.Name == eventName) + { + eventBinding = &b; + break; + } + } + if (bind) + { + // Bind to the event + if (!eventBinding) + { + eventBinding = &i->EventBindings.AddOne(); + eventBinding->Type = eventType; + eventBinding->Name = eventName; + } + eventBinding->BindedMethods.Add(method); + if (eventBinding->BindedMethods.Count() == 1) + (*eventBinder)(instance.AsObject, object, true); + } + else if (eventBinding) + { + // Unbind from the event + if (eventBinding->BindedMethods.Count() == 1) + (*eventBinder)(instance.AsObject, object, false); + eventBinding->BindedMethods.Remove(method); + } + } + } + + // Call graph further + const auto returnedImpulse = &node->Boxes[2]; + if (returnedImpulse && returnedImpulse->HasConnection()) + eatBox(node, returnedImpulse->FirstConnection()); + break; } default: break; @@ -1304,7 +1424,7 @@ Asset::LoadResult VisualScript::load() // Update instanced data from previous format to the current graph parameters scheme for (auto& e : _instances) { - auto& instanceParams = e.Value; + auto& instanceParams = e.Value.Params; Array valuesCache(MoveTemp(instanceParams)); instanceParams.Resize(count); for (int32 i = 0; i < count; i++) @@ -1319,7 +1439,7 @@ Asset::LoadResult VisualScript::load() // Reset instances values to defaults for (auto& e : _instances) { - auto& instanceParams = e.Value; + auto& instanceParams = e.Value.Params; instanceParams.Resize(count); for (int32 i = 0; i < count; i++) instanceParams[i] = Graph.Parameters[i].Value; @@ -1421,13 +1541,17 @@ void VisualScript::CacheScriptingType() _scriptingTypeHandle = ScriptingTypeHandle(&binaryModule, typeIndex); binaryModule.Scripts.Add(this); -#if USE_EDITOR - // When first Visual Script gets loaded register for other modules unload to clear runtime execution cache + // Special initialization when the first Visual Script gets loaded if (typeIndex == 0) { +#if USE_EDITOR + // Register for other modules unload to clear runtime execution cache Scripting::ScriptsReloading.Bind(&binaryModule); - } #endif + + // Register for scripting events + ScriptingEvents::Event.Bind(VisualScriptingBinaryModule::OnEvent); + } } auto& type = _scriptingTypeHandle.Module->Types[_scriptingTypeHandle.TypeIndex]; type.ManagedClass = baseType.GetType().ManagedClass; @@ -1550,7 +1674,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex]; // Initialize instance data - auto& instanceParams = visualScript->_instances[object->GetID()]; + auto& instanceParams = visualScript->_instances[object->GetID()].Params; instanceParams.Resize(visualScript->Graph.Parameters.Count()); for (int32 i = 0; i < instanceParams.Count(); i++) instanceParams[i] = visualScript->Graph.Parameters[i].Value; @@ -1608,6 +1732,56 @@ void VisualScriptingBinaryModule::OnScriptsReloading() #endif +void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span& parameters, const ScriptingTypeHandle& eventType, const StringView& eventName) +{ + if (object) + { + // Object event + const auto& type = object->GetType(); + Guid id; + if (Guid::Parse(type.Fullname, id)) + return; + if (const auto visualScript = (VisualScript*)Content::GetAsset(id)) + { + if (auto instance = visualScript->GetScriptInstance(object)) + { + for (auto& b : instance->EventBindings) + { + if (b.Type != eventType || b.Name != eventName) + continue; + for (auto& m : b.BindedMethods) + { + VisualScripting::Invoke(m, object, parameters); + } + } + } + } + } + else + { + // Static event + for (auto& asset : Content::GetAssetsRaw()) + { + if (const auto visualScript = ScriptingObject::Cast(asset.Value)) + { + for (auto& e : visualScript->_instances) + { + auto instance = &e.Value; + for (auto& b : instance->EventBindings) + { + if (b.Type != eventType || b.Name != eventName) + continue; + for (auto& m : b.BindedMethods) + { + VisualScripting::Invoke(m, object, parameters); + } + } + } + } + } + } +} + const StringAnsi& VisualScriptingBinaryModule::GetName() const { return _name; @@ -1702,7 +1876,7 @@ bool VisualScriptingBinaryModule::GetFieldValue(void* field, const Variant& inst LOG(Error, "Missing parameters for the object instance."); return true; } - result = instanceParams->Value[vsFiled->Index]; + result = instanceParams->Value.Params[vsFiled->Index]; return false; } @@ -1721,7 +1895,7 @@ bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& inst LOG(Error, "Missing parameters for the object instance."); return true; } - instanceParams->Value[vsFiled->Index] = value; + instanceParams->Value.Params[vsFiled->Index] = value; return false; } @@ -1735,7 +1909,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO const auto instanceParams = asset->_instances.Find(object->GetID()); if (instanceParams) { - auto& params = instanceParams->Value; + auto& params = instanceParams->Value.Params; if (otherObj) { // Serialize parameters diff @@ -1746,7 +1920,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO { auto& param = asset->Graph.Parameters[paramIndex]; auto& value = params[paramIndex]; - auto& otherValue = otherParams->Value[paramIndex]; + auto& otherValue = otherParams->Value.Params[paramIndex]; if (value != otherValue) { param.Identifier.ToString(idName, Guid::FormatType::N); @@ -1798,7 +1972,7 @@ void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeSt if (instanceParams) { // Deserialize all parameters - auto& params = instanceParams->Value; + auto& params = instanceParams->Value.Params; for (auto i = stream.MemberBegin(); i != stream.MemberEnd(); ++i) { StringAnsiView idNameAnsi(i->name.GetString(), i->name.GetStringLength()); @@ -1865,6 +2039,11 @@ ScriptingObject* VisualScript::CreateInstance() return scriptingTypeHandle ? scriptingTypeHandle.GetType().Script.Spawn(ScriptingObjectSpawnParams(Guid::New(), scriptingTypeHandle)) : nullptr; } +VisualScript::Instance* VisualScript::GetScriptInstance(ScriptingObject* instance) const +{ + return instance ? _instances.TryGet(instance->GetID()) : nullptr; +} + Variant VisualScript::GetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance) const { CHECK_RETURN(instance, Variant()); @@ -1874,7 +2053,7 @@ Variant VisualScript::GetScriptInstanceParameterValue(const StringView& name, Sc { const auto instanceParams = _instances.Find(instance->GetID()); if (instanceParams) - return instanceParams->Value[paramIndex]; + return instanceParams->Value.Params[paramIndex]; LOG(Error, "Failed to access Visual Script parameter {1} for {0}.", instance->ToString(), name); return Graph.Parameters[paramIndex].Value; } @@ -1893,7 +2072,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip const auto instanceParams = _instances.Find(instance->GetID()); if (instanceParams) { - instanceParams->Value[paramIndex] = value; + instanceParams->Value.Params[paramIndex] = value; return; } LOG(Error, "Failed to access Visual Script parameter {1} for {0}.", instance->ToString(), name); @@ -1913,7 +2092,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip const auto instanceParams = _instances.Find(instance->GetID()); if (instanceParams) { - instanceParams->Value[paramIndex] = MoveTemp(value); + instanceParams->Value.Params[paramIndex] = MoveTemp(value); return; } } diff --git a/Source/Engine/Content/Assets/VisualScript.h b/Source/Engine/Content/Assets/VisualScript.h index 0a7f34b1c..cf8dccce3 100644 --- a/Source/Engine/Content/Assets/VisualScript.h +++ b/Source/Engine/Content/Assets/VisualScript.h @@ -125,9 +125,22 @@ public: StringAnsi Name; }; + struct EventBinding + { + ScriptingTypeHandle Type; + String Name; + Array> BindedMethods; + }; + + struct Instance + { + Array Params; + Array EventBindings; + }; + private: - Dictionary> _instances; + Dictionary _instances; ScriptingTypeHandle _scriptingTypeHandle; ScriptingTypeHandle _scriptingTypeHandleCached; StringAnsiView _typename; @@ -179,6 +192,13 @@ public: /// The created instance or null if failed. API_FUNCTION() ScriptingObject* CreateInstance(); + /// + /// Gets the Visual Script instance data. + /// + /// The object instance. + /// The data or invalid instance (not VS or missing). + Instance* GetScriptInstance(ScriptingObject* instance) const; + /// /// Gets the value of the Visual Script parameter of the given instance. /// @@ -307,6 +327,7 @@ private: #if USE_EDITOR void OnScriptsReloading(); #endif + static void OnEvent(ScriptingObject* object, Span& parameters, const ScriptingTypeHandle& eventType, const StringView& eventName); public: diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index c802caff2..689b552f8 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -14,6 +14,10 @@ #include "MException.h" #include "Scripting.h" #include "StdTypesContainer.h" +#include "Events.h" + +Dictionary, void(*)(ScriptingObject*, void*, bool)> ScriptingEvents::EventsTable; +Delegate&, const ScriptingTypeHandle&, const StringView&> ScriptingEvents::Event; ManagedBinaryModule* GetBinaryModuleCorlib() { diff --git a/Source/Engine/Scripting/Events.h b/Source/Engine/Scripting/Events.h new file mode 100644 index 000000000..11f1643ee --- /dev/null +++ b/Source/Engine/Scripting/Events.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "ScriptingType.h" +#include "Engine/Core/Delegate.h" +#include "Engine/Core/Types/Span.h" +#include "Engine/Core/Types/Pair.h" +#include "Engine/Core/Types/Variant.h" +#include "Engine/Core/Types/StringView.h" +#include "Engine/Core/Collections/Dictionary.h" + +/// +/// The helper utility for binding and invoking scripting events (eg. used by Visual Scripting). +/// +class FLAXENGINE_API ScriptingEvents +{ +public: + + /// + /// Global table for registered even binder methods (key is pair of type and event name, value is method that takes instance with event, object to bind and flag to bind or unbind). + /// + /// + /// Key: pair of event type name (full), event name. + /// Value: event binder function with parameters: event caller instance (null for static events), object to bind, true to bind/false to unbind. + /// + static Dictionary, void(*)(ScriptingObject*, void*, bool)> EventsTable; + + /// + /// The action called when any scripting event occurs. Can be used to invoke scripting code that binded to this particular event. + /// + /// + /// Delegate parameters: event caller instance (null for static events), event invocation parameters list, event type name (full), event name. + /// + static Delegate&, const ScriptingTypeHandle&, const StringView&> Event; +}; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 6cdc5462b..11fe6a414 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1091,19 +1091,19 @@ namespace Flax.Build.Bindings { if (!useScripting) continue; - CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h"); + var paramsCount = eventInfo.Type.GenericArgs.Count; // C# event invoking wrapper (calls C# event from C++ delegate) + CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h"); contents.Append(" "); if (eventInfo.IsStatic) contents.Append("static "); contents.AppendFormat("void {0}_ManagedWrapper(", eventInfo.Name); - for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++) + for (var i = 0; i < paramsCount; i++) { if (i != 0) contents.Append(", "); - contents.Append(eventInfo.Type.GenericArgs[i]); - contents.Append(" arg" + i); + contents.Append(eventInfo.Type.GenericArgs[i]).Append(" arg" + i); } contents.Append(')').AppendLine(); contents.Append(" {").AppendLine(); @@ -1112,11 +1112,11 @@ namespace Flax.Build.Bindings contents.AppendFormat(" mmethod = {1}::GetStaticClass()->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, eventInfo.Type.GenericArgs.Count).AppendLine(); contents.Append(" CHECK(mmethod);").AppendLine(); contents.Append(" MonoObject* exception = nullptr;").AppendLine(); - if (eventInfo.Type.GenericArgs.Count == 0) + if (paramsCount == 0) contents.AppendLine(" void** params = nullptr;"); else - contents.AppendLine($" void* params[{eventInfo.Type.GenericArgs.Count}];"); - for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++) + contents.AppendLine($" void* params[{paramsCount}];"); + for (var i = 0; i < paramsCount; i++) { var paramType = eventInfo.Type.GenericArgs[i]; var paramName = "arg" + i; @@ -1139,7 +1139,7 @@ namespace Flax.Build.Bindings contents.Append("bool bind)").AppendLine(); contents.Append(" {").AppendLine(); contents.Append(" Function(params, {paramsCount}), {classTypeNameNative}::TypeInitializer, StringView(TEXT(\"{eventInfo.Name}\"), {eventInfo.Name.Length}));"); + contents.Append(" }").AppendLine().AppendLine(); + + // Scripting event wrapper binding method (binds/unbinds generic wrapper to C++ delegate) + contents.AppendFormat(" static void {0}_Bind(", eventInfo.Name); + contents.AppendFormat("{0}* obj, void* instance, bool bind)", classTypeNameNative).AppendLine(); + contents.Append(" {").AppendLine(); + contents.Append(" Function f;").AppendLine(); + if (eventInfo.IsStatic) + contents.AppendFormat(" f.Bind<{0}_Wrapper>();", eventInfo.Name).AppendLine(); + else + contents.AppendFormat(" f.Bind<{1}Internal, &{1}Internal::{0}_Wrapper>(({1}Internal*)instance);", eventInfo.Name, classTypeNameInternal).AppendLine(); + contents.Append(" if (bind)").AppendLine(); + contents.AppendFormat(" {0}{1}.Bind(f);", bindPrefix, eventInfo.Name).AppendLine(); + contents.Append(" else").AppendLine(); + contents.AppendFormat(" {0}{1}.Unbind(f);", bindPrefix, eventInfo.Name).AppendLine(); + contents.Append(" }").AppendLine().AppendLine(); } // Fields @@ -1299,6 +1349,9 @@ namespace Flax.Build.Bindings foreach (var eventInfo in classInfo.Events) { contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);"); + + // Register scripting event binder + contents.AppendLine($" ScriptingEvents::EventsTable[Pair({classTypeNameNative}::TypeInitializer, StringView(TEXT(\"{eventInfo.Name}\"), {eventInfo.Name.Length}))] = (void(*)(ScriptingObject*, void*, bool)){classTypeNameInternal}Internal::{eventInfo.Name}_Bind;"); } foreach (var fieldInfo in classInfo.Fields) { From 236a5a2f6f87e902bf4fc6ecfa9897a0363c9b4c Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Fri, 22 Jan 2021 14:13:45 +0100 Subject: [PATCH 123/419] Update Animation.cs --- Source/Editor/Surface/Archetypes/Animation.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index 2ec196e13..2a0036f21 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -872,7 +872,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Get Node Transform (model space)", Description = "Samples the skeleton node transformation (in model space)", Flags = NodeFlags.AnimGraph, - Size = new Vector2(250, 40), + Size = new Vector2(324, 40), DefaultValues = new object[] { string.Empty, @@ -882,7 +882,8 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), - NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0), + NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1), } }, new NodeArchetype @@ -913,7 +914,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Get Node Transform (local space)", Description = "Samples the skeleton node transformation (in local space)", Flags = NodeFlags.AnimGraph, - Size = new Vector2(250, 40), + Size = new Vector2(316, 40), DefaultValues = new object[] { string.Empty, @@ -923,7 +924,8 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), - NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0), + NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1), } }, new NodeArchetype From 049450e31b5f4bc45fac80a5d9ea744accd50e18 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 22 Jan 2021 23:45:19 +0100 Subject: [PATCH 124/419] Fix node widths for anim nodes --- Source/Editor/Surface/Archetypes/Animation.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index 2a0036f21..e067dceb4 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -791,7 +791,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Transform Node (local space)", Description = "Transforms the skeleton node", Flags = NodeFlags.AnimGraph, - Size = new Vector2(270, 130), + Size = new Vector2(280, 130), DefaultValues = new object[] { string.Empty, @@ -816,7 +816,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Transform Node (model space)", Description = "Transforms the skeleton node", Flags = NodeFlags.AnimGraph, - Size = new Vector2(270, 130), + Size = new Vector2(280, 130), DefaultValues = new object[] { string.Empty, @@ -880,7 +880,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), - NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0), + NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 160, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0), NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1), @@ -904,7 +904,7 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 1), NodeElementArchetype.Factory.Input(1, "Target", true, typeof(Vector3), 2), NodeElementArchetype.Factory.Input(2, "Weight", true, typeof(float), 3, 1), - NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 3, 120, 0), + NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 3, 160, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 3, "Node:"), } }, From 83ceb31967b1c8b1a130fa971af46ad743995aa5 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" <63303990+W2Wizard@users.noreply.github.com> Date: Sat, 23 Jan 2021 01:37:46 +0100 Subject: [PATCH 125/419] Added contribution guidelines Since there were no set out guidelines for contributing, I decided to add them. The guidelines overall are very straightforward. --- CONTRIBUTING.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..31ce62f85 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# How to contribute to the FlaxEngine + +For any questions, suggestions or help join our discord! + + + +Want to see whats planned for Flax? + +Go check out our [Trello](https://trello.com/b/NQjLXRCP/flax-roadmap). + +## **Found a bug?** + +* Avoid opening any new issues without having checked if your problem has already been reported. If there are no currently open issues that fit your problem's description, feel free to [add it](https://github.com/FlaxEngine/FlaxEngine/issues/new). + +* When writing an issue make sure to include a clear title and description as well as having filled out all the necessary information, depending on the severity of the issue also include the necessary log files and minidump. + +* Try to following the given template when writing a new issue if possible. + +## **Want to contribute?** + +* When creating a PR for fixing an issue/bug make sure to describe as to what led to the fix as well as mentioning the + relevant issue where it was first mentioned if necessary, for small and obvious fixes this is not needed. + +* For feature PR's the first thing you should evaluate is the value of your contribution, as in, would what it bring to this engine? + If its a small change you could preferably suggest it to us on our discord, else feel free to open up a PR for it. + +* Ensure when creating a PR that your contribution is well explained with a adequate description and title. + +* For large contributions, generally good code quality is expected, make sure your contribution works as intended and is appropriately commented where necessary. + + +Thank you for taking interest in contributing to Flax! From b208b7066bde73fc0f7fdd8e628c1373b3e8d10f Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sat, 23 Jan 2021 11:35:50 +0100 Subject: [PATCH 126/419] Fix fade distance (use input box as well) --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 998c3f3a3..f805264f5 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -274,8 +274,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Compute depth difference auto depthDiff = writeLocal(VariantType::Float, String::Format(TEXT("{0} * ViewFar - {1}"), sceneDepth.Value, posVS.Value), node); + auto fadeDistance = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); + // Apply smoothing factor and clamp the result - value = writeLocal(VariantType::Float, String::Format(TEXT("saturate({0} / {1})"), depthDiff.Value, node->Values[0].AsFloat), node); + value = writeLocal(VariantType::Float, String::Format(TEXT("saturate({0} / {1})"), depthDiff.Value, fadeDistance.Value), node); break; } // Material Function From d901c115704456b6b2bddd473fc79de00fc00ed8 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Wed, 13 Jan 2021 20:37:19 +0200 Subject: [PATCH 127/419] Add compile flag for whole program optimization --- .../Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs | 6 ++++++ .../Flax.Build/Platforms/Windows/WindowsToolchainBase.cs | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs index b1159db1c..9939cea53 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs @@ -56,6 +56,11 @@ namespace Flax.Build.NativeCpp /// public bool Optimization = false; + /// + /// Enables the whole program optimization. + /// + public bool WholeProgramOptimization = false; + /// /// Enables functions level linking support. /// @@ -131,6 +136,7 @@ namespace Flax.Build.NativeCpp RuntimeTypeInfo = RuntimeTypeInfo, Inlining = Inlining, Optimization = Optimization, + WholeProgramOptimization = WholeProgramOptimization, FunctionLevelLinking = FunctionLevelLinking, DebugInformation = DebugInformation, UseDebugCRT = UseDebugCRT, diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 0917b5027..79581e391 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -467,8 +467,11 @@ namespace Flax.Build.Platforms // Frame-Pointer Omission commonArgs.Add("/Oy"); - // Whole Program Optimization - commonArgs.Add("/GL"); + if (compileEnvironment.WholeProgramOptimization) + { + // Whole Program Optimization + commonArgs.Add("/GL"); + } } else { From 8c6dbf2e30eb01c0c94fd1895a20be52d1bf115e Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Wed, 13 Jan 2021 20:37:45 +0200 Subject: [PATCH 128/419] Disable linker optimizations with incremental linking --- .../Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 79581e391..4d3289a86 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -724,7 +724,7 @@ namespace Flax.Build.Platforms args.Add("/PDBALTPATH:%_PDB%"); // Optimize - if (linkEnvironment.Optimization) + if (linkEnvironment.Optimization && !linkEnvironment.UseIncrementalLinking) { // Generate an EXE checksum args.Add("/RELEASE"); From 1a51752ca4c68fc0bd273344b08d7a66769a32cc Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Wed, 13 Jan 2021 20:41:14 +0200 Subject: [PATCH 129/419] Disable whole program optimization and enable incremental linking in development builds --- Source/Tools/Flax.Build/Build/Target.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Target.cs b/Source/Tools/Flax.Build/Build/Target.cs index ebf1d36c9..239578d51 100644 --- a/Source/Tools/Flax.Build/Build/Target.cs +++ b/Source/Tools/Flax.Build/Build/Target.cs @@ -256,6 +256,7 @@ namespace Flax.Build options.CompileEnv.IntrinsicFunctions = false; options.CompileEnv.BufferSecurityCheck = true; options.CompileEnv.Inlining = false; + options.CompileEnv.WholeProgramOptimization = false; options.LinkEnv.DebugInformation = true; options.LinkEnv.LinkTimeCodeGeneration = false; @@ -273,11 +274,11 @@ namespace Flax.Build options.CompileEnv.IntrinsicFunctions = true; options.CompileEnv.BufferSecurityCheck = true; options.CompileEnv.Inlining = true; - //options.CompileEnv.WholeProgramOptimization = true; + options.CompileEnv.WholeProgramOptimization = false; options.LinkEnv.DebugInformation = true; - options.LinkEnv.LinkTimeCodeGeneration = true; - options.LinkEnv.UseIncrementalLinking = false; + options.LinkEnv.LinkTimeCodeGeneration = false; + options.LinkEnv.UseIncrementalLinking = true; options.LinkEnv.Optimization = true; break; case TargetConfiguration.Release: @@ -291,7 +292,7 @@ namespace Flax.Build options.CompileEnv.IntrinsicFunctions = true; options.CompileEnv.BufferSecurityCheck = false; options.CompileEnv.Inlining = true; - //options.CompileEnv.WholeProgramOptimization = true; + options.CompileEnv.WholeProgramOptimization = true; options.LinkEnv.DebugInformation = false; options.LinkEnv.LinkTimeCodeGeneration = true; From f3f25836e205e1ecb4486d8284b89087c7ee1157 Mon Sep 17 00:00:00 2001 From: intolerantape Date: Tue, 19 Jan 2021 23:09:23 -0800 Subject: [PATCH 130/419] Added 3D version of CollisionsHelper::ClosestPointPointLine(). --- Source/Engine/Core/Math/CollisionsHelper.cpp | 22 ++++++++++++++++++++ Source/Engine/Core/Math/CollisionsHelper.h | 9 ++++++++ 2 files changed, 31 insertions(+) diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp index 48e7472d1..3200d8962 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cpp +++ b/Source/Engine/Core/Math/CollisionsHelper.cpp @@ -41,6 +41,28 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2 } } +Vector3 CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1) +{ + const Vector3 p = point - p0; + Vector3 n = p1 - p0; + const float length = n.Length(); + if (length < 1e-10) + { + return p0; + } + n /= length; + const float dot = Vector3::Dot(n, p); + if (dot <= 0.0) + { + return p0; + } + else if (dot >= length) + { + return p1; + } + return p0 + n * dot; +} + void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result) { // Source: Real-Time Collision Detection by Christer Ericson diff --git a/Source/Engine/Core/Math/CollisionsHelper.h b/Source/Engine/Core/Math/CollisionsHelper.h index 33631a682..ee8ea2828 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.h +++ b/Source/Engine/Core/Math/CollisionsHelper.h @@ -72,6 +72,15 @@ public: /// When the method completes, contains the closest point between the two objects. static void ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1, Vector2& result); + /// + /// Determines the closest point between a point and a line. + /// + /// The point to test. + /// The line first point. + /// The line second point. + /// When the method completes, contains the closest point between the two objects. + static Vector3 ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1); + /// /// Determines the closest point between a point and a triangle. /// From 54e544a0f202ac9b14b76071feee205c3f330df3 Mon Sep 17 00:00:00 2001 From: intolerantape Date: Sat, 23 Jan 2021 03:18:04 -0800 Subject: [PATCH 131/419] In CollisionsHelper, added versions of functions with output parameters that return the outputs as said. Also added a 3D version of ClosestPointPointLine() because why wasn't that a thing? --- Source/Engine/Core/Math/CollisionsHelper.cpp | 70 +++++++++++++++++-- Source/Engine/Core/Math/CollisionsHelper.h | 73 ++++++++++++++++++++ 2 files changed, 138 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp index 3200d8962..36a0b0182 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cpp +++ b/Source/Engine/Core/Math/CollisionsHelper.cpp @@ -41,26 +41,44 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2 } } -Vector3 CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1) +Vector2 CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1) +{ + Vector2 result; + ClosestPointPointLine(point, p0, p1, result); + return result; +} + + +void CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result) { const Vector3 p = point - p0; Vector3 n = p1 - p0; const float length = n.Length(); if (length < 1e-10) { - return p0; + result = p0; + return; } n /= length; const float dot = Vector3::Dot(n, p); if (dot <= 0.0) { - return p0; + result = p0; + return; } else if (dot >= length) { - return p1; + result = p1; + return; } - return p0 + n * dot; + result = p0 + n * dot; +} + +Vector3 CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1) +{ + Vector3 result; + ClosestPointPointLine(point, p0, p1, result); + return result; } void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result) @@ -123,6 +141,13 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec result = vertex1 + ab * v2 + ac * w2; //= u*vertex1 + v*vertex2 + w*vertex3, u = va * denom = 1.0f - v - w } +Vector3 CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3) +{ + Vector3 result; + ClosestPointPointTriangle(point, vertex1, vertex2, vertex3, result); + return result; +} + void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result) { // Source: Real-Time Collision Detection by Christer Ericson @@ -134,6 +159,13 @@ void CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& result = point - t * plane.Normal; } +Vector3 CollisionsHelper::ClosestPointPlanePoint(const Plane& plane, const Vector3& point) +{ + Vector3 result; + ClosestPointPlanePoint(plane, point, result); + return result; +} + void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result) { // Source: Real-Time Collision Detection by Christer Ericson @@ -144,6 +176,13 @@ void CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector Vector3::Min(temp, box.Maximum, result); } +Vector3 CollisionsHelper::ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point) +{ + Vector3 result; + ClosestPointBoxPoint(box, point, result); + return result; +} + void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result) { Vector2 temp, end; @@ -152,6 +191,13 @@ void CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const V Vector2::Min(temp, end, result); } +Vector2 CollisionsHelper::ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point) +{ + Vector2 result; + ClosestPointRectanglePoint(rect, point, result); + return result; +} + void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result) { // Source: Jorgy343 @@ -169,6 +215,13 @@ void CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, con result += sphere.Center; } +Vector3 CollisionsHelper::ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point) +{ + Vector3 result; + ClosestPointSpherePoint(sphere, point, result); + return result; +} + void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result) { // Source: Jorgy343 @@ -186,6 +239,13 @@ void CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, c result += sphere1.Center; } +Vector3 CollisionsHelper::ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2) +{ + Vector3 result; + ClosestPointSphereSphere(sphere1, sphere2, result); + return result; +} + float CollisionsHelper::DistancePlanePoint(const Plane& plane, const Vector3& point) { // Source: Real-Time Collision Detection by Christer Ericson diff --git a/Source/Engine/Core/Math/CollisionsHelper.h b/Source/Engine/Core/Math/CollisionsHelper.h index ee8ea2828..06b69741f 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.h +++ b/Source/Engine/Core/Math/CollisionsHelper.h @@ -72,6 +72,15 @@ public: /// When the method completes, contains the closest point between the two objects. static void ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1, Vector2& result); + /// + /// Determines the closest point between a point and a line. + /// + /// The point to test. + /// The line first point. + /// The line second point. + /// The closest point between the two objects. + static Vector2 ClosestPointPointLine(const Vector2& point, const Vector2& p0, const Vector2& p1); + /// /// Determines the closest point between a point and a line. /// @@ -79,6 +88,15 @@ public: /// The line first point. /// The line second point. /// When the method completes, contains the closest point between the two objects. + static void ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result); + + /// + /// Determines the closest point between a point and a line. + /// + /// The point to test. + /// The line first point. + /// The line second point. + /// The closest point between the two objects. static Vector3 ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1); /// @@ -91,6 +109,16 @@ public: /// When the method completes, contains the closest point between the two objects. static void ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Vector3& result); + /// + /// Determines the closest point between a point and a triangle. + /// + /// The point to test. + /// The first vertex to test. + /// The second vertex to test. + /// The third vertex to test. + /// The closest point between the two objects. + static Vector3 ClosestPointPointTriangle(const Vector3& point, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3); + /// /// Determines the closest point between a and a point. /// @@ -98,6 +126,14 @@ public: /// The point to test. /// When the method completes, contains the closest point between the two objects. static void ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result); + + /// + /// Determines the closest point between a and a point. + /// + /// The plane to test. + /// The point to test. + /// The closest point between the two objects. + static Vector3 ClosestPointPlanePoint(const Plane& plane, const Vector3& point); /// /// Determines the closest point between a and a point. @@ -106,6 +142,14 @@ public: /// The point to test. /// When the method completes, contains the closest point between the two objects. static void ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result); + + /// + /// Determines the closest point between a and a point. + /// + /// The box to test. + /// The point to test. + /// The closest point between the two objects. + static Vector3 ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point); /// /// Determines the closest point between a and a point. @@ -114,6 +158,14 @@ public: /// The point to test. /// When the method completes, contains the closest point between the two objects. static void ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result); + + /// + /// Determines the closest point between a and a point. + /// + /// The rectangle to test. + /// The point to test. + /// The closest point between the two objects. + static Vector2 ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point); /// /// Determines the closest point between a and a point. @@ -122,6 +174,14 @@ public: /// The point to test. /// When the method completes, contains the closest point between the two objects; or, if the point is directly in the center of the sphere, contains . static void ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result); + + /// + /// Determines the closest point between a and a point. + /// + /// The sphere to test. + /// The point to test. + /// The closest point between the two objects; or, if the point is directly in the center of the sphere, contains . + static Vector3 ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point); /// /// Determines the closest point between a and a . @@ -135,6 +195,19 @@ public: /// intersection. /// static void ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result); + + /// + /// Determines the closest point between a and a . + /// + /// The first sphere to test. + /// The second sphere to test. + /// The closest point between the two objects; or, if the point is directly in the center of the sphere, contains . + /// + /// If the two spheres are overlapping, but not directly on top of each other, the closest point + /// is the 'closest' point of intersection. This can also be considered is the deepest point of + /// intersection. + /// + static Vector3 ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2); /// /// Determines the distance between a and a point. From 71110c9f295bb160142491e3eb19629cdea00056 Mon Sep 17 00:00:00 2001 From: Richard Date: Sat, 23 Jan 2021 23:39:24 +1100 Subject: [PATCH 132/419] Fixed issue High DPI Issue --- Source/Engine/Input/Input.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 1dfa0e5fe..507e1a5ff 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -676,7 +676,7 @@ void InputService::Update() const auto lockMode = Screen::GetCursorLock(); if (lockMode == CursorLockMode::Locked) { - Input::SetMousePosition(Screen::GetSize() * 0.5f); + Input::SetMousePosition(Screen::GetSize() * 0.5f * Platform::GetDpiScale()); } // Send events for the active actions (send events only in play mode) From d76c167ad93f5672898096cd2925b086616d6f6b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 15:34:40 +0100 Subject: [PATCH 133/419] Add NetworkBase. --- Source/Engine/Platform/Base/NetworkBase.cpp | 63 +++++++++++++++++++++ Source/Engine/Platform/Base/NetworkBase.h | 63 +++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 Source/Engine/Platform/Base/NetworkBase.cpp create mode 100644 Source/Engine/Platform/Base/NetworkBase.h diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp new file mode 100644 index 000000000..79e122f08 --- /dev/null +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "NetworkBase.h" + +bool NetworkBase::Init() +{ + return false; +} + +void NetworkBase::Exit() +{ +} + +bool NetworkBase::CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettings& settings) +{ + return false; +} + +bool NetworkBase::DestroySocket(NetworkSocket& socket) +{ + return false; +} + +bool NetworkBase::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) +{ + return false; +} + +bool NetworkBase::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) +{ + return false; +} + +bool NetworkBase::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint) +{ + return false; +} + +bool NetworkBase::IsReadable(NetworkSocket& socket, uint64* size) +{ + return false; +} + +uint32 NetworkBase::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) +{ + return 0; +} + +uint32 NetworkBase::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint) +{ + return 0; +} + +bool NetworkBase::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint) +{ + return false; +} + +NetworkEndPoint NetworkBase::RemapEndPointToIPv6(NetworkEndPoint& endPoint) +{ + return NetworkEndPoint(); +} + diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h new file mode 100644 index 000000000..60231e66c --- /dev/null +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" +#include "Engine/Core/Types/String.h" +API_INJECT_CPP_CODE("#include \"Engine/Platform/Network.h\""); + +enum class FLAXENGINE_API NetworkProtocolType +{ + Undefined, + Udp, + Tcp +}; + +enum class FLAXENGINE_API NetworkIPVersion +{ + Undefined, + IPv4, + IPv6 +}; + +struct FLAXENGINE_API NetworkSocketCreateSettings +{ + NetworkProtocolType Protocol = NetworkProtocolType::Udp; + NetworkIPVersion IPVersion = NetworkIPVersion::IPv4; + bool DualStacking = true; + bool ReuseAddress = true; + bool Broadcast = false; +}; + +struct FLAXENGINE_API NetworkSocket +{ + NetworkProtocolType Protocol = NetworkProtocolType::Undefined; + NetworkIPVersion IPVersion = NetworkIPVersion::Undefined; + byte Data[8] = {}; // sizeof SOCKET = unsigned __int64 +}; + +struct FLAXENGINE_API NetworkEndPoint +{ + NetworkIPVersion IPVersion = NetworkIPVersion::Undefined; + String Address; + String Port; + byte Data[28] = {}; // sizeof sockaddr_in6 , biggest sockaddr that we will use +}; + +class FLAXENGINE_API NetworkBase +{ + public: + static bool Init(); + static void Exit(); + static bool CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettings& settings); + static bool DestroySocket(NetworkSocket& socket); + static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); + static bool IsReadable(NetworkSocket& socket, uint64* size); + static uint32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); + static uint32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); + static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint); + static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint& endPoint); +}; + From aa972961232a1d9faa275e38f16fe34ccacf8c6d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 15:35:22 +0100 Subject: [PATCH 134/419] Add windows implementation. --- Source/Engine/Platform/Win32/Win32Network.cpp | 370 ++++++++++++++++++ Source/Engine/Platform/Win32/Win32Network.h | 29 ++ .../Engine/Platform/Win32/Win32Platform.cpp | 9 + Source/Engine/Platform/Win32/Win32Platform.h | 1 + .../Platform/Windows/WindowsPlatform.cpp | 2 + 5 files changed, 411 insertions(+) create mode 100644 Source/Engine/Platform/Win32/Win32Network.cpp create mode 100644 Source/Engine/Platform/Win32/Win32Network.h diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp new file mode 100644 index 000000000..6013f92e2 --- /dev/null +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -0,0 +1,370 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "Win32Network.h" +#include "Engine/Core/Log.h" +#include "Engine/Core/Collections/Array.h" +#include +#include +#include + +static_assert(sizeof NetworkSocket::Data >= sizeof SOCKET, "NetworkSocket::Data is not big enough to contains SOCKET !"); +static_assert(sizeof NetworkEndPoint::Data >= sizeof sockaddr_in6, "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !"); + +static const IN6_ADDR v4MappedPrefix = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }}; +static WSAData _wsaData; + +/* + * TODO: Create func to retrieve WSA error string + * Known issues : + * Sometimes getaddrinfo fails without reason, re-trying right after works + * Even if dualstacking is enabled it's not possible to bind an Ipv4mappedIPv6 endpoint. windows limitation + */ + +static int GetAddrSize(const sockaddr& addr) +{ + return addr.sa_family == AF_INET6 ? sizeof sockaddr_in6 : sizeof sockaddr_in; +} + +static int GetAddrSizeFromEP(NetworkEndPoint& endPoint) +{ + return endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof sockaddr_in6 : sizeof sockaddr_in; +} + +static NetworkIPVersion GetIPVersionFromAddr(const sockaddr& addr) +{ + return addr.sa_family == AF_INET6 ? NetworkIPVersion::IPv6 : NetworkIPVersion::IPv4;; +} + +//TODO: can be simplified by casting addr to the right struct and ntohl the port, so we can get rid of getnameinfo +// getnameinfo return a name ( like JimiPC ), not the ip ! +static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) +{ + uint16 size = GetAddrSize(*addr); + char name[100]; + char service[20]; + if (getnameinfo(addr, size, name, sizeof name, service, sizeof service, 0) != 0) + { + LOG(Error, "Unable to extract info from sockaddr ! Error : {0}", WSAGetLastError()); + return true; + } + void* paddr; + if (addr->sa_family == AF_INET6) + paddr = &((sockaddr_in6*)addr)->sin6_addr; + else if (addr->sa_family == AF_INET) + paddr = &((sockaddr_in*)addr)->sin_addr; + + char ip[INET6_ADDRSTRLEN]; + if (inet_ntop(addr->sa_family, paddr, ip, INET6_ADDRSTRLEN) == nullptr) + { + LOG(Error, "Unable to extract address from sockaddr ! Error : {0}", WSAGetLastError()); + return true; + } + endPoint.Address = String(ip); + endPoint.Port = String(service); + endPoint.IPVersion = GetIPVersionFromAddr(*addr); + memcpy(endPoint.Data, addr, size); + return false; +} + +static void PrintAddrFromInfo(addrinfo& info) +{ + addrinfo* curr; + for (curr = &info; curr != nullptr; curr = curr->ai_next) + { + void* addr; + if (curr->ai_family == AF_INET) { + sockaddr_in *ipv4 = (struct sockaddr_in *)curr->ai_addr; + addr = &(ipv4->sin_addr); + } else { + sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)curr->ai_addr; + addr = &(ipv6->sin6_addr); + } + + char str[INET6_ADDRSTRLEN]; + inet_ntop(curr->ai_family, addr, str, INET6_ADDRSTRLEN); + LOG(Info, "ADDR INFO family : {0} socktype : {1}, proto : {2} address : {3}", curr->ai_family, curr->ai_socktype, curr->ai_protocol, StringAnsi(str).ToString().Get()); + } +} + +bool Win32Network::Init() +{ + if (WSAStartup(MAKEWORD(2,0), &_wsaData) != 0) + return true; + return false; +} + +void Win32Network::Exit() +{ + WSACleanup(); +} + +bool Win32Network::CreateSocket(NetworkSocket& netsock, NetworkSocketCreateSettings& settings) +{ + netsock.Protocol = settings.Protocol; + netsock.IPVersion = settings.IPVersion; + const uint8 family = settings.IPVersion == NetworkIPVersion::IPv6 ? AF_INET6 : AF_INET; + const uint8 stype = settings.Protocol == NetworkProtocolType::Tcp ? SOCK_STREAM : SOCK_DGRAM; + const uint8 proto = settings.Protocol == NetworkProtocolType::Tcp ? IPPROTO_TCP : IPPROTO_UDP; + SOCKET sock; + + if ((sock = socket(family, stype, proto)) == INVALID_SOCKET) + { + LOG(Error, "Can't create native socket ! Error : {0}", WSAGetLastError()); + return true; + } + memcpy(netsock.Data, &sock, sizeof sock); + DWORD dw = 0; + if (family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&dw, sizeof dw) == SOCKET_ERROR) + { + LOG(Warning, "System does not support dual stacking socket ! Error : {0}", WSAGetLastError()); + } + unsigned long value = 1; + if (settings.ReuseAddress && setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof value) == SOCKET_ERROR) + { + LOG(Warning, "Can't set socket option to SO_REUSEADDR ! Error : {0}", WSAGetLastError()); + } + + if (settings.Broadcast && settings.Protocol == NetworkProtocolType::Udp) + { + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&value, sizeof value) == SOCKET_ERROR) + { + LOG(Warning, "Can't set socket option to SO_BROADCAST ! Error : {0}", WSAGetLastError()); + } + } + else if (settings.Broadcast) + LOG(Warning, "Can't set socket option to SO_BROADCAST ! The socket must use UDP protocol. Error : {0}", WSAGetLastError()); + + if (ioctlsocket(sock, FIONBIO, &value) == SOCKET_ERROR) + { + LOG(Error, "Can't set socket to NON-BLOCKING type ! Error : {0}", WSAGetLastError()); + return true; // Support using blocking socket , need to test it + } + //DEBUG + LOG(Info, "New socket created : {0}", sock); //TODO: DEBUG + return false; +} + +bool Win32Network::DestroySocket(NetworkSocket& socket) +{ + const SOCKET sock = *(SOCKET*)socket.Data; + if (sock != INVALID_SOCKET) + { + closesocket(sock); + //DEBUG + LOG(Info, "Deleting socket : {0}", sock); //TODO: DEBUG + return false; + } + LOG(Warning, "Unable to delete socket INVALID_SOCKET ! Socket : {0}", sock); + return true; +} + + +bool Win32Network::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) +{ + const uint16 size = GetAddrSizeFromEP(endPoint); + if (connect(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) + { + LOG(Error, "Unable to connect socket to address ! Socket : {0} Address : {1} Port : {2}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get()); + return true; + } + return false; +} + +bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) +{ + if (socket.IPVersion != endPoint.IPVersion) + { + LOG(Error, "Can't bind socket to end point, Socket.IPVersion != EndPoint.IPVersion ! Socket : {0}", *(SOCKET*)socket.Data); + return true; + } + + const uint16 size = endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof sockaddr_in6 : sizeof sockaddr_in; + const sockaddr* addr = (sockaddr*)endPoint.Data; + LOG(Info, "BIND : EndPoint family : {0}", addr->sa_family); + if (bind(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) + { + LOG(Error, "Unable to bind socket ! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), WSAGetLastError()); + return true; + } + //DEBUG + LOG(Info, "Binding socket ! Socket : {0} Address : {1} Port : {2}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get()); + return false; +} + +bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint) +{ + if (serverSock.Protocol != NetworkProtocolType::Tcp) + { + LOG(Warning, "Can't accept connection on UDP socket ! Socket : {0}", *(SOCKET*)serverSock.Data); + return true; + } + SOCKET sock; + sockaddr addr; + if ((sock = accept(*(SOCKET*)serverSock.Data, &addr, nullptr)) == INVALID_SOCKET) + { + LOG(Warning, "Unable to accept incoming connection ! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, WSAGetLastError()); + return true; + } + memcpy(newSock.Data, &sock, sizeof sock); + memcpy(newEndPoint.Data, &addr, GetAddrSize(addr)); + + newSock.Protocol = serverSock.Protocol; + newSock.IPVersion = serverSock.IPVersion; + if (CreateEndPointFromAddr(&addr, newEndPoint)) + return true; + return false; +} + +bool Win32Network::IsReadable(NetworkSocket& socket, uint64* size) +{ + unsigned long value; + if (ioctlsocket(*(SOCKET*)socket.Data, FIONREAD, &value) != 0) + { + LOG(Error, "Unable to query socket for readability ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, WSAGetLastError()); + return true; + } + *size = value; + return false; +} + +uint32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) +{ + if (socket.IPVersion != endPoint->IPVersion) + { + LOG(Error, "Unable to send data, Socket.IPVersion != EndPoint.IPVersion ! Socket : {0}", *(SOCKET*)socket.Data); + return 0; + } + uint32 size; + if (endPoint == nullptr && socket.Protocol == NetworkProtocolType::Tcp) + { + if ((size = send(*(SOCKET*)socket.Data, (const char*)data, length, 0)) == SOCKET_ERROR) + { + LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1}", *(SOCKET*)socket.Data, length); + return -1; + } + } + else if (endPoint != nullptr && socket.Protocol == NetworkProtocolType::Udp) + { + if ((size = sendto(*(SOCKET*)socket.Data, (const char*)data, length, 0, (const sockaddr*)endPoint->Data, GetAddrSizeFromEP(*endPoint))) == SOCKET_ERROR) + { + LOG(Error, "Unable to send data ! Socket : {0} Address : {1} Port : {2} Data Length : {3}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length); + return -1; + } + } + else + { + //TODO: better explanation + LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1}", *(SOCKET*)socket.Data, length); + return -1; + } + return size; +} + +/* + * TODO: handle size == 0 when there is a shutdown + */ +uint32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint) +{ + uint32 size; + if (endPoint == nullptr) // TCP + { + if ((size = recv(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0)) == SOCKET_ERROR) + { + LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1}", *(SOCKET*)socket.Data, bufferSize); + return -1; + } + } + else // UDP + { + int32 addrsize; + sockaddr_in6 addr; + if ((size = recvfrom(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR) + { + LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1}", *(SOCKET*)socket.Data, bufferSize); + return -1; + } + if (CreateEndPointFromAddr((sockaddr*)&addr, *endPoint)) + return true; + } + return size; +} + +// if address is null, it's ADDR_ANY +bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint) +{ + int status; + addrinfo hints; + addrinfo *info; + //TODO: Refactor this crappy thing + char* paddr= address != nullptr ? address->ToStringAnsi().Get() : nullptr; + char* pport = port != nullptr ? port->ToStringAnsi().Get() : nullptr; + //DEBUG + LOG(Info, "Searching available adresses for {0} : {1}", address == nullptr ? String("nullptr").Get() : address->Get(), + port == nullptr ? String("nullptr").Get() : port->Get()); + memset(&hints, 0, sizeof hints); + hints.ai_family = ipv == NetworkIPVersion::IPv6 ? AF_INET6 : ipv == NetworkIPVersion::IPv4 ? AF_INET : AF_UNSPEC; + hints.ai_flags |= AI_ADDRCONFIG; + hints.ai_flags |= AI_V4MAPPED; + if (paddr == nullptr) + { + hints.ai_flags = AI_PASSIVE; + LOG(Info, "PASSIVE MODE"); + } + + // consider using NUMERICHOST/NUMERICSERV if address is a valid Ipv4 or IPv6 so we can skip some look up ( potentially slow when resolving host names ) + // can *paddr works ? + // paddr = nullptr don't work with this func + if ((status = getaddrinfo(paddr == nullptr ? nullptr : paddr, pport, &hints, &info)) != 0) + { + LOG(Error, "Unable to query info for address : {0} Error : {1} !", address->Get(), gai_strerror(status)); //TODO: address can be NULL + return true; + } + + if (info == nullptr) + { + LOG(Error, "Unable to resolve address ! Address : {0}", address->Get());//TODO: address can be NULL + return true; + } + + //DEBUG + PrintAddrFromInfo(*info); + + // We are taking the first addr in the linked list + if (CreateEndPointFromAddr(info->ai_addr, endPoint)) + { + freeaddrinfo(info); + return true; + } + freeaddrinfo(info); + + //DEBUG + LOG(Info, "Address found : {0} : {1}", endPoint.Address, endPoint.Port); + return false; +} + +NetworkEndPoint Win32Network::RemapEndPointToIPv6(NetworkEndPoint endPoint) +{ + if (endPoint.IPVersion == NetworkIPVersion::IPv6) + { + LOG(Warning, "Unable to remap EndPoint, already in IPv6 format !"); + return endPoint; + } + + NetworkEndPoint pv6; + sockaddr_in* addr4 = (sockaddr_in*)endPoint.Data; + sockaddr_in6* addr6 = (sockaddr_in6*)pv6.Data; + const SCOPE_ID scope = SCOPEID_UNSPECIFIED_INIT; + + // Can be replaced by windows built-in macro IN6ADDR_SETV4MAPPED() + memset(addr6, 0, sizeof sockaddr_in6); + addr6->sin6_family = AF_INET6; + addr6->sin6_scope_struct = scope; + addr6->sin6_addr = v4MappedPrefix; + addr6->sin6_port = addr4->sin_port; + memcpy(&addr6->sin6_addr.u.Byte[12], &addr4->sin_addr, 4); // :::::FFFF:XXXX:XXXX X=IPv4 + pv6.IPVersion = NetworkIPVersion::IPv6; + pv6.Port = endPoint.Port; + + return pv6; +} diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h new file mode 100644 index 000000000..708462320 --- /dev/null +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_WIN32 + +#include "Engine/Platform/Base/NetworkBase.h" + +class FLAXENGINE_API Win32Network : public NetworkBase +{ +public: + // [NetworkBase] + static bool Init(); + static void Exit(); + static bool CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettings& settings); + static bool DestroySocket(NetworkSocket& socket); + static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); + static bool IsReadable(NetworkSocket& socket, uint64* size); + static uint32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); + static uint32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); + static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint); + static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint endPoint); + friend NetworkEndPoint; + friend NetworkSocket; +}; + +#endif diff --git a/Source/Engine/Platform/Win32/Win32Platform.cpp b/Source/Engine/Platform/Win32/Win32Platform.cpp index 129ff4249..db6151a17 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.cpp +++ b/Source/Engine/Platform/Win32/Win32Platform.cpp @@ -11,6 +11,7 @@ #include "Engine/Core/Math/Math.h" #include "IncludeWindowsHeaders.h" #include "Engine/Core/Collections/HashFunctions.h" +#include "Engine/Platform/Network.h" #include #include @@ -212,9 +213,17 @@ bool Win32Platform::Init() DeviceId.D = (uint32)cpuInfo.ClockSpeed * cpuInfo.LogicalProcessorCount * cpuInfo.ProcessorCoreCount * cpuInfo.CacheLineSize; } + //TODO: log error if true + Win32Network::Init(); + return false; } +void Win32Platform::Exit() +{ + Network::Exit(); +} + void Win32Platform::MemoryBarrier() { // NOTE: _ReadWriteBarrier and friends only prevent the diff --git a/Source/Engine/Platform/Win32/Win32Platform.h b/Source/Engine/Platform/Win32/Win32Platform.h index b6a4cdd62..cde506278 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.h +++ b/Source/Engine/Platform/Win32/Win32Platform.h @@ -16,6 +16,7 @@ public: // [PlatformBase] static bool Init(); + static void Exit(); static void MemoryBarrier(); static int64 InterlockedExchange(int64 volatile* dst, int64 exchange); static int32 InterlockedCompareExchange(int32 volatile* dst, int32 exchange, int32 comperand); diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 0b1f70989..13c52d9a8 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -624,6 +624,8 @@ void WindowsPlatform::Exit() // Unregister app class UnregisterClassW(ApplicationWindowClass, nullptr); + + Win32Platform::Exit(); } #if !BUILD_RELEASE From 125b4347daf9010329cbabc7fa0b0cd9831c4440 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 15:35:41 +0100 Subject: [PATCH 135/419] Add windows typedef. --- Source/Engine/Platform/Types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Platform/Types.h b/Source/Engine/Platform/Types.h index c8e955c1b..82b44ace9 100644 --- a/Source/Engine/Platform/Types.h +++ b/Source/Engine/Platform/Types.h @@ -22,6 +22,8 @@ class Win32Thread; typedef Win32Thread Thread; class WindowsWindow; typedef WindowsWindow Window; +class Win32Network; +typedef Win32Network Network; #elif PLATFORM_UWP From c53fe681f573c1f30e0abd903f8af9dbbfbeb802 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 15:36:02 +0100 Subject: [PATCH 136/419] Add network header. --- Source/Engine/Platform/Network.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Source/Engine/Platform/Network.h diff --git a/Source/Engine/Platform/Network.h b/Source/Engine/Platform/Network.h new file mode 100644 index 000000000..916555ef4 --- /dev/null +++ b/Source/Engine/Platform/Network.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if PLATFORM_WINDOWS +#include "Win32/Win32Network.h" +#elif PLATFORM_UWP +#include "Win32/Win32Network.h" +#elif PLATFORM_LINUX + +#elif PLATFORM_PS4 + +#elif PLATFORM_XBOX_SCARLETT +#include "Win32/Win32Network.h" // it's probably isn't working +#elif PLATFORM_ANDROID + +#else +#error Missing Network implementation! +#endif + +#include "Types.h" From 9674f276d3a363ca32181fefccfa9711d0787192 Mon Sep 17 00:00:00 2001 From: TalkingWallnut <62713831+TalkingWallnut@users.noreply.github.com> Date: Sat, 23 Jan 2021 15:56:18 +0100 Subject: [PATCH 137/419] Fixed the issue when slope limit was set to 90 degrees flax crashed. I fixed the issue when slope limit was set to 90 degrees flax would commit suicide (crash) (: . I just changed the clamp limit from 90 to 89 degrees. --- Source/Engine/Physics/Colliders/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 6c75879ad..42c316a55 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -55,7 +55,7 @@ void CharacterController::SetHeight(const float value) void CharacterController::SetSlopeLimit(float value) { - value = Math::Clamp(value, 0.0f, 90.0f); + value = Math::Clamp(value, 0.0f, 89.0f); if (Math::NearEqual(value, _slopeLimit)) return; From bf147ba13a2a89bdc7591d37e34dc83c4d033469 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sat, 23 Jan 2021 17:15:46 +0100 Subject: [PATCH 138/419] Sentence restructuring --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 31ce62f85..e8eb1eb47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,15 +18,15 @@ Go check out our [Trello](https://trello.com/b/NQjLXRCP/flax-roadmap). ## **Want to contribute?** -* When creating a PR for fixing an issue/bug make sure to describe as to what led to the fix as well as mentioning the - relevant issue where it was first mentioned if necessary, for small and obvious fixes this is not needed. +* When creating a PR for fixing an issue/bug make sure to describe as to what led to the fix for better understanding, for small and obvious fixes this is not really needed. + However make sure to mention the relevant issue where it was first reported if possible. -* For feature PR's the first thing you should evaluate is the value of your contribution, as in, would what it bring to this engine? +* For feature PR's the first thing you should evaluate is the value of your contribution, as in, what would it bring to this engine? Is it really required? If its a small change you could preferably suggest it to us on our discord, else feel free to open up a PR for it. * Ensure when creating a PR that your contribution is well explained with a adequate description and title. -* For large contributions, generally good code quality is expected, make sure your contribution works as intended and is appropriately commented where necessary. +* Generally, good code quality is expected, make sure your contribution works as intended and is appropriately commented where necessary. Thank you for taking interest in contributing to Flax! From a60e1ab8bbff273aa69fbbbc4c7118b4b1b6b422 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 17:37:43 +0100 Subject: [PATCH 139/419] Place holder for other platforms. --- Source/Engine/Platform/Network.h | 8 ++++---- Source/Engine/Platform/Types.h | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Platform/Network.h b/Source/Engine/Platform/Network.h index 916555ef4..a361eb6bf 100644 --- a/Source/Engine/Platform/Network.h +++ b/Source/Engine/Platform/Network.h @@ -5,15 +5,15 @@ #if PLATFORM_WINDOWS #include "Win32/Win32Network.h" #elif PLATFORM_UWP -#include "Win32/Win32Network.h" +#include "Win32/Win32Network.h" // place holder #elif PLATFORM_LINUX - +#include "Base/NetworkBase.h" // place holder #elif PLATFORM_PS4 - +#include "Base/NetworkBase.h" // place holder #elif PLATFORM_XBOX_SCARLETT #include "Win32/Win32Network.h" // it's probably isn't working #elif PLATFORM_ANDROID - +#include "Base/NetworkBase.h" // place holder #else #error Missing Network implementation! #endif diff --git a/Source/Engine/Platform/Types.h b/Source/Engine/Platform/Types.h index 82b44ace9..cc1bcafcf 100644 --- a/Source/Engine/Platform/Types.h +++ b/Source/Engine/Platform/Types.h @@ -45,6 +45,8 @@ class Win32Thread; typedef Win32Thread Thread; class UWPWindow; typedef UWPWindow Window; +class NetworkBase; // place holder +typedef NetworkBase Network; // place holder #elif PLATFORM_LINUX @@ -66,6 +68,8 @@ class LinuxThread; typedef LinuxThread Thread; class LinuxWindow; typedef LinuxWindow Window; +class NetworkBase; // place holder +typedef NetworkBase Network; // place holder #elif PLATFORM_PS4 @@ -87,6 +91,8 @@ class PS4Thread; typedef PS4Thread Thread; class PS4Window; typedef PS4Window Window; +class NetworkBase; // place holder +typedef NetworkBase Network; // place holder #elif PLATFORM_XBOX_SCARLETT @@ -108,6 +114,8 @@ class Win32Thread; typedef Win32Thread Thread; class XboxScarlettWindow; typedef XboxScarlettWindow Window; +class NetworkBase; // place holder +typedef NetworkBase Network; // place holder #elif PLATFORM_ANDROID @@ -129,6 +137,8 @@ class AndroidThread; typedef AndroidThread Thread; class AndroidWindow; typedef AndroidWindow Window; +class NetworkBase; // place holder +typedef NetworkBase Network; // place holder #else From 3c1a69eb6f04dd0b3c1a8ba46bdf71c28d06db0b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 20:15:13 +0100 Subject: [PATCH 140/419] Replace old getaddrinfo by unicode one. --- Source/Engine/Platform/Win32/Win32Network.cpp | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 6013f92e2..5a05573a6 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -67,9 +67,9 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) return false; } -static void PrintAddrFromInfo(addrinfo& info) +static void PrintAddrFromInfo(addrinfoW& info) { - addrinfo* curr; + addrinfoW* curr; for (curr = &info; curr != nullptr; curr = curr->ai_next) { void* addr; @@ -294,11 +294,8 @@ uint32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffe bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint) { int status; - addrinfo hints; - addrinfo *info; - //TODO: Refactor this crappy thing - char* paddr= address != nullptr ? address->ToStringAnsi().Get() : nullptr; - char* pport = port != nullptr ? port->ToStringAnsi().Get() : nullptr; + addrinfoW hints; + addrinfoW *info; //DEBUG LOG(Info, "Searching available adresses for {0} : {1}", address == nullptr ? String("nullptr").Get() : address->Get(), port == nullptr ? String("nullptr").Get() : port->Get()); @@ -309,21 +306,19 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio if (paddr == nullptr) { hints.ai_flags = AI_PASSIVE; - LOG(Info, "PASSIVE MODE"); - } // consider using NUMERICHOST/NUMERICSERV if address is a valid Ipv4 or IPv6 so we can skip some look up ( potentially slow when resolving host names ) // can *paddr works ? // paddr = nullptr don't work with this func - if ((status = getaddrinfo(paddr == nullptr ? nullptr : paddr, pport, &hints, &info)) != 0) + if ((status = GetAddrInfoW(address == nullptr ? nullptr : address->Get(), port->Get(), &hints, &info)) != 0) { - LOG(Error, "Unable to query info for address : {0} Error : {1} !", address->Get(), gai_strerror(status)); //TODO: address can be NULL + LOG(Error, "Unable to query info for address : {0} Error : {1} !", address != nullptr ? address->Get() : String("ANY").Get(), gai_strerror(status)); //TODO: address can be NULL return true; } if (info == nullptr) { - LOG(Error, "Unable to resolve address ! Address : {0}", address->Get());//TODO: address can be NULL + LOG(Error, "Unable to resolve address ! Address : {0}", address != nullptr ? address->Get() : String("ANY").Get());//TODO: address can be NULL return true; } @@ -333,10 +328,10 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio // We are taking the first addr in the linked list if (CreateEndPointFromAddr(info->ai_addr, endPoint)) { - freeaddrinfo(info); + FreeAddrInfoW(info); return true; } - freeaddrinfo(info); + FreeAddrInfoW(info); //DEBUG LOG(Info, "Address found : {0} : {1}", endPoint.Address, endPoint.Port); From 54da2c8de026f6865e60bad287d76fdd56e4a0f6 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 20:19:21 +0100 Subject: [PATCH 141/419] Fix type. --- Source/Engine/Platform/Base/NetworkBase.cpp | 4 ++-- Source/Engine/Platform/Base/NetworkBase.h | 4 ++-- Source/Engine/Platform/Win32/Win32Network.cpp | 4 ++-- Source/Engine/Platform/Win32/Win32Network.h | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index 79e122f08..6c8c1b822 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -41,12 +41,12 @@ bool NetworkBase::IsReadable(NetworkSocket& socket, uint64* size) return false; } -uint32 NetworkBase::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) +int32 NetworkBase::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) { return 0; } -uint32 NetworkBase::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint) +int32 NetworkBase::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint) { return 0; } diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 60231e66c..084f64027 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -55,8 +55,8 @@ class FLAXENGINE_API NetworkBase static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket, uint64* size); - static uint32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); - static uint32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); + static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); + static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint); static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint& endPoint); }; diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 5a05573a6..1b05e12a1 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -228,7 +228,7 @@ bool Win32Network::IsReadable(NetworkSocket& socket, uint64* size) return false; } -uint32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) +int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) { if (socket.IPVersion != endPoint->IPVersion) { @@ -264,7 +264,7 @@ uint32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length /* * TODO: handle size == 0 when there is a shutdown */ -uint32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint) +int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint) { uint32 size; if (endPoint == nullptr) // TCP diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index 708462320..abb215842 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -18,8 +18,8 @@ public: static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket, uint64* size); - static uint32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); - static uint32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); + static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); + static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint); static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint endPoint); friend NetworkEndPoint; From c9655eee8f851aee921c4ed4a6808695c608d7aa Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 20:21:06 +0100 Subject: [PATCH 142/419] Add bindable argument. --- Source/Engine/Platform/Base/NetworkBase.cpp | 2 +- Source/Engine/Platform/Base/NetworkBase.h | 2 +- Source/Engine/Platform/Win32/Win32Network.cpp | 5 ++--- Source/Engine/Platform/Win32/Win32Network.h | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index 6c8c1b822..fb1fb7ade 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -51,7 +51,7 @@ int32 NetworkBase::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferS return 0; } -bool NetworkBase::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint) +bool NetworkBase::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable) { return false; } diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 084f64027..749c5df3c 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -57,7 +57,7 @@ class FLAXENGINE_API NetworkBase static bool IsReadable(NetworkSocket& socket, uint64* size); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); - static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint); + static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint& endPoint); }; diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 1b05e12a1..3e45e2117 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -291,7 +291,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer } // if address is null, it's ADDR_ANY -bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint) +bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable) { int status; addrinfoW hints; @@ -303,8 +303,7 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio hints.ai_family = ipv == NetworkIPVersion::IPv6 ? AF_INET6 : ipv == NetworkIPVersion::IPv4 ? AF_INET : AF_UNSPEC; hints.ai_flags |= AI_ADDRCONFIG; hints.ai_flags |= AI_V4MAPPED; - if (paddr == nullptr) - { + if (bindable) hints.ai_flags = AI_PASSIVE; // consider using NUMERICHOST/NUMERICSERV if address is a valid Ipv4 or IPv6 so we can skip some look up ( potentially slow when resolving host names ) diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index abb215842..3388e09f2 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -20,7 +20,7 @@ public: static bool IsReadable(NetworkSocket& socket, uint64* size); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); - static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint); + static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint endPoint); friend NetworkEndPoint; friend NetworkSocket; From 3250ca9fad61161a2abb6ca0c7ccdaf1e0772447 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 20:21:44 +0100 Subject: [PATCH 143/419] Add error code to logs. --- Source/Engine/Platform/Win32/Win32Network.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 3e45e2117..f2999f2cf 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -240,7 +240,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = send(*(SOCKET*)socket.Data, (const char*)data, length, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1}", *(SOCKET*)socket.Data, length); + LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, WSAGetLastError()); return -1; } } @@ -248,7 +248,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = sendto(*(SOCKET*)socket.Data, (const char*)data, length, 0, (const sockaddr*)endPoint->Data, GetAddrSizeFromEP(*endPoint))) == SOCKET_ERROR) { - LOG(Error, "Unable to send data ! Socket : {0} Address : {1} Port : {2} Data Length : {3}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length); + LOG(Error, "Unable to send data ! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, WSAGetLastError()); return -1; } } @@ -271,7 +271,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer { if ((size = recv(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1}", *(SOCKET*)socket.Data, bufferSize); + LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, WSAGetLastError()); return -1; } } @@ -281,7 +281,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer sockaddr_in6 addr; if ((size = recvfrom(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1}", *(SOCKET*)socket.Data, bufferSize); + LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, WSAGetLastError()); return -1; } if (CreateEndPointFromAddr((sockaddr*)&addr, *endPoint)) From 4a71fe6727c49266a3deff8fe72e58b029001b9b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 20:21:58 +0100 Subject: [PATCH 144/419] Fix crash. --- Source/Engine/Platform/Win32/Win32Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index f2999f2cf..a75eeaed5 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -277,7 +277,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer } else // UDP { - int32 addrsize; + int32 addrsize = sizeof sockaddr_in6; sockaddr_in6 addr; if ((size = recvfrom(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR) { From c9bab2c521ef60272d1b8d960c78fcc37a765398 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 21:07:19 +0100 Subject: [PATCH 145/419] Remove issue comment, resolved by commit #c9655ee. --- Source/Engine/Platform/Win32/Win32Network.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index a75eeaed5..2047be372 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -17,7 +17,6 @@ static WSAData _wsaData; /* * TODO: Create func to retrieve WSA error string * Known issues : - * Sometimes getaddrinfo fails without reason, re-trying right after works * Even if dualstacking is enabled it's not possible to bind an Ipv4mappedIPv6 endpoint. windows limitation */ From b3388d6fc7167fd40052646b774560236c6767c2 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 21:17:14 +0100 Subject: [PATCH 146/419] Replace error code by error strings. --- Source/Engine/Platform/Win32/Win32Network.cpp | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 2047be372..1e9b3fe3c 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -20,6 +20,16 @@ static WSAData _wsaData; * Even if dualstacking is enabled it's not possible to bind an Ipv4mappedIPv6 endpoint. windows limitation */ +static Char* GetLastErrorMessage() +{ + wchar_t *s = NULL; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&s, 0, NULL); + return s; +} + static int GetAddrSize(const sockaddr& addr) { return addr.sa_family == AF_INET6 ? sizeof sockaddr_in6 : sizeof sockaddr_in; @@ -44,7 +54,7 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) char service[20]; if (getnameinfo(addr, size, name, sizeof name, service, sizeof service, 0) != 0) { - LOG(Error, "Unable to extract info from sockaddr ! Error : {0}", WSAGetLastError()); + LOG(Error, "Unable to extract info from sockaddr ! Error : {0}", String(GetLastErrorMessage()).Get()); return true; } void* paddr; @@ -56,7 +66,7 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) char ip[INET6_ADDRSTRLEN]; if (inet_ntop(addr->sa_family, paddr, ip, INET6_ADDRSTRLEN) == nullptr) { - LOG(Error, "Unable to extract address from sockaddr ! Error : {0}", WSAGetLastError()); + LOG(Error, "Unable to extract address from sockaddr ! Error : {0}", String(GetLastErrorMessage()).Get()); return true; } endPoint.Address = String(ip); @@ -109,34 +119,34 @@ bool Win32Network::CreateSocket(NetworkSocket& netsock, NetworkSocketCreateSetti if ((sock = socket(family, stype, proto)) == INVALID_SOCKET) { - LOG(Error, "Can't create native socket ! Error : {0}", WSAGetLastError()); + LOG(Error, "Can't create native socket ! Error : {0}", String(GetLastErrorMessage()).Get()); return true; } memcpy(netsock.Data, &sock, sizeof sock); DWORD dw = 0; if (family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&dw, sizeof dw) == SOCKET_ERROR) { - LOG(Warning, "System does not support dual stacking socket ! Error : {0}", WSAGetLastError()); + LOG(Warning, "System does not support dual stacking socket ! Error : {0}", String(GetLastErrorMessage()).Get()); } unsigned long value = 1; if (settings.ReuseAddress && setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof value) == SOCKET_ERROR) { - LOG(Warning, "Can't set socket option to SO_REUSEADDR ! Error : {0}", WSAGetLastError()); + LOG(Warning, "Can't set socket option to SO_REUSEADDR ! Error : {0}", String(GetLastErrorMessage()).Get()); } if (settings.Broadcast && settings.Protocol == NetworkProtocolType::Udp) { if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&value, sizeof value) == SOCKET_ERROR) { - LOG(Warning, "Can't set socket option to SO_BROADCAST ! Error : {0}", WSAGetLastError()); + LOG(Warning, "Can't set socket option to SO_BROADCAST ! Error : {0}", String(GetLastErrorMessage()).Get()); } } else if (settings.Broadcast) - LOG(Warning, "Can't set socket option to SO_BROADCAST ! The socket must use UDP protocol. Error : {0}", WSAGetLastError()); + LOG(Warning, "Can't set socket option to SO_BROADCAST ! The socket must use UDP protocol. Error : {0}", String(GetLastErrorMessage()).Get()); if (ioctlsocket(sock, FIONBIO, &value) == SOCKET_ERROR) { - LOG(Error, "Can't set socket to NON-BLOCKING type ! Error : {0}", WSAGetLastError()); + LOG(Error, "Can't set socket to NON-BLOCKING type ! Error : {0}", String(GetLastErrorMessage()).Get()); return true; // Support using blocking socket , need to test it } //DEBUG @@ -183,7 +193,7 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) LOG(Info, "BIND : EndPoint family : {0}", addr->sa_family); if (bind(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) { - LOG(Error, "Unable to bind socket ! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), WSAGetLastError()); + LOG(Error, "Unable to bind socket ! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), String(GetLastErrorMessage()).Get()); return true; } //DEBUG @@ -202,7 +212,7 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net sockaddr addr; if ((sock = accept(*(SOCKET*)serverSock.Data, &addr, nullptr)) == INVALID_SOCKET) { - LOG(Warning, "Unable to accept incoming connection ! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, WSAGetLastError()); + LOG(Warning, "Unable to accept incoming connection ! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, String(GetLastErrorMessage()).Get()); return true; } memcpy(newSock.Data, &sock, sizeof sock); @@ -220,7 +230,7 @@ bool Win32Network::IsReadable(NetworkSocket& socket, uint64* size) unsigned long value; if (ioctlsocket(*(SOCKET*)socket.Data, FIONREAD, &value) != 0) { - LOG(Error, "Unable to query socket for readability ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, WSAGetLastError()); + LOG(Error, "Unable to query socket for readability ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, String(GetLastErrorMessage()).Get()); return true; } *size = value; @@ -239,7 +249,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = send(*(SOCKET*)socket.Data, (const char*)data, length, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, WSAGetLastError()); + LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, String(GetLastErrorMessage()).Get()); return -1; } } @@ -247,7 +257,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = sendto(*(SOCKET*)socket.Data, (const char*)data, length, 0, (const sockaddr*)endPoint->Data, GetAddrSizeFromEP(*endPoint))) == SOCKET_ERROR) { - LOG(Error, "Unable to send data ! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, WSAGetLastError()); + LOG(Error, "Unable to send data ! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, String(GetLastErrorMessage()).Get()); return -1; } } @@ -270,7 +280,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer { if ((size = recv(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, WSAGetLastError()); + LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, String(GetLastErrorMessage()).Get()); return -1; } } @@ -280,7 +290,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer sockaddr_in6 addr; if ((size = recvfrom(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, WSAGetLastError()); + LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, String(GetLastErrorMessage()).Get()); return -1; } if (CreateEndPointFromAddr((sockaddr*)&addr, *endPoint)) From 261aeb92456b4eba6bf9ca04a04170b693cff0cc Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 21:22:38 +0100 Subject: [PATCH 147/419] Fix leak. --- Source/Engine/Platform/Win32/Win32Network.cpp | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 1e9b3fe3c..e41779dac 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -20,14 +20,16 @@ static WSAData _wsaData; * Even if dualstacking is enabled it's not possible to bind an Ipv4mappedIPv6 endpoint. windows limitation */ -static Char* GetLastErrorMessage() +static String GetLastErrorMessage() { wchar_t *s = NULL; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&s, 0, NULL); - return s; + String str(s); + LocalFree(s); + return str; } static int GetAddrSize(const sockaddr& addr) @@ -54,7 +56,7 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) char service[20]; if (getnameinfo(addr, size, name, sizeof name, service, sizeof service, 0) != 0) { - LOG(Error, "Unable to extract info from sockaddr ! Error : {0}", String(GetLastErrorMessage()).Get()); + LOG(Error, "Unable to extract info from sockaddr ! Error : {0}", GetLastErrorMessage().Get()); return true; } void* paddr; @@ -66,7 +68,7 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) char ip[INET6_ADDRSTRLEN]; if (inet_ntop(addr->sa_family, paddr, ip, INET6_ADDRSTRLEN) == nullptr) { - LOG(Error, "Unable to extract address from sockaddr ! Error : {0}", String(GetLastErrorMessage()).Get()); + LOG(Error, "Unable to extract address from sockaddr ! Error : {0}", GetLastErrorMessage().Get()); return true; } endPoint.Address = String(ip); @@ -119,34 +121,34 @@ bool Win32Network::CreateSocket(NetworkSocket& netsock, NetworkSocketCreateSetti if ((sock = socket(family, stype, proto)) == INVALID_SOCKET) { - LOG(Error, "Can't create native socket ! Error : {0}", String(GetLastErrorMessage()).Get()); + LOG(Error, "Can't create native socket ! Error : {0}", GetLastErrorMessage().Get()); return true; } memcpy(netsock.Data, &sock, sizeof sock); DWORD dw = 0; if (family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&dw, sizeof dw) == SOCKET_ERROR) { - LOG(Warning, "System does not support dual stacking socket ! Error : {0}", String(GetLastErrorMessage()).Get()); + LOG(Warning, "System does not support dual stacking socket ! Error : {0}", GetLastErrorMessage().Get()); } unsigned long value = 1; if (settings.ReuseAddress && setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof value) == SOCKET_ERROR) { - LOG(Warning, "Can't set socket option to SO_REUSEADDR ! Error : {0}", String(GetLastErrorMessage()).Get()); + LOG(Warning, "Can't set socket option to SO_REUSEADDR ! Error : {0}", GetLastErrorMessage().Get()); } if (settings.Broadcast && settings.Protocol == NetworkProtocolType::Udp) { if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&value, sizeof value) == SOCKET_ERROR) { - LOG(Warning, "Can't set socket option to SO_BROADCAST ! Error : {0}", String(GetLastErrorMessage()).Get()); + LOG(Warning, "Can't set socket option to SO_BROADCAST ! Error : {0}", GetLastErrorMessage().Get()); } } else if (settings.Broadcast) - LOG(Warning, "Can't set socket option to SO_BROADCAST ! The socket must use UDP protocol. Error : {0}", String(GetLastErrorMessage()).Get()); + LOG(Warning, "Can't set socket option to SO_BROADCAST ! The socket must use UDP protocol. Error : {0}", GetLastErrorMessage().Get()); if (ioctlsocket(sock, FIONBIO, &value) == SOCKET_ERROR) { - LOG(Error, "Can't set socket to NON-BLOCKING type ! Error : {0}", String(GetLastErrorMessage()).Get()); + LOG(Error, "Can't set socket to NON-BLOCKING type ! Error : {0}", GetLastErrorMessage().Get()); return true; // Support using blocking socket , need to test it } //DEBUG @@ -193,7 +195,7 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) LOG(Info, "BIND : EndPoint family : {0}", addr->sa_family); if (bind(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) { - LOG(Error, "Unable to bind socket ! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), String(GetLastErrorMessage()).Get()); + LOG(Error, "Unable to bind socket ! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetLastErrorMessage().Get()); return true; } //DEBUG @@ -212,7 +214,7 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net sockaddr addr; if ((sock = accept(*(SOCKET*)serverSock.Data, &addr, nullptr)) == INVALID_SOCKET) { - LOG(Warning, "Unable to accept incoming connection ! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, String(GetLastErrorMessage()).Get()); + LOG(Warning, "Unable to accept incoming connection ! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetLastErrorMessage().Get()); return true; } memcpy(newSock.Data, &sock, sizeof sock); @@ -230,7 +232,7 @@ bool Win32Network::IsReadable(NetworkSocket& socket, uint64* size) unsigned long value; if (ioctlsocket(*(SOCKET*)socket.Data, FIONREAD, &value) != 0) { - LOG(Error, "Unable to query socket for readability ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, String(GetLastErrorMessage()).Get()); + LOG(Error, "Unable to query socket for readability ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); return true; } *size = value; @@ -249,7 +251,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = send(*(SOCKET*)socket.Data, (const char*)data, length, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, String(GetLastErrorMessage()).Get()); + LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, GetLastErrorMessage().Get()); return -1; } } @@ -257,7 +259,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = sendto(*(SOCKET*)socket.Data, (const char*)data, length, 0, (const sockaddr*)endPoint->Data, GetAddrSizeFromEP(*endPoint))) == SOCKET_ERROR) { - LOG(Error, "Unable to send data ! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, String(GetLastErrorMessage()).Get()); + LOG(Error, "Unable to send data ! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, GetLastErrorMessage().Get()); return -1; } } @@ -280,7 +282,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer { if ((size = recv(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, String(GetLastErrorMessage()).Get()); + LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage().Get()); return -1; } } @@ -290,7 +292,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer sockaddr_in6 addr; if ((size = recvfrom(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, String(GetLastErrorMessage()).Get()); + LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage().Get()); return -1; } if (CreateEndPointFromAddr((sockaddr*)&addr, *endPoint)) From 86b5fa93213801d93579a5c6b5de867f298ef7ac Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 23 Jan 2021 21:32:07 +0100 Subject: [PATCH 148/419] Remove old comment. --- Source/Engine/Platform/Win32/Win32Network.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index e41779dac..bb2b4e51f 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -15,7 +15,6 @@ static const IN6_ADDR v4MappedPrefix = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x static WSAData _wsaData; /* - * TODO: Create func to retrieve WSA error string * Known issues : * Even if dualstacking is enabled it's not possible to bind an Ipv4mappedIPv6 endpoint. windows limitation */ From 9b300fe074b4ad4c3897d14ad8f8990448d62930 Mon Sep 17 00:00:00 2001 From: Damian Korczowski Date: Sat, 23 Jan 2021 21:33:01 +0100 Subject: [PATCH 149/419] Cleanup --- Source/Engine/Platform/Win32/Win32Network.cpp | 107 +++++++++--------- Source/Engine/Platform/Win32/Win32Network.h | 9 +- 2 files changed, 59 insertions(+), 57 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index bb2b4e51f..29f5ef495 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -5,13 +5,13 @@ #include "Engine/Core/Collections/Array.h" #include #include -#include +#include static_assert(sizeof NetworkSocket::Data >= sizeof SOCKET, "NetworkSocket::Data is not big enough to contains SOCKET !"); static_assert(sizeof NetworkEndPoint::Data >= sizeof sockaddr_in6, "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !"); -static const IN6_ADDR v4MappedPrefix = {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }}; +static const IN6_ADDR v4MappedPrefix = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } }; static WSAData _wsaData; /* @@ -21,11 +21,11 @@ static WSAData _wsaData; static String GetLastErrorMessage() { - wchar_t *s = NULL; - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, WSAGetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPWSTR)&s, 0, NULL); + wchar_t* s = nullptr; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&s), 0, nullptr); String str(s); LocalFree(s); return str; @@ -47,7 +47,7 @@ static NetworkIPVersion GetIPVersionFromAddr(const sockaddr& addr) } //TODO: can be simplified by casting addr to the right struct and ntohl the port, so we can get rid of getnameinfo -// getnameinfo return a name ( like JimiPC ), not the ip ! +// getnameinfo return a name ( like JimiPC ), not the ip! static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) { uint16 size = GetAddrSize(*addr); @@ -55,7 +55,7 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) char service[20]; if (getnameinfo(addr, size, name, sizeof name, service, sizeof service, 0) != 0) { - LOG(Error, "Unable to extract info from sockaddr ! Error : {0}", GetLastErrorMessage().Get()); + LOG(Error, "Unable to extract info from sockaddr! Error : {0}", GetLastErrorMessage().Get()); return true; } void* paddr; @@ -67,7 +67,7 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) char ip[INET6_ADDRSTRLEN]; if (inet_ntop(addr->sa_family, paddr, ip, INET6_ADDRSTRLEN) == nullptr) { - LOG(Error, "Unable to extract address from sockaddr ! Error : {0}", GetLastErrorMessage().Get()); + LOG(Error, "Unable to extract address from sockaddr! Error : {0}", GetLastErrorMessage().Get()); return true; } endPoint.Address = String(ip); @@ -83,23 +83,24 @@ static void PrintAddrFromInfo(addrinfoW& info) for (curr = &info; curr != nullptr; curr = curr->ai_next) { void* addr; - if (curr->ai_family == AF_INET) { - sockaddr_in *ipv4 = (struct sockaddr_in *)curr->ai_addr; + if (curr->ai_family == AF_INET) { + sockaddr_in* ipv4 = (struct sockaddr_in*)curr->ai_addr; addr = &(ipv4->sin_addr); - } else { - sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)curr->ai_addr; + } + else { + sockaddr_in6* ipv6 = (struct sockaddr_in6*)curr->ai_addr; addr = &(ipv6->sin6_addr); } - + char str[INET6_ADDRSTRLEN]; inet_ntop(curr->ai_family, addr, str, INET6_ADDRSTRLEN); - LOG(Info, "ADDR INFO family : {0} socktype : {1}, proto : {2} address : {3}", curr->ai_family, curr->ai_socktype, curr->ai_protocol, StringAnsi(str).ToString().Get()); + LOG(Info, "ADDR INFO family : {0} socktype : {1}, proto : {2} address : {3}", curr->ai_family, curr->ai_socktype, curr->ai_protocol, StringAnsi(str).ToString().Get()); } } bool Win32Network::Init() { - if (WSAStartup(MAKEWORD(2,0), &_wsaData) != 0) + if (WSAStartup(MAKEWORD(2, 0), &_wsaData) != 0) return true; return false; } @@ -109,45 +110,45 @@ void Win32Network::Exit() WSACleanup(); } -bool Win32Network::CreateSocket(NetworkSocket& netsock, NetworkSocketCreateSettings& settings) +bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettings& settings) { - netsock.Protocol = settings.Protocol; - netsock.IPVersion = settings.IPVersion; + socket.Protocol = settings.Protocol; + socket.IPVersion = settings.IPVersion; const uint8 family = settings.IPVersion == NetworkIPVersion::IPv6 ? AF_INET6 : AF_INET; const uint8 stype = settings.Protocol == NetworkProtocolType::Tcp ? SOCK_STREAM : SOCK_DGRAM; const uint8 proto = settings.Protocol == NetworkProtocolType::Tcp ? IPPROTO_TCP : IPPROTO_UDP; SOCKET sock; - - if ((sock = socket(family, stype, proto)) == INVALID_SOCKET) + + if ((sock = ::socket(family, stype, proto)) == INVALID_SOCKET) { - LOG(Error, "Can't create native socket ! Error : {0}", GetLastErrorMessage().Get()); + LOG(Error, "Can't create native socket! Error : {0}", GetLastErrorMessage().Get()); return true; } - memcpy(netsock.Data, &sock, sizeof sock); + memcpy(socket.Data, &sock, sizeof sock); DWORD dw = 0; if (family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&dw, sizeof dw) == SOCKET_ERROR) { - LOG(Warning, "System does not support dual stacking socket ! Error : {0}", GetLastErrorMessage().Get()); + LOG(Warning, "System does not support dual stacking socket! Error : {0}", GetLastErrorMessage().Get()); } unsigned long value = 1; if (settings.ReuseAddress && setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof value) == SOCKET_ERROR) { - LOG(Warning, "Can't set socket option to SO_REUSEADDR ! Error : {0}", GetLastErrorMessage().Get()); + LOG(Warning, "Can't set socket option to SO_REUSEADDR! Error : {0}", GetLastErrorMessage().Get()); } if (settings.Broadcast && settings.Protocol == NetworkProtocolType::Udp) { if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&value, sizeof value) == SOCKET_ERROR) { - LOG(Warning, "Can't set socket option to SO_BROADCAST ! Error : {0}", GetLastErrorMessage().Get()); + LOG(Warning, "Can't set socket option to SO_BROADCAST! Error : {0}", GetLastErrorMessage().Get()); } } else if (settings.Broadcast) - LOG(Warning, "Can't set socket option to SO_BROADCAST ! The socket must use UDP protocol. Error : {0}", GetLastErrorMessage().Get()); - + LOG(Warning, "Can't set socket option to SO_BROADCAST! The socket must use UDP protocol. Error : {0}", GetLastErrorMessage().Get()); + if (ioctlsocket(sock, FIONBIO, &value) == SOCKET_ERROR) { - LOG(Error, "Can't set socket to NON-BLOCKING type ! Error : {0}", GetLastErrorMessage().Get()); + LOG(Error, "Can't set socket to NON-BLOCKING type! Error : {0}", GetLastErrorMessage().Get()); return true; // Support using blocking socket , need to test it } //DEBUG @@ -165,7 +166,7 @@ bool Win32Network::DestroySocket(NetworkSocket& socket) LOG(Info, "Deleting socket : {0}", sock); //TODO: DEBUG return false; } - LOG(Warning, "Unable to delete socket INVALID_SOCKET ! Socket : {0}", sock); + LOG(Warning, "Unable to delete socket INVALID_SOCKET! Socket : {0}", sock); return true; } @@ -175,7 +176,7 @@ bool Win32Network::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoin const uint16 size = GetAddrSizeFromEP(endPoint); if (connect(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) { - LOG(Error, "Unable to connect socket to address ! Socket : {0} Address : {1} Port : {2}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get()); + LOG(Error, "Unable to connect socket to address! Socket : {0} Address : {1} Port : {2}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get()); return true; } return false; @@ -185,7 +186,7 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) { if (socket.IPVersion != endPoint.IPVersion) { - LOG(Error, "Can't bind socket to end point, Socket.IPVersion != EndPoint.IPVersion ! Socket : {0}", *(SOCKET*)socket.Data); + LOG(Error, "Can't bind socket to end point, Socket.IPVersion != EndPoint.IPVersion! Socket : {0}", *(SOCKET*)socket.Data); return true; } @@ -194,11 +195,11 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) LOG(Info, "BIND : EndPoint family : {0}", addr->sa_family); if (bind(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) { - LOG(Error, "Unable to bind socket ! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetLastErrorMessage().Get()); + LOG(Error, "Unable to bind socket! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetLastErrorMessage().Get()); return true; } //DEBUG - LOG(Info, "Binding socket ! Socket : {0} Address : {1} Port : {2}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get()); + LOG(Info, "Binding socket! Socket : {0} Address : {1} Port : {2}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get()); return false; } @@ -206,14 +207,14 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net { if (serverSock.Protocol != NetworkProtocolType::Tcp) { - LOG(Warning, "Can't accept connection on UDP socket ! Socket : {0}", *(SOCKET*)serverSock.Data); + LOG(Warning, "Can't accept connection on UDP socket! Socket : {0}", *(SOCKET*)serverSock.Data); return true; } SOCKET sock; sockaddr addr; if ((sock = accept(*(SOCKET*)serverSock.Data, &addr, nullptr)) == INVALID_SOCKET) { - LOG(Warning, "Unable to accept incoming connection ! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetLastErrorMessage().Get()); + LOG(Warning, "Unable to accept incoming connection! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetLastErrorMessage().Get()); return true; } memcpy(newSock.Data, &sock, sizeof sock); @@ -231,7 +232,7 @@ bool Win32Network::IsReadable(NetworkSocket& socket, uint64* size) unsigned long value; if (ioctlsocket(*(SOCKET*)socket.Data, FIONREAD, &value) != 0) { - LOG(Error, "Unable to query socket for readability ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); + LOG(Error, "Unable to query socket for readability! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); return true; } *size = value; @@ -242,7 +243,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if (socket.IPVersion != endPoint->IPVersion) { - LOG(Error, "Unable to send data, Socket.IPVersion != EndPoint.IPVersion ! Socket : {0}", *(SOCKET*)socket.Data); + LOG(Error, "Unable to send data, Socket.IPVersion != EndPoint.IPVersion! Socket : {0}", *(SOCKET*)socket.Data); return 0; } uint32 size; @@ -250,7 +251,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = send(*(SOCKET*)socket.Data, (const char*)data, length, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, GetLastErrorMessage().Get()); + LOG(Error, "Unable to send data! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, GetLastErrorMessage().Get()); return -1; } } @@ -258,14 +259,14 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = sendto(*(SOCKET*)socket.Data, (const char*)data, length, 0, (const sockaddr*)endPoint->Data, GetAddrSizeFromEP(*endPoint))) == SOCKET_ERROR) { - LOG(Error, "Unable to send data ! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, GetLastErrorMessage().Get()); + LOG(Error, "Unable to send data! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, GetLastErrorMessage().Get()); return -1; } } else { //TODO: better explanation - LOG(Error, "Unable to send data ! Socket : {0} Data Length : {1}", *(SOCKET*)socket.Data, length); + LOG(Error, "Unable to send data! Socket : {0} Data Length : {1}", *(SOCKET*)socket.Data, length); return -1; } return size; @@ -279,9 +280,9 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer uint32 size; if (endPoint == nullptr) // TCP { - if ((size = recv(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0)) == SOCKET_ERROR) + if ((size = recv(*(SOCKET*)socket.Data, (char*)buffer, bufferSize, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage().Get()); + LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage().Get()); return -1; } } @@ -289,9 +290,9 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer { int32 addrsize = sizeof sockaddr_in6; sockaddr_in6 addr; - if ((size = recvfrom(*(SOCKET*)socket.Data, (char*) buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR) + if ((size = recvfrom(*(SOCKET*)socket.Data, (char*)buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data ! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage().Get()); + LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage().Get()); return -1; } if (CreateEndPointFromAddr((sockaddr*)&addr, *endPoint)) @@ -305,7 +306,7 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio { int status; addrinfoW hints; - addrinfoW *info; + addrinfoW* info; //DEBUG LOG(Info, "Searching available adresses for {0} : {1}", address == nullptr ? String("nullptr").Get() : address->Get(), port == nullptr ? String("nullptr").Get() : port->Get()); @@ -321,19 +322,19 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio // paddr = nullptr don't work with this func if ((status = GetAddrInfoW(address == nullptr ? nullptr : address->Get(), port->Get(), &hints, &info)) != 0) { - LOG(Error, "Unable to query info for address : {0} Error : {1} !", address != nullptr ? address->Get() : String("ANY").Get(), gai_strerror(status)); //TODO: address can be NULL + LOG(Error, "Unable to query info for address : {0} Error : {1}", address != nullptr ? address->Get() : String("ANY").Get(), gai_strerror(status)); //TODO: address can be NULL return true; } if (info == nullptr) { - LOG(Error, "Unable to resolve address ! Address : {0}", address != nullptr ? address->Get() : String("ANY").Get());//TODO: address can be NULL + LOG(Error, "Unable to resolve address! Address : {0}", address != nullptr ? address->Get() : String("ANY").Get());//TODO: address can be NULL return true; } //DEBUG PrintAddrFromInfo(*info); - + // We are taking the first addr in the linked list if (CreateEndPointFromAddr(info->ai_addr, endPoint)) { @@ -351,7 +352,7 @@ NetworkEndPoint Win32Network::RemapEndPointToIPv6(NetworkEndPoint endPoint) { if (endPoint.IPVersion == NetworkIPVersion::IPv6) { - LOG(Warning, "Unable to remap EndPoint, already in IPv6 format !"); + LOG(Warning, "Unable to remap EndPoint, already in IPv6 format!"); return endPoint; } @@ -369,6 +370,6 @@ NetworkEndPoint Win32Network::RemapEndPointToIPv6(NetworkEndPoint endPoint) memcpy(&addr6->sin6_addr.u.Byte[12], &addr4->sin_addr, 4); // :::::FFFF:XXXX:XXXX X=IPv4 pv6.IPVersion = NetworkIPVersion::IPv6; pv6.Port = endPoint.Port; - + return pv6; } diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index 3388e09f2..fc1ca093b 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #pragma once @@ -7,7 +7,10 @@ #include "Engine/Platform/Base/NetworkBase.h" class FLAXENGINE_API Win32Network : public NetworkBase -{ +{ + friend NetworkEndPoint; + friend NetworkSocket; + public: // [NetworkBase] static bool Init(); @@ -22,8 +25,6 @@ public: static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint endPoint); - friend NetworkEndPoint; - friend NetworkSocket; }; #endif From 3ab54bd7e12c0cf70e69193a854de2271d65f4b0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 24 Jan 2021 00:02:24 +0100 Subject: [PATCH 150/419] Add listen implementation. --- Source/Engine/Platform/Base/NetworkBase.cpp | 5 +++++ Source/Engine/Platform/Base/NetworkBase.h | 1 + Source/Engine/Platform/Win32/Win32Network.cpp | 10 ++++++++++ Source/Engine/Platform/Win32/Win32Network.h | 1 + 4 files changed, 17 insertions(+) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index fb1fb7ade..389f58335 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -31,6 +31,11 @@ bool NetworkBase::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) return false; } +bool NetworkBase::Listen(NetworkSocket& socket, int32 queueSize) +{ + return false; +} + bool NetworkBase::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint) { return false; diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 749c5df3c..6b33d7b1e 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -53,6 +53,7 @@ class FLAXENGINE_API NetworkBase static bool DestroySocket(NetworkSocket& socket); static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + static bool Listen(NetworkSocket& socket, int32 queueSize); static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket, uint64* size); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 29f5ef495..cec785225 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -203,6 +203,16 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) return false; } +bool Win32Network::Listen(NetworkSocket& socket, int32 queueSize) +{ + if (listen(*(SOCKET*)socket.Data, (int32)queueSize) == SOCKET_ERROR) + { + LOG(Error, "Unable to listen ! Socket : {0} Error : {1}", GetLastErrorMessage().Get()); + return true; + } + return false; +} + bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint) { if (serverSock.Protocol != NetworkProtocolType::Tcp) diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index fc1ca093b..8f4d375d0 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -19,6 +19,7 @@ public: static bool DestroySocket(NetworkSocket& socket); static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + static bool Listen(NetworkSocket& socket, int32 queueSize); static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket, uint64* size); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); From 4457bcf4a7625cbeb3350f87d8cdf9ad7857bce5 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Sun, 24 Jan 2021 02:33:08 +0200 Subject: [PATCH 151/419] Use more modern file dialog for browsing folders --- .../Platform/Windows/WindowsFileSystem.cpp | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp index 7c6b04fef..e30747961 100644 --- a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp +++ b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp @@ -5,6 +5,7 @@ #include "WindowsFileSystem.h" #include "Engine/Platform/File.h" #include "Engine/Platform/Window.h" +#include "Engine/Platform/Windows/ComPtr.h" #include "Engine/Core/Types/StringView.h" #include "../Win32/IncludeWindowsHeaders.h" @@ -293,41 +294,50 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin { bool result = true; + // Randomly generated GUID used for storing the last location of this dialog + const Guid folderGuid(0x53890ed9, 0xa55e47ba, 0xa970bdae, 0x72acedff); + // Allocate memory for the filenames int32 maxPathSize = 2 * MAX_PATH; Array pathBuffer; - pathBuffer.Resize(maxPathSize); - pathBuffer[0] = 0; + pathBuffer.EnsureCapacity(maxPathSize); - // Setup description - BROWSEINFOW bi; - ZeroMemory(&bi, sizeof(bi)); - if (parentWindow) - bi.hwndOwner = static_cast(parentWindow->GetNativePtr()); - bi.lpszTitle = title.HasChars() ? title.Get() : nullptr; - bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; - bi.lpfn = BrowseCallbackProc; - bi.lParam = (LPARAM)(initialDirectory.HasChars() ? initialDirectory.Get() : nullptr); - - LPITEMIDLIST pidl = SHBrowseForFolder(&bi); - - if (pidl != nullptr) + ComPtr fd; + if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fd)))) { - // Get the name of the folder and put it in path - SHGetPathFromIDList(pidl, pathBuffer.Get()); + DWORD options; + fd->GetOptions(&options); + fd->SetOptions(options | FOS_PICKFOLDERS | FOS_NOCHANGEDIR); - if (pathBuffer[0] != 0) - { - path = pathBuffer.Get(); - result = false; - } + if (title.HasChars()) + fd->SetTitle(title.Get()); - // Free memory used - IMalloc* imalloc = 0; - if (SUCCEEDED(SHGetMalloc(&imalloc))) + // Associate the last selected folder with this GUID instead of overwriting the global one + fd->SetClientGuid(*reinterpret_cast(&folderGuid)); + + ComPtr defaultFolder; + if (SUCCEEDED(SHCreateItemFromParsingName(initialDirectory.Get(), NULL, IID_PPV_ARGS(&defaultFolder)))) + fd->SetFolder(defaultFolder); + + if (SUCCEEDED(fd->Show(parentWindow->GetHWND()))) { - imalloc->Free(pidl); - imalloc->Release(); + ComPtr si; + if (SUCCEEDED(fd->GetResult(&si))) + { + LPWSTR resultPath; + if (SUCCEEDED(si->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &resultPath))) + { + int32 resultPathLength = StringUtils::Length(resultPath); + if (resultPathLength < pathBuffer.Capacity()) + { + StringUtils::Copy(pathBuffer.Get(), resultPath, resultPathLength); + CoTaskMemFree(resultPath); + + path = pathBuffer.Get(); + result = false; + } + } + } } } From 9b2c940896a24ebac6764dd47fc086e7c2a72950 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 24 Jan 2021 11:39:59 +0100 Subject: [PATCH 152/419] Switch type. --- Source/Engine/Platform/Base/NetworkBase.cpp | 2 +- Source/Engine/Platform/Base/NetworkBase.h | 2 +- Source/Engine/Platform/Win32/Win32Network.cpp | 2 +- Source/Engine/Platform/Win32/Win32Network.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index 389f58335..8c28f26bd 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -31,7 +31,7 @@ bool NetworkBase::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) return false; } -bool NetworkBase::Listen(NetworkSocket& socket, int32 queueSize) +bool NetworkBase::Listen(NetworkSocket& socket, uint16 queueSize) { return false; } diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 6b33d7b1e..142e7ead1 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -53,7 +53,7 @@ class FLAXENGINE_API NetworkBase static bool DestroySocket(NetworkSocket& socket); static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); - static bool Listen(NetworkSocket& socket, int32 queueSize); + static bool Listen(NetworkSocket& socket, uint16 queueSize); static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket, uint64* size); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index cec785225..6d26b9ba5 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -203,7 +203,7 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) return false; } -bool Win32Network::Listen(NetworkSocket& socket, int32 queueSize) +bool Win32Network::Listen(NetworkSocket& socket, uint16 queueSize) { if (listen(*(SOCKET*)socket.Data, (int32)queueSize) == SOCKET_ERROR) { diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index 8f4d375d0..69f37c8c6 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -19,7 +19,7 @@ public: static bool DestroySocket(NetworkSocket& socket); static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); - static bool Listen(NetworkSocket& socket, int32 queueSize); + static bool Listen(NetworkSocket& socket, uint16 queueSize); static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket, uint64* size); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); From b4f791e0cac697b85f7f89e90b79b09b3d8fd839 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 24 Jan 2021 11:42:11 +0100 Subject: [PATCH 153/419] Remove comment. --- Source/Engine/Platform/Win32/Win32Network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 6d26b9ba5..02d7be637 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -332,13 +332,13 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio // paddr = nullptr don't work with this func if ((status = GetAddrInfoW(address == nullptr ? nullptr : address->Get(), port->Get(), &hints, &info)) != 0) { - LOG(Error, "Unable to query info for address : {0} Error : {1}", address != nullptr ? address->Get() : String("ANY").Get(), gai_strerror(status)); //TODO: address can be NULL + LOG(Error, "Unable to query info for address : {0} Error : {1}", address != nullptr ? address->Get() : String("ANY").Get(), gai_strerror(status)); return true; } if (info == nullptr) { - LOG(Error, "Unable to resolve address! Address : {0}", address != nullptr ? address->Get() : String("ANY").Get());//TODO: address can be NULL + LOG(Error, "Unable to resolve address! Address : {0}", address != nullptr ? address->Get() : String("ANY").Get()); return true; } From 018e751bf6a2322f97cfcdd257510ade2b19ce72 Mon Sep 17 00:00:00 2001 From: Vizepi Date: Sun, 24 Jan 2021 12:03:38 +0100 Subject: [PATCH 154/419] Added option to invert panning direction in editor viewport --- Source/Editor/Options/ViewportOptions.cs | 7 +++ Source/Editor/Viewport/Cameras/FPSCamera.cs | 12 ++++- .../Editor/Viewport/Cameras/ViewportCamera.cs | 9 ++++ Source/Editor/Viewport/EditorViewport.cs | 46 +++++++++++++++---- 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Options/ViewportOptions.cs b/Source/Editor/Options/ViewportOptions.cs index 6e1f8a9d6..5558ac89d 100644 --- a/Source/Editor/Options/ViewportOptions.cs +++ b/Source/Editor/Options/ViewportOptions.cs @@ -45,5 +45,12 @@ namespace FlaxEditor.Options [DefaultValue(60.0f), Limit(35.0f, 160.0f, 0.1f)] [EditorDisplay("Defaults", "Default Field Of View"), EditorOrder(140), Tooltip("The default field of view angle (in degrees) for the viewport camera.")] public float DefaultFieldOfView { get; set; } = 60.0f; + + /// + /// Gets or sets if the panning direction is inverted for the viewport camera. + /// + [DefaultValue(false)] + [EditorDisplay("Defaults"), EditorOrder(150), Tooltip( "Invert the panning direction for the viewport camera." )] + public bool DefaultInvertPanning { get; set; } = false; } } diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index 8ed219449..8a84390eb 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -188,8 +188,16 @@ namespace FlaxEditor.Viewport.Cameras if (input.IsPanning) { var panningSpeed = 0.8f; - position -= right * (mouseDelta.X * panningSpeed); - position -= up * (mouseDelta.Y * panningSpeed); + if (_invertPanning) + { + position += up * (mouseDelta.Y * panningSpeed); + position += right * (mouseDelta.X * panningSpeed); + } + else + { + position -= right * (mouseDelta.X * panningSpeed); + position -= up * (mouseDelta.Y * panningSpeed); + } } // Move diff --git a/Source/Editor/Viewport/Cameras/ViewportCamera.cs b/Source/Editor/Viewport/Cameras/ViewportCamera.cs index a01000035..21993f55c 100644 --- a/Source/Editor/Viewport/Cameras/ViewportCamera.cs +++ b/Source/Editor/Viewport/Cameras/ViewportCamera.cs @@ -12,6 +12,7 @@ namespace FlaxEditor.Viewport.Cameras public abstract class ViewportCamera : IViewportCamera { private EditorViewport _viewport; + protected bool _invertPanning; /// /// Gets the parent viewport. @@ -27,6 +28,14 @@ namespace FlaxEditor.Viewport.Cameras /// public virtual bool UseMovementSpeed => true; + /// + /// Sets if the panning direction is inverted. + /// + public bool InvertPanning + { + set => _invertPanning = value; + } + /// /// Sets view orientation and position to match the arc ball camera style view for the given target object bounds. /// diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 0b810e891..fc777d1a9 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -180,6 +180,7 @@ namespace FlaxEditor.Viewport private float _orthoSize = 1.0f; private bool _isOrtho = false; private float _wheelMovementChangeDeltaSum = 0; + private bool _invertPanning; /// /// Speed of the mouse. @@ -403,6 +404,15 @@ namespace FlaxEditor.Viewport set => _isOrtho = value; } + /// + /// Gets or sets if the panning direction is inverted. + /// + public bool InvertPanning + { + get => _invertPanning; + set => _invertPanning = value; + } + /// /// The input actions collection to processed during user input. /// @@ -434,6 +444,7 @@ namespace FlaxEditor.Viewport _nearPlane = options.Viewport.DefaultNearPlane; _farPlane = options.Viewport.DefaultFarPlane; _fieldOfView = options.Viewport.DefaultFieldOfView; + _invertPanning = options.Viewport.DefaultInvertPanning; Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; OnEditorOptionsChanged(options); @@ -515,7 +526,7 @@ namespace FlaxEditor.Viewport // Orthographic { var ortho = ViewWidgetButtonMenu.AddButton("Orthographic"); - var orthoValue = new CheckBox(75, 2, _isOrtho); + var orthoValue = new CheckBox(80, 2, _isOrtho); orthoValue.Parent = ortho; orthoValue.StateChanged += (checkBox) => { @@ -543,7 +554,7 @@ namespace FlaxEditor.Viewport // Field of View { var fov = ViewWidgetButtonMenu.AddButton("Field Of View"); - var fovValue = new FloatValueBox(1, 75, 2, 50.0f, 35.0f, 160.0f, 0.1f); + var fovValue = new FloatValueBox(1, 80, 2, 50.0f, 35.0f, 160.0f, 0.1f); fovValue.Parent = fov; fovValue.ValueChanged += () => _fieldOfView = fovValue.Value; ViewWidgetButtonMenu.VisibleChanged += (control) => @@ -556,7 +567,7 @@ namespace FlaxEditor.Viewport // Ortho Scale { var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale"); - var orthoSizeValue = new FloatValueBox(_orthoSize, 75, 2, 50.0f, 0.001f, 100000.0f, 0.01f); + var orthoSizeValue = new FloatValueBox(_orthoSize, 80, 2, 50.0f, 0.001f, 100000.0f, 0.01f); orthoSizeValue.Parent = orthoSize; orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value; ViewWidgetButtonMenu.VisibleChanged += (control) => @@ -569,7 +580,7 @@ namespace FlaxEditor.Viewport // Near Plane { var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane"); - var nearPlaneValue = new FloatValueBox(2.0f, 75, 2, 50.0f, 0.001f, 1000.0f); + var nearPlaneValue = new FloatValueBox(2.0f, 80, 2, 50.0f, 0.001f, 1000.0f); nearPlaneValue.Parent = nearPlane; nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane; @@ -578,7 +589,7 @@ namespace FlaxEditor.Viewport // Far Plane { var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane"); - var farPlaneValue = new FloatValueBox(1000, 75, 2, 50.0f, 10.0f); + var farPlaneValue = new FloatValueBox(1000, 80, 2, 50.0f, 10.0f); farPlaneValue.Parent = farPlane; farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane; @@ -587,7 +598,7 @@ namespace FlaxEditor.Viewport // Brightness { var brightness = ViewWidgetButtonMenu.AddButton("Brightness"); - var brightnessValue = new FloatValueBox(1.0f, 75, 2, 50.0f, 0.001f, 10.0f, 0.001f); + var brightnessValue = new FloatValueBox(1.0f, 80, 2, 50.0f, 0.001f, 10.0f, 0.001f); brightnessValue.Parent = brightness; brightnessValue.ValueChanged += () => Brightness = brightnessValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => brightnessValue.Value = Brightness; @@ -596,11 +607,26 @@ namespace FlaxEditor.Viewport // Resolution { var resolution = ViewWidgetButtonMenu.AddButton("Resolution"); - var resolutionValue = new FloatValueBox(1.0f, 75, 2, 50.0f, 0.1f, 4.0f, 0.001f); + var resolutionValue = new FloatValueBox(1.0f, 80, 2, 50.0f, 0.1f, 4.0f, 0.001f); resolutionValue.Parent = resolution; resolutionValue.ValueChanged += () => ResolutionScale = resolutionValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale; } + + // Invert Panning + { + var invert = ViewWidgetButtonMenu.AddButton("Invert Panning"); + var invertValue = new CheckBox(80, 2, _invertPanning); + invertValue.Parent = invert; + invertValue.StateChanged += (checkBox) => + { + if (checkBox.Checked != _invertPanning) + { + _invertPanning = checkBox.Checked; + } + }; + ViewWidgetButtonMenu.VisibleChanged += control => invertValue.Checked = _invertPanning; + } } // Link for task event @@ -874,7 +900,11 @@ namespace FlaxEditor.Viewport protected virtual void UpdateView(float dt, ref Vector3 moveDelta, ref Vector2 mouseDelta, out bool centerMouse) { centerMouse = true; - _camera?.UpdateView(dt, ref moveDelta, ref mouseDelta, out centerMouse); + if (_camera != null) + { + _camera.InvertPanning = _invertPanning; + _camera.UpdateView(dt, ref moveDelta, ref mouseDelta, out centerMouse); + } } /// From 843a04604df6fb42fb72250c8bd479c94aaeace1 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 24 Jan 2021 22:31:45 +1100 Subject: [PATCH 155/419] Added Material Node to Blend Normals --- Source/Editor/Surface/Archetypes/Material.cs | 16 ++++++++++++++++ .../MaterialGenerator.Material.cpp | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index a007db7d7..c415ef012 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -642,6 +642,22 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "XYZ", typeof(Vector3), 0), } }, + new NodeArchetype + { + TypeID = 26, + Title = "Blend Normals", + Description = "Blend two normal maps to create a single normal map", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(170, 40), + ConnectionsHints = ConnectionsHint.Vector, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Base Normal", true, typeof(Vector3), 0), + NodeElementArchetype.Factory.Input(1, "Additional Normal", true, typeof(Vector3), 1), + NodeElementArchetype.Factory.Output(0, "Result", typeof(Vector3), 2) + } + }, + }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 998c3f3a3..55836bcad 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -337,6 +337,14 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) case 25: value = Value(VariantType::Vector3, TEXT("GetObjectSize(input)")); break; + case 26: + { + const auto BaseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); + const auto AdditionalNormal = tryGetValue(node->GetBox(1), Value::Zero).AsVector3(); + const String text = String::Format(TEXT("float3((float2({0}.xy) + float2({1}.xy) * 2.0), sqrt(saturate(1.0 - dot((float2({0}.xy) + float2({1}.xy) * 2.0).xy, (float2({0}.xy) + float2({1}.xy) * 2.0).xy))))"), BaseNormal.Value, AdditionalNormal.Value); + value = writeLocal(ValueType::Vector3, text, node); + break; + } default: break; } From 7de56304c89d91e40bb6236a1cdf03c323641115 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 24 Jan 2021 22:40:03 +1100 Subject: [PATCH 156/419] Remove change as it only works for the editor --- Source/Engine/Input/Input.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 507e1a5ff..1dfa0e5fe 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -676,7 +676,7 @@ void InputService::Update() const auto lockMode = Screen::GetCursorLock(); if (lockMode == CursorLockMode::Locked) { - Input::SetMousePosition(Screen::GetSize() * 0.5f * Platform::GetDpiScale()); + Input::SetMousePosition(Screen::GetSize() * 0.5f); } // Send events for the active actions (send events only in play mode) From 47bff456e0172fa64fed480064b3625afcf96aa5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Jan 2021 13:09:40 +0100 Subject: [PATCH 157/419] Fix foot offset in Character Controller --- Source/Engine/Physics/Colliders/CharacterController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 6c75879ad..da885aee9 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -177,7 +177,7 @@ void CharacterController::CreateActor() const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float minSize = 0.001f; desc.height = Math::Max(Math::Abs(_height) * scaling, minSize); - desc.radius = Math::Max(Math::Abs(_radius) * scaling, minSize); + desc.radius = Math::Max(Math::Abs(_radius) * scaling - desc.contactOffset, minSize); // Create controller _controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc); @@ -202,7 +202,7 @@ void CharacterController::UpdateSize() const { const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); + const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), minSize); const float height = Math::Max(Math::Abs(_height) * scaling, minSize); _controller->setRadius(radius); From b04e1381dc480b733a9560c3a90ec7e1ae034765 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Jan 2021 13:30:48 +0100 Subject: [PATCH 158/419] Fix Unsigned Integer value field to prevent negative values --- Source/Editor/GUI/Input/UIntValueBox.cs | 2 ++ Source/Editor/Surface/Archetypes/Constants.cs | 2 +- .../Surface/Elements/UnsignedIntegerValue.cs | 2 +- Source/Editor/Surface/NodeElementArchetype.cs | 26 +++++++++++++++++++ Source/Editor/Surface/NodeElementType.cs | 5 ++++ Source/Editor/Surface/SurfaceNode.cs | 3 +++ 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Input/UIntValueBox.cs b/Source/Editor/GUI/Input/UIntValueBox.cs index 9f1b2c1f4..d691be116 100644 --- a/Source/Editor/GUI/Input/UIntValueBox.cs +++ b/Source/Editor/GUI/Input/UIntValueBox.cs @@ -143,6 +143,8 @@ namespace FlaxEditor.GUI.Input try { var value = ShuntingYard.Parse(Text); + if (value < 0) + value = 0; Value = (uint)value; } catch (Exception ex) diff --git a/Source/Editor/Surface/Archetypes/Constants.cs b/Source/Editor/Surface/Archetypes/Constants.cs index 63cec366b..3ae9d6a8a 100644 --- a/Source/Editor/Surface/Archetypes/Constants.cs +++ b/Source/Editor/Surface/Archetypes/Constants.cs @@ -379,7 +379,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Output(0, "Value", typeof(uint), 0), - NodeElementArchetype.Factory.Integer(0, 0, 0) + NodeElementArchetype.Factory.UnsignedInteger(0, 0, 0, -1, 0, int.MaxValue) } }, }; diff --git a/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs b/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs index 9668105f1..a1822a268 100644 --- a/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs +++ b/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs @@ -85,7 +85,7 @@ namespace FlaxEditor.Surface.Elements else if (value is Vector4 valueVec4) result = (uint)(arch.BoxID == 0 ? valueVec4.X : arch.BoxID == 1 ? valueVec4.Y : arch.BoxID == 2 ? valueVec4.Z : valueVec4.W); else - result = 0; + result = 0u; return result; } diff --git a/Source/Editor/Surface/NodeElementArchetype.cs b/Source/Editor/Surface/NodeElementArchetype.cs index 07ea35875..cc2f41106 100644 --- a/Source/Editor/Surface/NodeElementArchetype.cs +++ b/Source/Editor/Surface/NodeElementArchetype.cs @@ -239,6 +239,32 @@ namespace FlaxEditor.Surface }; } + /// + /// Creates new Unsigned Integer value element description. + /// + /// The x location (in node area space). + /// The y location (in node area space). + /// The index of the node variable linked as the input. Useful to make a physical connection between input box and default value for it. + /// The index of the component to edit. For vectors this can be set to modify only single component of it. Eg. for vec2 value component set to 1 will edit only Y component. Default value -1 will be used to edit whole value. + /// The minimum value range. + /// The maximum value range. + /// The archetype. + public static NodeElementArchetype UnsignedInteger(float x, float y, int valueIndex = -1, int component = -1, uint valueMin = 0, uint valueMax = 1000000) + { + return new NodeElementArchetype + { + Type = NodeElementType.UnsignedIntegerValue, + Position = new Vector2(Constants.NodeMarginX + x, Constants.NodeMarginY + Constants.NodeHeaderSize + y), + Text = null, + Single = false, + ValueIndex = valueIndex, + ValueMin = valueMin, + ValueMax = valueMax, + BoxID = -1, + ConnectionsType = ScriptType.Null + }; + } + /// /// Creates new Float value element description. /// diff --git a/Source/Editor/Surface/NodeElementType.cs b/Source/Editor/Surface/NodeElementType.cs index 48b2356dd..f104b234c 100644 --- a/Source/Editor/Surface/NodeElementType.cs +++ b/Source/Editor/Surface/NodeElementType.cs @@ -94,5 +94,10 @@ namespace FlaxEditor.Surface /// The actor picker. /// Actor = 19, + + /// + /// The unsigned integer value. + /// + UnsignedIntegerValue = 20, } } diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index cf39384ce..1989992df 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -267,6 +267,9 @@ namespace FlaxEditor.Surface case NodeElementType.Actor: element = new ActorSelect(this, arch); break; + case NodeElementType.UnsignedIntegerValue: + element = new UnsignedIntegerValue(this, arch); + break; //default: throw new NotImplementedException("Unknown node element type: " + arch.Type); } if (element != null) From f1819ee35cd49a76062157427fa77e1a128479de Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Jan 2021 14:01:42 +0100 Subject: [PATCH 159/419] Fix code style --- Source/Engine/Core/Math/CollisionsHelper.cpp | 21 +++++--------------- Source/Engine/Core/Math/CollisionsHelper.h | 10 +++++----- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp index 36a0b0182..d485e5fba 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cpp +++ b/Source/Engine/Core/Math/CollisionsHelper.cpp @@ -15,7 +15,7 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2 Vector2 n = p1 - p0; const float length = n.Length(); - if (length < 1e-10) + if (length < 1e-10f) { // Both points are the same, just give any result = p0; @@ -24,7 +24,7 @@ void CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vector2 n /= length; const float dot = Vector2::Dot(n, p); - if (dot <= 0.0) + if (dot <= 0.0f) { // Before first point result = p0; @@ -48,25 +48,24 @@ Vector2 CollisionsHelper::ClosestPointPointLine(const Vector2& point, const Vect return result; } - void CollisionsHelper::ClosestPointPointLine(const Vector3& point, const Vector3& p0, const Vector3& p1, Vector3& result) { const Vector3 p = point - p0; Vector3 n = p1 - p0; const float length = n.Length(); - if (length < 1e-10) + if (length < 1e-10f) { result = p0; return; } n /= length; const float dot = Vector3::Dot(n, p); - if (dot <= 0.0) + if (dot <= 0.0f) { result = p0; return; } - else if (dot >= length) + if (dot >= length) { result = p1; return; @@ -903,7 +902,6 @@ bool CollisionsHelper::RayIntersectsBox(const Ray& ray, const BoundingBox& box, d = Math::Abs(size.Z - Math::Abs(localPoint.Z)); if (d < dMin) { - dMin = d; normal = Vector3(0, 0, Math::Sign(localPoint.Z)); } @@ -1072,15 +1070,11 @@ PlaneIntersectionType CollisionsHelper::PlaneIntersectsBox(const Plane& plane, c min.Z = plane.Normal.Z >= 0.0f ? box.Maximum.Z : box.Minimum.Z; float distance = Vector3::Dot(plane.Normal, max); - if (distance + plane.D > Plane::DistanceEpsilon) return PlaneIntersectionType::Front; - distance = Vector3::Dot(plane.Normal, min); - if (distance + plane.D < Plane::DistanceEpsilon) return PlaneIntersectionType::Back; - return PlaneIntersectionType::Intersecting; } @@ -1094,10 +1088,8 @@ PlaneIntersectionType CollisionsHelper::PlaneIntersectsSphere(const Plane& plane if (distance > sphere.Radius) return PlaneIntersectionType::Front; - if (distance < -sphere.Radius) return PlaneIntersectionType::Back; - return PlaneIntersectionType::Intersecting; } @@ -1105,13 +1097,10 @@ bool CollisionsHelper::BoxIntersectsBox(const BoundingBox& box1, const BoundingB { if (box1.Minimum.X > box2.Maximum.X || box2.Minimum.X > box1.Maximum.X) return false; - if (box1.Minimum.Y > box2.Maximum.Y || box2.Minimum.Y > box1.Maximum.Y) return false; - if (box1.Minimum.Z > box2.Maximum.Z || box2.Minimum.Z > box1.Maximum.Z) return false; - return true; } diff --git a/Source/Engine/Core/Math/CollisionsHelper.h b/Source/Engine/Core/Math/CollisionsHelper.h index 06b69741f..4bebf2492 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.h +++ b/Source/Engine/Core/Math/CollisionsHelper.h @@ -126,7 +126,7 @@ public: /// The point to test. /// When the method completes, contains the closest point between the two objects. static void ClosestPointPlanePoint(const Plane& plane, const Vector3& point, Vector3& result); - + /// /// Determines the closest point between a and a point. /// @@ -142,7 +142,7 @@ public: /// The point to test. /// When the method completes, contains the closest point between the two objects. static void ClosestPointBoxPoint(const BoundingBox& box, const Vector3& point, Vector3& result); - + /// /// Determines the closest point between a and a point. /// @@ -158,7 +158,7 @@ public: /// The point to test. /// When the method completes, contains the closest point between the two objects. static void ClosestPointRectanglePoint(const Rectangle& rect, const Vector2& point, Vector2& result); - + /// /// Determines the closest point between a and a point. /// @@ -174,7 +174,7 @@ public: /// The point to test. /// When the method completes, contains the closest point between the two objects; or, if the point is directly in the center of the sphere, contains . static void ClosestPointSpherePoint(const BoundingSphere& sphere, const Vector3& point, Vector3& result); - + /// /// Determines the closest point between a and a point. /// @@ -195,7 +195,7 @@ public: /// intersection. /// static void ClosestPointSphereSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2, Vector3& result); - + /// /// Determines the closest point between a and a . /// From 3a33d6ff8483db0c0830a06405493c46c2131411 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Jan 2021 14:14:30 +0100 Subject: [PATCH 160/419] Fix code style --- Source/Editor/Surface/Archetypes/Material.cs | 1 - .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index c415ef012..38ebd9134 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -657,7 +657,6 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "Result", typeof(Vector3), 2) } }, - }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 6460863d8..ce1101868 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -341,9 +341,9 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) break; case 26: { - const auto BaseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); - const auto AdditionalNormal = tryGetValue(node->GetBox(1), Value::Zero).AsVector3(); - const String text = String::Format(TEXT("float3((float2({0}.xy) + float2({1}.xy) * 2.0), sqrt(saturate(1.0 - dot((float2({0}.xy) + float2({1}.xy) * 2.0).xy, (float2({0}.xy) + float2({1}.xy) * 2.0).xy))))"), BaseNormal.Value, AdditionalNormal.Value); + const auto baseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); + const auto additionalNormal = tryGetValue(node->GetBox(1), Value::Zero).AsVector3(); + const String text = String::Format(TEXT("float3((float2({0}.xy) + float2({1}.xy) * 2.0), sqrt(saturate(1.0 - dot((float2({0}.xy) + float2({1}.xy) * 2.0).xy, (float2({0}.xy) + float2({1}.xy) * 2.0).xy))))"), baseNormal.Value, additionalNormal.Value); value = writeLocal(ValueType::Vector3, text, node); break; } From bcb38dc2ca52a79269ae545b392467b6e0f2c2d4 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sun, 24 Jan 2021 15:10:38 +0100 Subject: [PATCH 161/419] Added rotator node --- Source/Editor/Surface/Archetypes/Material.cs | 15 +++++++++++++++ .../MaterialGenerator.Material.cpp | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 38ebd9134..ee70c2b5e 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -657,6 +657,21 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "Result", typeof(Vector3), 2) } }, + new NodeArchetype + { + TypeID = 27, + Title = "Rotator", + Description = "Rotates UV coordinates according to a scalar angle (0-1)", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(150, 55), + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Vector2), 0), + NodeElementArchetype.Factory.Input(1, "Center", true, typeof(Vector2), 1), + NodeElementArchetype.Factory.Input(2, "Rotation Angle", true, typeof(float), 2), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3), + } + }, }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index ce1101868..60e932d8c 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -339,6 +339,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) case 25: value = Value(VariantType::Vector3, TEXT("GetObjectSize(input)")); break; + // Normal blend case 26: { const auto baseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); @@ -346,6 +347,22 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) const String text = String::Format(TEXT("float3((float2({0}.xy) + float2({1}.xy) * 2.0), sqrt(saturate(1.0 - dot((float2({0}.xy) + float2({1}.xy) * 2.0).xy, (float2({0}.xy) + float2({1}.xy) * 2.0).xy))))"), baseNormal.Value, additionalNormal.Value); value = writeLocal(ValueType::Vector3, text, node); break; + } + // Rotator + case 27: + { + auto UV = tryGetValue(node->GetBox(0), Value::Zero).AsVector2(); + auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2(); + auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat(); + + const auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, UV.Value), node); + const auto RACosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node); + + const auto DotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), RACosSin.Value), node); + const auto DotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), RACosSin.Value), node); + + value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, DotB1.Value, DotB2.Value, center.Value), node); + break; } default: break; From 6f0a556af8e67a2eac5584fbea3aff1423891364 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sun, 24 Jan 2021 16:42:13 +0100 Subject: [PATCH 162/419] Document the clockwise order of the indices --- Source/Engine/Graphics/Mesh.cs | 8 ++++---- Source/Engine/Graphics/Models/Mesh.h | 6 +++--- Source/Engine/Graphics/Models/SkinnedMesh.h | 6 +++--- Source/Engine/Graphics/SkinnedMesh.cs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Graphics/Mesh.cs b/Source/Engine/Graphics/Mesh.cs index 22579eb07..f96b4ce63 100644 --- a/Source/Engine/Graphics/Mesh.cs +++ b/Source/Engine/Graphics/Mesh.cs @@ -117,7 +117,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. /// The normal vectors (per vertex). /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). @@ -163,7 +163,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. /// The normal vectors (per vertex). /// The normal vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). @@ -210,7 +210,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null. /// The normal vectors (per vertex). /// The tangent vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). @@ -257,7 +257,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null. /// The normal vectors (per vertex). /// The tangent vectors (per vertex). Use null to compute them from normal vectors. /// The texture coordinates (per vertex). diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index c8311a1f3..33e21aa5f 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -216,7 +216,7 @@ public: /// The first vertex buffer data. /// The second vertex buffer data. /// The third vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if failed, otherwise false. FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, int32* ib) { @@ -231,7 +231,7 @@ public: /// The first vertex buffer data. /// The second vertex buffer data. /// The third vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if failed, otherwise false. FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint16* ib) { @@ -246,7 +246,7 @@ public: /// The first vertex buffer data. /// The second vertex buffer data. /// The third vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. /// True if failed, otherwise false. bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices); diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h index 2c168980b..bbdf38014 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.h +++ b/Source/Engine/Graphics/Models/SkinnedMesh.h @@ -163,7 +163,7 @@ public: /// The amount of vertices in the vertex buffer. /// The amount of triangles in the index buffer. /// The vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if failed, otherwise false. FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, int32* ib) { @@ -176,7 +176,7 @@ public: /// The amount of vertices in the vertex buffer. /// The amount of triangles in the index buffer. /// The vertex buffer data. - /// The index buffer. + /// The index buffer, clockwise order. /// True if failed, otherwise false. FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint16* ib) { @@ -189,7 +189,7 @@ public: /// The amount of vertices in the vertex buffer. /// The amount of triangles in the index buffer. /// The vertex buffer data. - /// The index buffer. + /// The index buffer in clockwise order. /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. /// True if failed, otherwise false. bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices); diff --git a/Source/Engine/Graphics/SkinnedMesh.cs b/Source/Engine/Graphics/SkinnedMesh.cs index e8e0d8353..ea4cf283e 100644 --- a/Source/Engine/Graphics/SkinnedMesh.cs +++ b/Source/Engine/Graphics/SkinnedMesh.cs @@ -104,7 +104,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 32-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. /// The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null. /// The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null. /// The normal vectors (per vertex). @@ -140,7 +140,7 @@ namespace FlaxEngine /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The mesh vertices positions. Cannot be null. - /// The mesh index buffer (triangles). Uses 16-bit stride buffer. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 16-bit stride buffer. Cannot be null. /// The skinned mesh blend indices buffer. Contains indices of the skeleton bones (up to 4 bones per vertex) to use for vertex position blending. Cannot be null. /// The skinned mesh blend weights buffer (normalized). Contains weights per blend bone (up to 4 bones per vertex) of the skeleton bones to mix for vertex position blending. Cannot be null. /// The normal vectors (per vertex). From f9ba789af3002df23de2913a18e7157c45c2248b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Jan 2021 18:51:26 +0100 Subject: [PATCH 163/419] Add missing node type comment --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index ce1101868..f55512e0b 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -339,6 +339,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) case 25: value = Value(VariantType::Vector3, TEXT("GetObjectSize(input)")); break; + // Blend Normals case 26: { const auto baseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); From f714afc1807cb2fd7d401c2f1510d9d181ea8bfc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Jan 2021 19:01:25 +0100 Subject: [PATCH 164/419] Tweak Rotator node --- .../MaterialGenerator/MaterialGenerator.Material.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 64ca22a6f..c4d7c39e5 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -351,17 +351,17 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Rotator case 27: { - auto UV = tryGetValue(node->GetBox(0), Value::Zero).AsVector2(); + auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2(); auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2(); auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat(); - const auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, UV.Value), node); - const auto RACosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node); + const auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, uv.Value), node); + const auto raCosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node); - const auto DotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), RACosSin.Value), node); - const auto DotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), RACosSin.Value), node); + const auto dotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), raCosSin.Value), node); + const auto dotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), raCosSin.Value), node); - value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, DotB1.Value, DotB2.Value, center.Value), node); + value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, dotB1.Value, dotB2.Value, center.Value), node); break; } default: From 6e193d4d0d741e3b9134063f1cda199352a204cc Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 24 Jan 2021 22:54:44 +0100 Subject: [PATCH 165/419] Add Set/Get sockoption, refactor CreateSocket. --- Source/Engine/Platform/Base/NetworkBase.cpp | 22 ++++- Source/Engine/Platform/Base/NetworkBase.h | 34 ++++--- Source/Engine/Platform/Win32/Win32Network.cpp | 90 +++++++++++++++++-- Source/Engine/Platform/Win32/Win32Network.h | 6 +- 4 files changed, 133 insertions(+), 19 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index 8c28f26bd..d2b48206e 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -11,7 +11,7 @@ void NetworkBase::Exit() { } -bool NetworkBase::CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettings& settings) +bool NetworkBase::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv) { return false; } @@ -21,6 +21,26 @@ bool NetworkBase::DestroySocket(NetworkSocket& socket) return false; } +bool NetworkBase::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value) +{ + return false; +} + +bool NetworkBase::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32 value) +{ + return false; +} + +bool NetworkBase::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value) +{ + return false; +} + +bool NetworkBase::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32* value) +{ + return false; +} + bool NetworkBase::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) { return false; diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 142e7ead1..3731bbda6 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -20,15 +20,6 @@ enum class FLAXENGINE_API NetworkIPVersion IPv6 }; -struct FLAXENGINE_API NetworkSocketCreateSettings -{ - NetworkProtocolType Protocol = NetworkProtocolType::Udp; - NetworkIPVersion IPVersion = NetworkIPVersion::IPv4; - bool DualStacking = true; - bool ReuseAddress = true; - bool Broadcast = false; -}; - struct FLAXENGINE_API NetworkSocket { NetworkProtocolType Protocol = NetworkProtocolType::Undefined; @@ -44,13 +35,36 @@ struct FLAXENGINE_API NetworkEndPoint byte Data[28] = {}; // sizeof sockaddr_in6 , biggest sockaddr that we will use }; +enum FLAXENGINE_API NetworkSocketOption +{ + Debug, + ReuseAddr, + KeepAlive, + DontRoute, + Broadcast, + UseLoopback, + Linger, + OOBInline, + SendBuffer, + RecvBuffer, + SendTimeout, + RecvTimeout, + Error, + NoDelay, + IPv6Only +}; + class FLAXENGINE_API NetworkBase { public: static bool Init(); static void Exit(); - static bool CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettings& settings); + static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); static bool DestroySocket(NetworkSocket& socket); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32 value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32* value); static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool Listen(NetworkSocket& socket, uint16 queueSize); diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 02d7be637..f8325d6ff 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -7,6 +7,8 @@ #include #include +#define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) if (option == OPTENUM) { optlvl = OPTLEVEL; optnme = OPTNAME;} + static_assert(sizeof NetworkSocket::Data >= sizeof SOCKET, "NetworkSocket::Data is not big enough to contains SOCKET !"); static_assert(sizeof NetworkEndPoint::Data >= sizeof sockaddr_in6, "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !"); @@ -110,13 +112,13 @@ void Win32Network::Exit() WSACleanup(); } -bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettings& settings) +bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv) { - socket.Protocol = settings.Protocol; - socket.IPVersion = settings.IPVersion; - const uint8 family = settings.IPVersion == NetworkIPVersion::IPv6 ? AF_INET6 : AF_INET; - const uint8 stype = settings.Protocol == NetworkProtocolType::Tcp ? SOCK_STREAM : SOCK_DGRAM; - const uint8 proto = settings.Protocol == NetworkProtocolType::Tcp ? IPPROTO_TCP : IPPROTO_UDP; + socket.Protocol = proto; + socket.IPVersion = ipv; + const uint8 family = socket.IPVersion == NetworkIPVersion::IPv6 ? AF_INET6 : AF_INET; + const uint8 stype = socket.Protocol == NetworkProtocolType::Tcp ? SOCK_STREAM : SOCK_DGRAM; + const uint8 proto = socket.Protocol == NetworkProtocolType::Tcp ? IPPROTO_TCP : IPPROTO_UDP; SOCKET sock; if ((sock = ::socket(family, stype, proto)) == INVALID_SOCKET) @@ -125,11 +127,13 @@ bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettin return true; } memcpy(socket.Data, &sock, sizeof sock); + /* DWORD dw = 0; if (family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&dw, sizeof dw) == SOCKET_ERROR) { LOG(Warning, "System does not support dual stacking socket! Error : {0}", GetLastErrorMessage().Get()); } + unsigned long value = 1; if (settings.ReuseAddress && setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof value) == SOCKET_ERROR) { @@ -145,7 +149,8 @@ bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettin } else if (settings.Broadcast) LOG(Warning, "Can't set socket option to SO_BROADCAST! The socket must use UDP protocol. Error : {0}", GetLastErrorMessage().Get()); - +*/ + unsigned long value = 1; if (ioctlsocket(sock, FIONBIO, &value) == SOCKET_ERROR) { LOG(Error, "Can't set socket to NON-BLOCKING type! Error : {0}", GetLastErrorMessage().Get()); @@ -170,6 +175,77 @@ bool Win32Network::DestroySocket(NetworkSocket& socket) return true; } +bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value) +{ + int32 v = value; + return SetSocketOption(socket, option, v); +} + +bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32 value) +{ + int32 optlvl = 0; + int32 optnme = 0; + + SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) + SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) + SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) + SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) + SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) + SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) + SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) + SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) + SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) + SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) + SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) + SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) + SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) + SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) + SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) + + if (setsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)value, sizeof int32) == SOCKET_ERROR) + { + LOG(Warning, "Unable to set socket option ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); + return true; + } + return false; +} + +bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value) +{ + int32 v; + bool status = GetSocketOption(socket, option, &v); + *value = v; + return status; +} + +bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32* value) +{ + int32 optlvl = 0; + int32 optnme = 0; + + SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) + SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) + SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) + SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) + SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) + SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) + SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) + SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) + SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) + SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) + SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) + SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) + SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) + SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) + + int32 size; + if (getsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)value, &size) == SOCKET_ERROR) + { + LOG(Warning, "Unable to get socket option ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); + return true; + } + return false; +} bool Win32Network::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) { diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index 69f37c8c6..805f68c0a 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -15,8 +15,12 @@ public: // [NetworkBase] static bool Init(); static void Exit(); - static bool CreateSocket(NetworkSocket& socket, NetworkSocketCreateSettings& settings); + static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); static bool DestroySocket(NetworkSocket& socket); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32 value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32* value); static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool Listen(NetworkSocket& socket, uint16 queueSize); From ad621b824a851113f99d0bacfdacc9d6fa0765ef Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 24 Jan 2021 22:59:57 +0100 Subject: [PATCH 166/419] Add some const. --- Source/Engine/Platform/Win32/Win32Network.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index f8325d6ff..09ae94727 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -177,7 +177,7 @@ bool Win32Network::DestroySocket(NetworkSocket& socket) bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value) { - int32 v = value; + const int32 v = value; return SetSocketOption(socket, option, v); } @@ -213,7 +213,7 @@ bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& o bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value) { int32 v; - bool status = GetSocketOption(socket, option, &v); + const bool status = GetSocketOption(socket, option, &v); *value = v; return status; } From f6085c7479a0c4976e3f8bbb20c81ef704aa4308 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 24 Jan 2021 23:31:24 +0100 Subject: [PATCH 167/419] Fix crash when reloading scripts with opened custom json asset --- Source/Editor/Windows/Assets/JsonAssetWindow.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Editor/Windows/Assets/JsonAssetWindow.cs b/Source/Editor/Windows/Assets/JsonAssetWindow.cs index 670414241..da463c501 100644 --- a/Source/Editor/Windows/Assets/JsonAssetWindow.cs +++ b/Source/Editor/Windows/Assets/JsonAssetWindow.cs @@ -40,6 +40,12 @@ namespace FlaxEditor.Windows.Assets _presenter.Modified += MarkAsEdited; } + private void OnScriptsReloadBegin() + { + Close(); + ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; + } + /// public override void Save() { @@ -76,6 +82,10 @@ namespace FlaxEditor.Windows.Assets _presenter.Select(_object); ClearEditedFlag(); + // Auto-close on scripting reload if json asset is from game scripts (it might be reloaded) + if (_object != null && FlaxEngine.Scripting.IsTypeFromGameScripts(_object.GetType())) + ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; + base.OnAssetLoaded(); } From 6ef65734cd7a1f0bd99d4fc358e76406b16b7f29 Mon Sep 17 00:00:00 2001 From: Richard Date: Mon, 25 Jan 2021 09:35:22 +1100 Subject: [PATCH 168/419] Optimisied Blend Normal Node I've also added a getNormalZero to get a normal that has zeroed out, i.e. it faces directly outwards from the face. This enables the user to not have to input a vector for either the Base or Additional Normal inputs and still get a result that is acceptable. This is handy if you want to "wash out" a normal. --- .../MaterialGenerator/MaterialGenerator.Material.cpp | 12 ++++++++---- .../Tools/MaterialGenerator/MaterialGenerator.cpp | 1 + .../Tools/MaterialGenerator/MaterialGenerator.h | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index c4d7c39e5..e06142d81 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -342,10 +342,14 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Blend Normals case 26: { - const auto baseNormal = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); - const auto additionalNormal = tryGetValue(node->GetBox(1), Value::Zero).AsVector3(); - const String text = String::Format(TEXT("float3((float2({0}.xy) + float2({1}.xy) * 2.0), sqrt(saturate(1.0 - dot((float2({0}.xy) + float2({1}.xy) * 2.0).xy, (float2({0}.xy) + float2({1}.xy) * 2.0).xy))))"), baseNormal.Value, additionalNormal.Value); - value = writeLocal(ValueType::Vector3, text, node); + const auto baseNormal = tryGetValue(node->GetBox(0), getNormalZero).AsVector3(); + const auto additionalNormal = tryGetValue(node->GetBox(1), getNormalZero).AsVector3(); + + const String text1 = String::Format(TEXT("(float2({0}.xy) + float2({1}.xy) * 2.0)"), baseNormal.Value, additionalNormal.Value); + const auto appendXY = writeLocal(ValueType::Vector2, text1, node); + + const String text2 = String::Format(TEXT("float3({0}, sqrt(saturate(1.0 - dot({0}.xy, {0}.xy))))"), appendXY.Value); + value = writeLocal(ValueType::Vector3, text2, node); break; } // Rotator diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 7abd29af9..1e71a028d 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -27,6 +27,7 @@ enum MaterialTemplateInputsMapping MaterialValue MaterialGenerator::getUVs(VariantType::Vector2, TEXT("input.TexCoord")); MaterialValue MaterialGenerator::getTime(VariantType::Float, TEXT("TimeParam")); MaterialValue MaterialGenerator::getNormal(VariantType::Vector3, TEXT("input.TBN[2]")); +MaterialValue MaterialGenerator::getNormalZero(VariantType::Vector3, TEXT("float3(.5, .5, 1)")); MaterialValue MaterialGenerator::getVertexColor(VariantType::Vector4, TEXT("GetVertexColor(input)")); MaterialGenerator::MaterialGenerator() diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h index 005b9c58c..804955618 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h @@ -205,6 +205,7 @@ public: static MaterialValue getUVs; static MaterialValue getTime; static MaterialValue getNormal; + static MaterialValue getNormalZero; static MaterialValue getVertexColor; static MaterialGraphBoxesMapping MaterialGraphBoxesMappings[]; From ee166bafeddf2516b34f8027f946889424c22099 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Mon, 25 Jan 2021 00:51:47 +0100 Subject: [PATCH 169/419] Added initial remap node --- Source/Editor/Surface/Archetypes/Math.cs | 23 +++++++++++++++++++++++ Source/Engine/Visject/ShaderGraph.cpp | 16 ++++++++++++++++ Source/Engine/Visject/VisjectGraph.cpp | 16 ++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/Source/Editor/Surface/Archetypes/Math.cs b/Source/Editor/Surface/Archetypes/Math.cs index 92ae69a12..7416ea498 100644 --- a/Source/Editor/Surface/Archetypes/Math.cs +++ b/Source/Editor/Surface/Archetypes/Math.cs @@ -404,6 +404,29 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "A | B", null, 2), } }, + new NodeArchetype + { + TypeID = 48, + Title = "Remap", + Description = "Remaps a value from one range to another, so for example having 25 in a range of 0 to 100 being remapped to 0 to 1 would return 0.25", + Flags = NodeFlags.AllGraphs, + Size = new Vector2(175, 75), + DefaultValues = new object[] + { + 25.0f, + new Vector2(0.0f, 100.0f), + new Vector2(0.0f, 1.0f), + false + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, typeof(float), 0, 0), + NodeElementArchetype.Factory.Input(1, "In Range", true, typeof(Vector2), 1, 1), + NodeElementArchetype.Factory.Input(2, "Out Range", true, typeof(Vector2), 2, 2), + NodeElementArchetype.Factory.Input(3, "Clamp", true, typeof(bool), 3, 3), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 4), + } + }, }; } } diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 7b3c51f3e..7545e7c96 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -401,6 +401,22 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) { value = writeFunction1(node, tryGetValue(node->GetBox(0), Value::Zero), TEXT("radians")); break; + } + // Remap + case 48: + { + auto inVal = tryGetValue(node->GetBox(0), node->Values[0]); + auto rangeA = tryGetValue(node->GetBox(1), node->Values[1]); + auto rangeB = tryGetValue(node->GetBox(2), node->Values[2]); + + // Clamp value? + if (node->Values[3].AsBool) + { + value = writeLocal(ValueType::Float, String::Format(TEXT("clamp({2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x), {2}.x, {2}.y)"), inVal.Value, rangeA.Value, rangeB.Value), node); + break; + } + value = writeLocal(ValueType::Float, String::Format(TEXT("{2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x)"), inVal.Value, rangeA.Value, rangeB.Value), node); + break; } default: break; diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index eb2b8bf61..dfd5c9fdf 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -371,6 +371,22 @@ void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value) if (value.Type.Type == VariantType::Enum) value.AsUint64 = value.AsUint64 | (uint64)tryGetValue(node->GetBox(1), Value::Zero); break; + + case 48: + { + const float inVal = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat; + const Vector2 rangeA = tryGetValue(node->GetBox(1), node->Values[1]).AsVector2(); + const Vector2 rangeB = tryGetValue(node->GetBox(2), node->Values[2]).AsVector2(); + + // Use clamp? + if (node->Values[3].AsBool) + { + value = Math::Clamp(rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X), rangeB.X, rangeB.Y); + break; + } + value = rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X); + break; + } default: break; } From fe78fa757585b5e1315f77069eaed064cf7952fe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 25 Jan 2021 10:41:53 +0100 Subject: [PATCH 170/419] Add Spline --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 19 + Source/Editor/SceneGraph/SceneGraphFactory.cs | 1 + .../Editor/Windows/SceneTreeWindow.Actors.cs | 1 + Source/Editor/Windows/ToolboxWindow.cs | 1 + Source/Engine/Animations/AnimationUtils.h | 25 ++ Source/Engine/Animations/Curve.cs | 3 + Source/Engine/Animations/Curve.h | 44 +++ Source/Engine/Animations/CurveSerialization.h | 171 +++++++++ Source/Engine/Core/Math/Color.h | 8 + Source/Engine/Core/Math/Color32.h | 8 + Source/Engine/Core/Math/Transform.h | 8 + Source/Engine/Core/Math/Vector4.h | 2 +- .../Engine/Level/Actors/ModelInstanceActor.h | 2 +- Source/Engine/Level/Actors/Spline.cpp | 335 ++++++++++++++++++ Source/Engine/Level/Actors/Spline.h | 247 +++++++++++++ Source/Engine/Level/Spline.cs | 41 +++ 16 files changed, 914 insertions(+), 2 deletions(-) create mode 100644 Source/Editor/SceneGraph/Actors/SplineNode.cs create mode 100644 Source/Engine/Animations/CurveSerialization.h create mode 100644 Source/Engine/Level/Actors/Spline.cpp create mode 100644 Source/Engine/Level/Actors/Spline.h create mode 100644 Source/Engine/Level/Spline.cs diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs new file mode 100644 index 000000000..ea826c29e --- /dev/null +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using FlaxEngine; + +namespace FlaxEditor.SceneGraph.Actors +{ + /// + /// Scene tree node for actor type. + /// + [HideInEditor] + public sealed class SplineNode : ActorNode + { + /// + public SplineNode(Actor actor) + : base(actor) + { + } + } +} diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs index 0076742b3..5f3dd8b0f 100644 --- a/Source/Editor/SceneGraph/SceneGraphFactory.cs +++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs @@ -65,6 +65,7 @@ namespace FlaxEditor.SceneGraph CustomNodesTypes.Add(typeof(NavModifierVolume), typeof(NavModifierVolumeNode)); CustomNodesTypes.Add(typeof(ParticleEffect), typeof(ParticleEffectNode)); CustomNodesTypes.Add(typeof(SceneAnimationPlayer), typeof(SceneAnimationPlayerNode)); + CustomNodesTypes.Add(typeof(Spline), typeof(SplineNode)); } /// diff --git a/Source/Editor/Windows/SceneTreeWindow.Actors.cs b/Source/Editor/Windows/SceneTreeWindow.Actors.cs index 6fafe444f..8970b6a48 100644 --- a/Source/Editor/Windows/SceneTreeWindow.Actors.cs +++ b/Source/Editor/Windows/SceneTreeWindow.Actors.cs @@ -99,6 +99,7 @@ namespace FlaxEditor.Windows new KeyValuePair("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)), new KeyValuePair("Nav Link", typeof(NavLink)), new KeyValuePair("Nav Modifier Volume", typeof(NavModifierVolume)), + new KeyValuePair("Spline", typeof(Spline)), } }, new ActorsGroup diff --git a/Source/Editor/Windows/ToolboxWindow.cs b/Source/Editor/Windows/ToolboxWindow.cs index 8c30621b0..63e1f1f05 100644 --- a/Source/Editor/Windows/ToolboxWindow.cs +++ b/Source/Editor/Windows/ToolboxWindow.cs @@ -167,6 +167,7 @@ namespace FlaxEditor.Windows groupOther.AddChild(CreateActorItem("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume))); groupOther.AddChild(CreateActorItem("Nav Link", typeof(NavLink))); groupOther.AddChild(CreateActorItem("Nav Modifier Volume", typeof(NavModifierVolume))); + groupOther.AddChild(CreateActorItem("Spline", typeof(Spline))); var groupGui = CreateGroupWithList(actorGroups, "GUI"); groupGui.AddChild(CreateActorItem("UI Control", typeof(UIControl))); diff --git a/Source/Engine/Animations/AnimationUtils.h b/Source/Engine/Animations/AnimationUtils.h index 248efea0c..a7f85f124 100644 --- a/Source/Engine/Animations/AnimationUtils.h +++ b/Source/Engine/Animations/AnimationUtils.h @@ -7,6 +7,7 @@ #include "Engine/Core/Math/Vector2.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Quaternion.h" +#include "Engine/Core/Math/Transform.h" namespace AnimationUtils { @@ -40,6 +41,12 @@ namespace AnimationUtils return Quaternion::Identity; } + template<> + FORCE_INLINE Transform GetZero() + { + return Transform::Identity; + } + template<> FORCE_INLINE Color GetZero() { @@ -66,6 +73,16 @@ namespace AnimationUtils Quaternion::Slerp(a, b, oneThird, result); } + template<> + FORCE_INLINE void GetTangent(const Transform& a, const Transform& b, float length, Transform& result) + { + const float oneThird = 1.0f / 3.0f; + const float oneThirdLength = length * oneThird; + result.Translation = a.Translation + b.Translation * oneThirdLength; + Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation); + result.Scale = a.Scale + b.Scale * oneThirdLength; + } + template FORCE_INLINE static void Interpolate(const T& a, const T& b, float alpha, T& result) { @@ -203,4 +220,12 @@ namespace AnimationUtils Quaternion::Slerp(p12, p23, alpha, p123); Quaternion::Slerp(p012, p123, alpha, result); } + + template<> + void Bezier(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float alpha, Transform& result) + { + Bezier(p0.Translation, p1.Translation, p2.Translation, p3.Translation, alpha, result.Translation); + Bezier(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, alpha, result.Orientation); + Bezier(p0.Scale, p1.Scale, p2.Scale, p3.Scale, alpha, result.Scale); + } } diff --git a/Source/Engine/Animations/Curve.cs b/Source/Engine/Animations/Curve.cs index d9762d05f..3a8ddc100 100644 --- a/Source/Engine/Animations/Curve.cs +++ b/Source/Engine/Animations/Curve.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; namespace FlaxEngine { @@ -338,6 +339,7 @@ namespace FlaxEngine /// /// A single keyframe that can be injected into linear curve. /// + [StructLayout(LayoutKind.Sequential)] public struct Keyframe : IComparable, IComparable { /// @@ -586,6 +588,7 @@ namespace FlaxEngine /// /// A single keyframe that can be injected into Bezier curve. /// + [StructLayout(LayoutKind.Sequential)] public struct Keyframe : IComparable, IComparable { /// diff --git a/Source/Engine/Animations/Curve.h b/Source/Engine/Animations/Curve.h index 8261dea5b..618e0ee0c 100644 --- a/Source/Engine/Animations/Curve.h +++ b/Source/Engine/Animations/Curve.h @@ -52,6 +52,11 @@ public: { result = a; } + + bool operator==(const StepCurveKeyframe& other) const + { + return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value); + } } PACK_END(); /// @@ -97,6 +102,11 @@ public: result.Time = a.Time + (b.Time - a.Time) * alpha; AnimationUtils::Interpolate(a.Value, b.Value, alpha, result.Value); } + + bool operator==(const LinearCurveKeyframe& other) const + { + return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value); + } } PACK_END(); /// @@ -164,6 +174,11 @@ public: result.TangentIn /= length; result.TangentOut = result.TangentIn; } + + bool operator==(const HermiteCurveKeyframe& other) const + { + return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value) && Math::NearEqual(TangentIn, other.TangentIn) && Math::NearEqual(TangentOut, other.TangentOut); + } } PACK_END(); /// @@ -240,6 +255,11 @@ public: result.TangentIn = a.TangentOut; result.TangentOut = b.TangentIn; } + + bool operator==(const BezierCurveKeyframe& other) const + { + return Math::NearEqual(Time, other.Time) && Math::NearEqual(Value, other.Value) && Math::NearEqual(TangentIn, other.TangentIn) && Math::NearEqual(TangentOut, other.TangentOut); + } } PACK_END(); // @formatter:on @@ -708,6 +728,30 @@ public: return false; } + +public: + + FORCE_INLINE KeyFrame& operator[](int32 index) + { + return _keyframes[index]; + } + + FORCE_INLINE const KeyFrame& operator[](int32 index) const + { + return _keyframes[index]; + } + + bool operator==(const Curve& other) const + { + if (_keyframes.Count() != other._keyframes.Count()) + return false; + for (int32 i = 0; i < _keyframes.Count(); i++) + { + if (!(_keyframes[i] == other._keyframes[i])) + return false; + } + return true; + } }; /// diff --git a/Source/Engine/Animations/CurveSerialization.h b/Source/Engine/Animations/CurveSerialization.h new file mode 100644 index 000000000..06f7788ea --- /dev/null +++ b/Source/Engine/Animations/CurveSerialization.h @@ -0,0 +1,171 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Curve.h" +#include "Engine/Core/Collections/Array.h" +#include "Engine/Serialization/Serialization.h" + +// @formatter:off + +namespace Serialization +{ + // StepCurveKeyframe + + template + inline bool ShouldSerialize(const StepCurveKeyframe& v, const void* otherObj) + { + if (!otherObj) + return true; + const auto other = (const StepCurveKeyframe*)otherObj; + return !(v == *other); + } + template + inline void Serialize(ISerializable::SerializeStream& stream, const StepCurveKeyframe& v, const void* otherObj) + { + stream.StartObject(); + stream.JKEY("Time"); + Serialize(stream, v.Time, nullptr); + stream.JKEY("Value"); + Serialize(stream, v.Value, nullptr); + stream.EndObject(); + } + template + inline void Deserialize(ISerializable::DeserializeStream& stream, StepCurveKeyframe& v, ISerializeModifier* modifier) + { + DESERIALIZE_MEMBER(Time, v.Time); + DESERIALIZE_MEMBER(Value, v.Value); + } + + // LinearCurveKeyframe + + template + inline bool ShouldSerialize(const LinearCurveKeyframe& v, const void* otherObj) + { + if (!otherObj) + return true; + const auto other = (const LinearCurveKeyframe*)otherObj; + return !(v == *other); + } + template + inline void Serialize(ISerializable::SerializeStream& stream, const LinearCurveKeyframe& v, const void* otherObj) + { + stream.StartObject(); + stream.JKEY("Time"); + Serialize(stream, v.Time, nullptr); + stream.JKEY("Value"); + Serialize(stream, v.Value, nullptr); + stream.EndObject(); + } + template + inline void Deserialize(ISerializable::DeserializeStream& stream, LinearCurveKeyframe& v, ISerializeModifier* modifier) + { + DESERIALIZE_MEMBER(Time, v.Time); + DESERIALIZE_MEMBER(Value, v.Value); + } + + // HermiteCurveKeyframe + + template + inline bool ShouldSerialize(const HermiteCurveKeyframe& v, const void* otherObj) + { + if (!otherObj) + return true; + const auto other = (const HermiteCurveKeyframe*)otherObj; + return !(v == *other); + } + template + inline void Serialize(ISerializable::SerializeStream& stream, const HermiteCurveKeyframe& v, const void* otherObj) + { + stream.StartObject(); + stream.JKEY("Time"); + Serialize(stream, v.Time, nullptr); + stream.JKEY("Value"); + Serialize(stream, v.Value, nullptr); + stream.JKEY("TangentIn"); + Serialize(stream, v.TangentIn, nullptr); + stream.JKEY("TangentOut"); + Serialize(stream, v.TangentOut, nullptr); + stream.EndObject(); + } + template + inline void Deserialize(ISerializable::DeserializeStream& stream, HermiteCurveKeyframe& v, ISerializeModifier* modifier) + { + DESERIALIZE_MEMBER(Time, v.Time); + DESERIALIZE_MEMBER(Value, v.Value); + DESERIALIZE_MEMBER(TangentIn, v.TangentIn); + DESERIALIZE_MEMBER(TangentOut, v.TangentOut); + } + + // BezierCurveKeyframe + + template + inline bool ShouldSerialize(const BezierCurveKeyframe& v, const void* otherObj) + { + if (!otherObj) + return true; + const auto other = (const BezierCurveKeyframe*)otherObj; + return !(v == *other); + } + template + inline void Serialize(ISerializable::SerializeStream& stream, const BezierCurveKeyframe& v, const void* otherObj) + { + stream.StartObject(); + stream.JKEY("Time"); + Serialize(stream, v.Time, nullptr); + stream.JKEY("Value"); + Serialize(stream, v.Value, nullptr); + stream.JKEY("TangentIn"); + Serialize(stream, v.TangentIn, nullptr); + stream.JKEY("TangentOut"); + Serialize(stream, v.TangentOut, nullptr); + stream.EndObject(); + } + template + inline void Deserialize(ISerializable::DeserializeStream& stream, BezierCurveKeyframe& v, ISerializeModifier* modifier) + { + DESERIALIZE_MEMBER(Time, v.Time); + DESERIALIZE_MEMBER(Value, v.Value); + DESERIALIZE_MEMBER(TangentIn, v.TangentIn); + DESERIALIZE_MEMBER(TangentOut, v.TangentOut); + } + + // Curve + + template + inline bool ShouldSerialize(const Curve& v, const void* otherObj) + { + if (!otherObj) + return true; + const auto other = (const Curve*)otherObj; + return !(v == *other); + } + template + inline void Serialize(ISerializable::SerializeStream& stream, const Curve& v, const void* otherObj) + { + stream.StartObject(); + stream.JKEY("Keyframes"); + stream.StartArray(); + for (auto& k : v.GetKeyframes()) + Serialize(stream, k, nullptr); + stream.EndArray(); + stream.EndObject(); + } + template + inline void Deserialize(ISerializable::DeserializeStream& stream, Curve& v, ISerializeModifier* modifier) + { + if (!stream.IsObject()) + return; + const auto mKeyframes = SERIALIZE_FIND_MEMBER(stream, "Keyframes"); + if (mKeyframes != stream.MemberEnd()) + { + const auto& keyframesArray = mKeyframes->value.GetArray(); + auto& keyframes = v.GetKeyframes(); + keyframes.Resize(keyframesArray.Size()); + for (rapidjson::SizeType i = 0; i < keyframesArray.Size(); i++) + Deserialize(keyframesArray[i], keyframes[i], modifier); + } + } +} + +// @formatter:on diff --git a/Source/Engine/Core/Math/Color.h b/Source/Engine/Core/Math/Color.h index 3eee6d9f8..5a7986517 100644 --- a/Source/Engine/Core/Math/Color.h +++ b/Source/Engine/Core/Math/Color.h @@ -506,6 +506,14 @@ inline Color operator*(float a, const Color& b) return b * a; } +namespace Math +{ + FORCE_INLINE static bool NearEqual(const Color& a, const Color& b) + { + return Color::NearEqual(a, b); + } +} + template<> struct TIsPODType { diff --git a/Source/Engine/Core/Math/Color32.h b/Source/Engine/Core/Math/Color32.h index 89f64cc35..f848f6f73 100644 --- a/Source/Engine/Core/Math/Color32.h +++ b/Source/Engine/Core/Math/Color32.h @@ -231,6 +231,14 @@ inline Color32 operator*(float a, const Color32& b) return b * a; } +namespace Math +{ + FORCE_INLINE static bool NearEqual(const Color32& a, const Color32& b) + { + return a == b; + } +} + template<> struct TIsPODType { diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index ed19efeae..c5e67c986 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -293,6 +293,14 @@ public: static void Lerp(const Transform& t1, const Transform& t2, float amount, Transform& result); }; +namespace Math +{ + FORCE_INLINE static bool NearEqual(const Transform& a, const Transform& b) + { + return Transform::NearEqual(a, b); + } +} + template<> struct TIsPODType { diff --git a/Source/Engine/Core/Math/Vector4.h b/Source/Engine/Core/Math/Vector4.h index adca955fc..c7771aa67 100644 --- a/Source/Engine/Core/Math/Vector4.h +++ b/Source/Engine/Core/Math/Vector4.h @@ -379,7 +379,7 @@ public: static bool NearEqual(const Vector4& a, const Vector4& b) { - return Math::NearEqual(a.X, b.X) && Math::NearEqual(a.Y, b.Y) & Math::NearEqual(a.Z, b.Z) && Math::NearEqual(a.W, b.W); + return Math::NearEqual(a.X, b.X) && Math::NearEqual(a.Y, b.Y) && Math::NearEqual(a.Z, b.Z) && Math::NearEqual(a.W, b.W); } static bool NearEqual(const Vector4& a, const Vector4& b, float epsilon) diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.h b/Source/Engine/Level/Actors/ModelInstanceActor.h index 63c6c4807..c431429fd 100644 --- a/Source/Engine/Level/Actors/ModelInstanceActor.h +++ b/Source/Engine/Level/Actors/ModelInstanceActor.h @@ -42,7 +42,7 @@ public: /// Sets the material to the entry slot. Can be used to override the material of the meshes using this slot. /// /// The material slot entry index. - /// The material to set.. + /// The material to set. API_FUNCTION() void SetMaterial(int32 entryIndex, MaterialBase* material); /// diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp new file mode 100644 index 000000000..509c2f389 --- /dev/null +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -0,0 +1,335 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "Spline.h" +#include "Engine/Serialization/Serialization.h" +#include "Engine/Animations/CurveSerialization.h" +#include + +Spline::Spline(const SpawnParams& params) + : Actor(params) +{ +} + +void Spline::SetIsLoop(bool value) +{ + if (_loop != value) + { + _loop = value; + UpdateSpline(); + } +} + +Vector3 Spline::GetSplinePoint(float time) const +{ + Transform t; + Curve.Evaluate(t, time, _loop); + return _transform.LocalToWorld(t.Translation); +} + +Vector3 Spline::GetSplineLocalPoint(float time) const +{ + Transform t; + Curve.Evaluate(t, time, _loop); + return t.Translation; +} + +Quaternion Spline::GetSplineOrientation(float time) const +{ + Transform t; + Curve.Evaluate(t, time, _loop); + Quaternion::Multiply(_transform.Orientation, t.Orientation, t.Orientation); + t.Orientation.Normalize(); + return t.Orientation; +} + +Quaternion Spline::GetSplineLocalOrientation(float time) const +{ + Transform t; + Curve.Evaluate(t, time, _loop); + return t.Orientation; +} + +Vector3 Spline::GetSplineScale(float time) const +{ + Transform t; + Curve.Evaluate(t, time, _loop); + return _transform.Scale * t.Scale; +} + +Vector3 Spline::GetSplineLocalScale(float time) const +{ + Transform t; + Curve.Evaluate(t, time, _loop); + return t.Scale; +} + +Transform Spline::GetSplineTransform(float time) const +{ + Transform t; + Curve.Evaluate(t, time, _loop); + return _transform.LocalToWorld(t); +} + +Transform Spline::GetSplineLocalTransform(float time) const +{ + Transform t; + Curve.Evaluate(t, time, _loop); + return t; +} + +Vector3 Spline::GetSplinePoint(int32 index) const +{ + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) + return _transform.LocalToWorld(Curve[index].Value.Translation); +} + +Vector3 Spline::GetSplineLocalPoint(int32 index) const +{ + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) + return Curve[index].Value.Translation; +} + +Transform Spline::GetSplineTransform(int32 index) const +{ + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) + return _transform.LocalToWorld(Curve[index].Value); +} + +Transform Spline::GetSplineLocalTransform(int32 index) const +{ + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) + return Curve[index].Value; +} + +int32 Spline::GetSplinePointsCount() const +{ + return Curve.GetKeyframes().Count(); +} + +float Spline::GetSplineDuration() const +{ + return Curve.GetLength(); +} + +float Spline::GetSplineLength() const +{ + float sum = 0.0f; + for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++) + sum += Vector3::DistanceSquared(Curve[i - 1].Value.Translation * _transform.Scale, Curve[i].Value.Translation * _transform.Scale); + return Math::Sqrt(sum); +} + +namespace +{ + void FindTimeClosestToPoint(const Vector3& point, const Spline::Keyframe& start, const Spline::Keyframe& end, float& bestDistanceSquared, float& bestTime) + { + // TODO: implement sth more analytical than brute-force solution + const int32 slices = 100; + const float step = 1.0f / (float)slices; + const float length = Math::Abs(end.Time - start.Time); + for (int32 i = 0; i <= slices; i++) + { + const float t = (float)i * step; + Transform result; + Spline::Keyframe::Interpolate(start, end, t, length, result); + const float distanceSquared = Vector3::DistanceSquared(point, result.Translation); + if (distanceSquared < bestDistanceSquared) + { + bestDistanceSquared = distanceSquared; + bestTime = start.Time + t * length; + } + } + } +} + +float Spline::GetSplineTimeClosestToPoint(const Vector3& point) const +{ + const int32 pointsCount = Curve.GetKeyframes().Count(); + if (pointsCount == 0) + return 0.0f; + if (pointsCount == 1) + return Curve[0].Time; + const Vector3 localPoint = _transform.WorldToLocal(point); + float bestDistanceSquared = MAX_float; + float bestTime = 0.0f; + for (int32 i = 1; i < pointsCount; i++) + FindTimeClosestToPoint(localPoint, Curve[i - 1], Curve[i], bestDistanceSquared, bestTime); + if (_loop) + FindTimeClosestToPoint(localPoint, Curve[pointsCount - 1], Curve[0], bestDistanceSquared, bestTime); + return bestTime; +} + +Vector3 Spline::GetSplinePointClosestToPoint(const Vector3& point) const +{ + return GetSplinePoint(GetSplineTimeClosestToPoint(point)); +} + +void Spline::GetSplinePoints(Array& points) const +{ + for (auto& e : Curve.GetKeyframes()) + points.Add(_transform.LocalToWorld(e.Value.Translation)); +} + +void Spline::GetSplineLocalPoints(Array& points) const +{ + for (auto& e : Curve.GetKeyframes()) + points.Add(e.Value.Translation); +} + +void Spline::GetSplinePoints(Array& points) const +{ + for (auto& e : Curve.GetKeyframes()) + points.Add(_transform.LocalToWorld(e.Value)); +} + +void Spline::GetSplineLocalPoints(Array& points) const +{ + for (auto& e : Curve.GetKeyframes()) + points.Add(e.Value); +} + +void Spline::ClearSpline() +{ + if (Curve.IsEmpty()) + return; + Curve.Clear(); + UpdateSpline(); +} + +void Spline::AddSplinePoint(const Vector3& point, bool updateSpline) +{ + const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(point)); + Curve.GetKeyframes().Add(k); + if (updateSpline) + UpdateSpline(); +} + +void Spline::AddSplineLocalPoint(const Vector3& point, bool updateSpline) +{ + const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(_transform.WorldToLocal(point))); + Curve.GetKeyframes().Add(k); + if (updateSpline) + UpdateSpline(); +} + +void Spline::AddSplinePoint(const Transform& point, bool updateSpline) +{ + const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, _transform.WorldToLocal(point)); + Curve.GetKeyframes().Add(k); + if (updateSpline) + UpdateSpline(); +} + +void Spline::AddSplineLocalPoint(const Transform& point, bool updateSpline) +{ + const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, point); + Curve.GetKeyframes().Add(k); + if (updateSpline) + UpdateSpline(); +} + +void Spline::UpdateSpline() +{ +} + +void Spline::GetKeyframes(MonoArray* data) +{ + Platform::MemoryCopy(mono_array_addr_with_size(data, sizeof(Keyframe), 0), Curve.GetKeyframes().Get(), sizeof(Keyframe) * Curve.GetKeyframes().Count()); +} + +void Spline::SetKeyframes(MonoArray* data) +{ + const auto count = (int32)mono_array_length(data); + Curve.GetKeyframes().Resize(count, false); + Platform::MemoryCopy(Curve.GetKeyframes().Get(), mono_array_addr_with_size(data, sizeof(Keyframe), 0), sizeof(Keyframe) * count); + UpdateSpline(); +} + +#if USE_EDITOR + +#include "Engine/Debug/DebugDraw.h" + +namespace +{ + void DrawSegment(Spline* spline, int32 start, int32 end, const Color& color, const Transform& transform, bool depthTest) + { + const auto& startKey = spline->Curve[start]; + const auto& endKey = spline->Curve[end]; + const Vector3 startPos = transform.LocalToWorld(startKey.Value.Translation); + const Vector3 startTangent = transform.LocalToWorld(startKey.TangentOut.Translation); + const Vector3 endPos = transform.LocalToWorld(endKey.Value.Translation); + const Vector3 endTangent = transform.LocalToWorld(endKey.TangentIn.Translation); + const float d = (endKey.Time - startKey.Time) / 3.0f; + DEBUG_DRAW_BEZIER(startPos, startPos + startTangent * d, endPos + endTangent * d, endPos, color, 0.0f, depthTest); + } + + void DrawSpline(Spline* spline, const Color& color, const Transform& transform, bool depthTest) + { + const int32 count = spline->Curve.GetKeyframes().Count(); + for (int32 i = 0; i < count; i++) + { + Vector3 p = transform.LocalToWorld(spline->Curve[i].Value.Translation); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(p, 5.0f), color, 0.0f, true); + if (i != 0) + DrawSegment(spline, i - 1, i, color, transform, depthTest); + } + if (spline->GetIsLoop() && count > 1) + DrawSegment(spline, count - 1, 0, color, transform, depthTest); + } +} + +void Spline::OnDebugDraw() +{ + const Color color = GetSplineColor(); + DrawSpline(this, color.AlphaMultiplied(0.7f), _transform, true); + + // Base + Actor::OnDebugDraw(); +} + +void Spline::OnDebugDrawSelected() +{ + const Color color = GetSplineColor(); + DrawSpline(this, color.AlphaMultiplied(0.3f), _transform, false); + DrawSpline(this, color, _transform, true); + + // Base + Actor::OnDebugDrawSelected(); +} + +#endif + +void Spline::Serialize(SerializeStream& stream, const void* otherObj) +{ + // Base + Actor::Serialize(stream, otherObj); + + SERIALIZE_GET_OTHER_OBJ(Spline); + + SERIALIZE_MEMBER(IsLoop, _loop); + SERIALIZE(Curve); +} + +void Spline::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + // Base + Actor::Deserialize(stream, modifier); + + DESERIALIZE_MEMBER(IsLoop, _loop); + DESERIALIZE(Curve); + + // Initialize spline when loading data during gameplay + if (IsDuringPlay()) + { + UpdateSpline(); + } +} + +void Spline::OnEnable() +{ + // Base + Actor::OnEnable(); + + // Initialize spline + UpdateSpline(); +} diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h new file mode 100644 index 000000000..a94804ea7 --- /dev/null +++ b/Source/Engine/Level/Actors/Spline.h @@ -0,0 +1,247 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "../Actor.h" +#include "Engine/Animations/Curve.h" + +/// +/// Spline shape actor that defines spatial curve with utility functions for general purpose usage. +/// +API_CLASS() class FLAXENGINE_API Spline : public Actor +{ +DECLARE_SCENE_OBJECT(Spline); + typedef BezierCurveKeyframe Keyframe; +private: + + bool _loop = false; + +public: + + /// + /// The spline bezier curve points represented as series of transformations in 3D space (with tangents). Points are stored in local-space of the actor. + /// + /// Ensure to call UpdateSpline() after editing curve to reflect the changes. + BezierCurve Curve; + + /// + /// Whether to use spline as closed loop. + /// + API_PROPERTY(Attributes="EditorOrder(0), EditorDisplay(\"Spline\")") + FORCE_INLINE bool GetIsLoop() const + { + return _loop; + } + + /// + /// Whether to use spline as closed loop. + /// + API_PROPERTY() void SetIsLoop(bool value); + +public: + + /// + /// Evaluates the spline curve at the given time and calculates the point location in 3D (world-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve point location (world-space). + API_FUNCTION() Vector3 GetSplinePoint(float time) const; + + /// + /// Evaluates the spline curve at the given time and calculates the point location in 3D (local-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve point location (local-space). + API_FUNCTION() Vector3 GetSplineLocalPoint(float time) const; + + /// + /// Evaluates the spline curve at the given time and calculates the point rotation in 3D (world-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve point rotation (world-space). + API_FUNCTION() Quaternion GetSplineOrientation(float time) const; + + /// + /// Evaluates the spline curve at the given time and calculates the point rotation in 3D (local-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve point rotation (local-space). + API_FUNCTION() Quaternion GetSplineLocalOrientation(float time) const; + + /// + /// Evaluates the spline curve at the given time and calculates the point scale in 3D (world-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve point scale (world-space). + API_FUNCTION() Vector3 GetSplineScale(float time) const; + + /// + /// Evaluates the spline curve at the given time and calculates the point scale in 3D (local-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve point scale (local-space). + API_FUNCTION() Vector3 GetSplineLocalScale(float time) const; + + /// + /// Evaluates the spline curve at the given time and calculates the transformation in 3D (world-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve point transformation (world-space). + API_FUNCTION() Transform GetSplineTransform(float time) const; + + /// + /// Evaluates the spline curve at the given time and calculates the transformation in 3D (local-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve point transformation (local-space). + API_FUNCTION() Transform GetSplineLocalTransform(float time) const; + + /// + /// Evaluates the spline curve at the given index (world-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The curve point location (world-space). + API_FUNCTION() Vector3 GetSplinePoint(int32 index) const; + + /// + /// Evaluates the spline curve at the given index (local-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The curve point location (local-space). + API_FUNCTION() Vector3 GetSplineLocalPoint(int32 index) const; + + /// + /// Evaluates the spline curve at the given index (world-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The curve point transformation (world-space). + API_FUNCTION() Transform GetSplineTransform(int32 index) const; + + /// + /// Evaluates the spline curve at the given index (local-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The curve point transformation (local-space). + API_FUNCTION() Transform GetSplineLocalTransform(int32 index) const; + + /// + /// Gets the amount of points in the spline. + /// + API_PROPERTY() int32 GetSplinePointsCount() const; + + /// + /// Gets the total duration of the spline curve (time of the last point). + /// + API_PROPERTY() float GetSplineDuration() const; + + /// + /// Gets the total length of the spline curve (distance between all the points). + /// + API_PROPERTY() float GetSplineLength() const; + + /// + /// Calculates the closest point to the given location and returns the spline time at that point. + /// + /// The point in world-space to find the spline point that is closest to it. + /// The spline time. + API_FUNCTION() float GetSplineTimeClosestToPoint(const Vector3& point) const; + + /// + /// Calculates the closest point to the given location. + /// + /// The point in world-space to find the spline point that is closest to it. + /// The spline position. + API_FUNCTION() Vector3 GetSplinePointClosestToPoint(const Vector3& point) const; + + /// + /// Gets the spline curve points list (world-space). + /// + /// The result points collection. + API_FUNCTION() void GetSplinePoints(API_PARAM(Out) Array& points) const; + + /// + /// Gets the spline curve points list (local-space). + /// + /// The result points collection. + API_FUNCTION() void GetSplineLocalPoints(API_PARAM(Out) Array& points) const; + + /// + /// Gets the spline curve points list (world-space). + /// + /// The result points collection. + API_FUNCTION() void GetSplinePoints(API_PARAM(Out) Array& points) const; + + /// + /// Gets the spline curve points list (local-space). + /// + /// The result points collection. + API_FUNCTION() void GetSplineLocalPoints(API_PARAM(Out) Array& points) const; + +public: + + /// + /// Clears the spline to be empty. + /// + API_FUNCTION() void ClearSpline(); + + /// + /// Adds the point to the spline curve (at the end). + /// + /// The location of the point to add to the curve (world-space). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void AddSplinePoint(const Vector3& point, bool updateSpline = true); + + /// + /// Adds the point to the spline curve (at the end). + /// + /// The location of the point to add to the curve (local-space). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void AddSplineLocalPoint(const Vector3& point, bool updateSpline = true); + + /// + /// Adds the point to the spline curve (at the end). + /// + /// The transformation of the point to add to the curve (world-space). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void AddSplinePoint(const Transform& point, bool updateSpline = true); + + /// + /// Adds the point to the spline curve (at the end). + /// + /// The transformation of the point to add to the curve (local-space). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void AddSplineLocalPoint(const Transform& point, bool updateSpline = true); + +public: + + /// + /// Updates the spline after it was modified. Recreates the collision and/or any cached state that depends on the spline type. + /// + API_FUNCTION() virtual void UpdateSpline(); + +protected: + +#if USE_EDITOR + virtual Color GetSplineColor() + { + return Color::White; + } +#endif + +private: + + // Internal bindings + API_FUNCTION(NoProxy) void GetKeyframes(MonoArray* data); + API_FUNCTION(NoProxy) void SetKeyframes(MonoArray* data); + +public: + + // [Actor] +#if USE_EDITOR + void OnDebugDraw() override; + void OnDebugDrawSelected() override; +#endif + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void OnEnable() override; +}; diff --git a/Source/Engine/Level/Spline.cs b/Source/Engine/Level/Spline.cs new file mode 100644 index 000000000..46a05cd5d --- /dev/null +++ b/Source/Engine/Level/Spline.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; +using System.Runtime.InteropServices; + +namespace FlaxEngine +{ + partial class Spline + { + private BezierCurve.Keyframe[] _keyframes; + + /// + /// Gets or sets the spline keyframes collection. + /// + [Unmanaged] + [Tooltip("Spline keyframes collection.")] + [EditorOrder(10), EditorDisplay("Spline"), Collection(CanReorderItems = false)] + public BezierCurve.Keyframe[] SplineKeyframes + { + get + { + var count = SplinePointsCount; + if (_keyframes == null || _keyframes.Length != count) + _keyframes = new BezierCurve.Keyframe[count]; +#if !BUILD_RELEASE + if (Marshal.SizeOf(typeof(BezierCurve.Keyframe)) != Transform.SizeInBytes * 3 + sizeof(float)) + throw new Exception("Invalid size of BezierCurve keyframe " + Marshal.SizeOf(typeof(BezierCurve.Keyframe)) + " bytes."); +#endif + Internal_GetKeyframes(__unmanagedPtr, _keyframes); + return _keyframes; + } + set + { + if (value == null) + value = Utils.GetEmptyArray.Keyframe>(); + _keyframes = null; + Internal_SetKeyframes(__unmanagedPtr, value); + } + } + } +} From 075b049bb1d6edefa8a15387505e37b174965d03 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 12:25:20 +0100 Subject: [PATCH 171/419] Fixes. --- Source/Engine/Platform/Base/NetworkBase.cpp | 8 ++-- Source/Engine/Platform/Base/NetworkBase.h | 8 ++-- Source/Engine/Platform/Win32/Win32Network.cpp | 39 ++++--------------- Source/Engine/Platform/Win32/Win32Network.h | 8 ++-- 4 files changed, 20 insertions(+), 43 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index d2b48206e..73b649281 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -21,22 +21,22 @@ bool NetworkBase::DestroySocket(NetworkSocket& socket) return false; } -bool NetworkBase::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value) +bool NetworkBase::SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value) { return false; } -bool NetworkBase::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32 value) +bool NetworkBase::SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value) { return false; } -bool NetworkBase::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value) +bool NetworkBase::GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value) { return false; } -bool NetworkBase::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32* value) +bool NetworkBase::GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value) { return false; } diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 3731bbda6..a568fd215 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -61,10 +61,10 @@ class FLAXENGINE_API NetworkBase static void Exit(); static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); static bool DestroySocket(NetworkSocket& socket); - static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value); - static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32 value); - static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value); - static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32* value); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value); static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool Listen(NetworkSocket& socket, uint16 queueSize); diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 09ae94727..38018f6ff 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -118,38 +118,15 @@ bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto socket.IPVersion = ipv; const uint8 family = socket.IPVersion == NetworkIPVersion::IPv6 ? AF_INET6 : AF_INET; const uint8 stype = socket.Protocol == NetworkProtocolType::Tcp ? SOCK_STREAM : SOCK_DGRAM; - const uint8 proto = socket.Protocol == NetworkProtocolType::Tcp ? IPPROTO_TCP : IPPROTO_UDP; + const uint8 prot = socket.Protocol == NetworkProtocolType::Tcp ? IPPROTO_TCP : IPPROTO_UDP; SOCKET sock; - if ((sock = ::socket(family, stype, proto)) == INVALID_SOCKET) + if ((sock = ::socket(family, stype, prot)) == INVALID_SOCKET) { LOG(Error, "Can't create native socket! Error : {0}", GetLastErrorMessage().Get()); return true; } memcpy(socket.Data, &sock, sizeof sock); - /* - DWORD dw = 0; - if (family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&dw, sizeof dw) == SOCKET_ERROR) - { - LOG(Warning, "System does not support dual stacking socket! Error : {0}", GetLastErrorMessage().Get()); - } - - unsigned long value = 1; - if (settings.ReuseAddress && setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof value) == SOCKET_ERROR) - { - LOG(Warning, "Can't set socket option to SO_REUSEADDR! Error : {0}", GetLastErrorMessage().Get()); - } - - if (settings.Broadcast && settings.Protocol == NetworkProtocolType::Udp) - { - if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&value, sizeof value) == SOCKET_ERROR) - { - LOG(Warning, "Can't set socket option to SO_BROADCAST! Error : {0}", GetLastErrorMessage().Get()); - } - } - else if (settings.Broadcast) - LOG(Warning, "Can't set socket option to SO_BROADCAST! The socket must use UDP protocol. Error : {0}", GetLastErrorMessage().Get()); -*/ unsigned long value = 1; if (ioctlsocket(sock, FIONBIO, &value) == SOCKET_ERROR) { @@ -175,13 +152,13 @@ bool Win32Network::DestroySocket(NetworkSocket& socket) return true; } -bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value) +bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value) { const int32 v = value; return SetSocketOption(socket, option, v); } -bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32 value) +bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value) { int32 optlvl = 0; int32 optnme = 0; @@ -202,7 +179,7 @@ bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& o SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) - if (setsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)value, sizeof int32) == SOCKET_ERROR) + if (setsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)&value, sizeof value) == SOCKET_ERROR) { LOG(Warning, "Unable to set socket option ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); return true; @@ -210,15 +187,15 @@ bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption& o return false; } -bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value) +bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value) { int32 v; const bool status = GetSocketOption(socket, option, &v); - *value = v; + *value = v == 1 ? true : false; return status; } -bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32* value) +bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value) { int32 optlvl = 0; int32 optnme = 0; diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index 805f68c0a..8cbcf57ec 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -17,10 +17,10 @@ public: static void Exit(); static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); static bool DestroySocket(NetworkSocket& socket); - static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool value); - static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32 value); - static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, bool* value); - static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption& option, int32* value); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value); + static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value); + static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value); static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool Listen(NetworkSocket& socket, uint16 queueSize); From c0c2da34d738458fc92fe50f15793f61a3d2d92b Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Mon, 25 Jan 2021 12:37:42 +0100 Subject: [PATCH 172/419] Specified variants --- Source/Engine/Visject/ShaderGraph.cpp | 6 +++--- Source/Engine/Visject/VisjectGraph.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 7545e7c96..5e133eb8c 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -405,9 +405,9 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) // Remap case 48: { - auto inVal = tryGetValue(node->GetBox(0), node->Values[0]); - auto rangeA = tryGetValue(node->GetBox(1), node->Values[1]); - auto rangeB = tryGetValue(node->GetBox(2), node->Values[2]); + auto inVal = tryGetValue(node->GetBox(0), node->Values[0].AsFloat); + auto rangeA = tryGetValue(node->GetBox(1), node->Values[1].AsVector2()); + auto rangeB = tryGetValue(node->GetBox(2), node->Values[2].AsVector2()); // Clamp value? if (node->Values[3].AsBool) diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index dfd5c9fdf..e4e8f69cf 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -378,7 +378,7 @@ void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value) const Vector2 rangeA = tryGetValue(node->GetBox(1), node->Values[1]).AsVector2(); const Vector2 rangeB = tryGetValue(node->GetBox(2), node->Values[2]).AsVector2(); - // Use clamp? + // Clamp value? if (node->Values[3].AsBool) { value = Math::Clamp(rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X), rangeB.X, rangeB.Y); From dae366f04ca942112a3d2fa305a65bcacc800976 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 12:40:08 +0100 Subject: [PATCH 173/419] Cleanup. --- Source/Engine/Platform/Win32/Win32Network.cpp | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 38018f6ff..68a973547 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -133,8 +133,6 @@ bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto LOG(Error, "Can't set socket to NON-BLOCKING type! Error : {0}", GetLastErrorMessage().Get()); return true; // Support using blocking socket , need to test it } - //DEBUG - LOG(Info, "New socket created : {0}", sock); //TODO: DEBUG return false; } @@ -144,8 +142,6 @@ bool Win32Network::DestroySocket(NetworkSocket& socket) if (sock != INVALID_SOCKET) { closesocket(sock); - //DEBUG - LOG(Info, "Deleting socket : {0}", sock); //TODO: DEBUG return false; } LOG(Warning, "Unable to delete socket INVALID_SOCKET! Socket : {0}", sock); @@ -251,8 +247,6 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) LOG(Error, "Unable to bind socket! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetLastErrorMessage().Get()); return true; } - //DEBUG - LOG(Info, "Binding socket! Socket : {0} Address : {1} Port : {2}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get()); return false; } @@ -282,7 +276,6 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net } memcpy(newSock.Data, &sock, sizeof sock); memcpy(newEndPoint.Data, &addr, GetAddrSize(addr)); - newSock.Protocol = serverSock.Protocol; newSock.IPVersion = serverSock.IPVersion; if (CreateEndPointFromAddr(&addr, newEndPoint)) @@ -335,9 +328,6 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, return size; } -/* - * TODO: handle size == 0 when there is a shutdown - */ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint) { uint32 size; @@ -364,15 +354,11 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer return size; } -// if address is null, it's ADDR_ANY bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable) { int status; addrinfoW hints; addrinfoW* info; - //DEBUG - LOG(Info, "Searching available adresses for {0} : {1}", address == nullptr ? String("nullptr").Get() : address->Get(), - port == nullptr ? String("nullptr").Get() : port->Get()); memset(&hints, 0, sizeof hints); hints.ai_family = ipv == NetworkIPVersion::IPv6 ? AF_INET6 : ipv == NetworkIPVersion::IPv4 ? AF_INET : AF_UNSPEC; hints.ai_flags |= AI_ADDRCONFIG; @@ -381,8 +367,6 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio hints.ai_flags = AI_PASSIVE; // consider using NUMERICHOST/NUMERICSERV if address is a valid Ipv4 or IPv6 so we can skip some look up ( potentially slow when resolving host names ) - // can *paddr works ? - // paddr = nullptr don't work with this func if ((status = GetAddrInfoW(address == nullptr ? nullptr : address->Get(), port->Get(), &hints, &info)) != 0) { LOG(Error, "Unable to query info for address : {0} Error : {1}", address != nullptr ? address->Get() : String("ANY").Get(), gai_strerror(status)); @@ -395,10 +379,6 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio return true; } - //DEBUG - PrintAddrFromInfo(*info); - - // We are taking the first addr in the linked list if (CreateEndPointFromAddr(info->ai_addr, endPoint)) { FreeAddrInfoW(info); @@ -406,8 +386,6 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio } FreeAddrInfoW(info); - //DEBUG - LOG(Info, "Address found : {0} : {1}", endPoint.Address, endPoint.Port); return false; } From baa273f589f5f144be3fe8334e361c8314794efe Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 13:07:02 +0100 Subject: [PATCH 174/419] Remove old comment. --- Source/Engine/Platform/Win32/Win32Network.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 68a973547..ac43f29fe 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -241,7 +241,6 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) const uint16 size = endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof sockaddr_in6 : sizeof sockaddr_in; const sockaddr* addr = (sockaddr*)endPoint.Data; - LOG(Info, "BIND : EndPoint family : {0}", addr->sa_family); if (bind(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) { LOG(Error, "Unable to bind socket! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetLastErrorMessage().Get()); From 5bc1773dc5755db2b05d284cd0f9d441ce2f906b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 13:13:57 +0100 Subject: [PATCH 175/419] Refactor sock option translation. --- Source/Engine/Platform/Win32/Win32Network.cpp | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index ac43f29fe..18db01917 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -7,7 +7,7 @@ #include #include -#define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) if (option == OPTENUM) { optlvl = OPTLEVEL; optnme = OPTNAME;} +#define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) case OPTENUM: optlvl = OPTLEVEL; optnme = OPTNAME; break; static_assert(sizeof NetworkSocket::Data >= sizeof SOCKET, "NetworkSocket::Data is not big enough to contains SOCKET !"); static_assert(sizeof NetworkEndPoint::Data >= sizeof sockaddr_in6, "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !"); @@ -159,21 +159,24 @@ bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption op int32 optlvl = 0; int32 optnme = 0; - SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) - SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) - SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) - SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) - SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) - SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) - SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) - SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) - SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) - SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) - SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) - SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) - SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) - SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) - SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) + switch (option) + { + SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) + SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) + SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) + SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) + SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) + SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) + SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) + SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) + SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) + SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) + SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) + SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) + SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) + SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) + SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) + } if (setsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)&value, sizeof value) == SOCKET_ERROR) { @@ -195,22 +198,24 @@ bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption op { int32 optlvl = 0; int32 optnme = 0; - - SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) - SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) - SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) - SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) - SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) - SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) - SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) - SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) - SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) - SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) - SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) - SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) - SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) - SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) - + + switch (option) + { + SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) + SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) + SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) + SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) + SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) + SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) + SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) + SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) + SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) + SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) + SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) + SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) + SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) + SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) + } int32 size; if (getsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)value, &size) == SOCKET_ERROR) { From 206461e80ec492912cf9d8023260d016959be8ab Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 13:17:04 +0100 Subject: [PATCH 176/419] Refactor duplicate code. --- Source/Engine/Platform/Win32/Win32Network.cpp | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 18db01917..a7b26a0ca 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -7,7 +7,7 @@ #include #include -#define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) case OPTENUM: optlvl = OPTLEVEL; optnme = OPTNAME; break; +#define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) case OPTENUM: *level = OPTLEVEL; *name = OPTNAME; break; static_assert(sizeof NetworkSocket::Data >= sizeof SOCKET, "NetworkSocket::Data is not big enough to contains SOCKET !"); static_assert(sizeof NetworkEndPoint::Data >= sizeof sockaddr_in6, "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !"); @@ -100,6 +100,28 @@ static void PrintAddrFromInfo(addrinfoW& info) } } +static void TranslateSockOptToNative(NetworkSocketOption option, int32* level, int32* name) +{ + switch (option) + { + SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) + SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) + SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) + SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) + SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) + SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) + SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) + SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) + SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) + SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) + SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) + SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) + SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) + SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) + SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) + } +} + bool Win32Network::Init() { if (WSAStartup(MAKEWORD(2, 0), &_wsaData) != 0) @@ -159,24 +181,7 @@ bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption op int32 optlvl = 0; int32 optnme = 0; - switch (option) - { - SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) - SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) - SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) - SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) - SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) - SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) - SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) - SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) - SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) - SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) - SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) - SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) - SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) - SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) - SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) - } + TranslateSockOptToNative(option, &optlvl, &optnme); if (setsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)&value, sizeof value) == SOCKET_ERROR) { @@ -199,23 +204,8 @@ bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption op int32 optlvl = 0; int32 optnme = 0; - switch (option) - { - SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) - SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) - SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) - SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) - SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) - SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) - SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) - SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) - SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) - SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) - SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) - SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) - SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) - SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) - } + TranslateSockOptToNative(option, &optlvl, &optnme); + int32 size; if (getsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)value, &size) == SOCKET_ERROR) { From 6dd699cbab64d9d3bd50eec76d7a652b330fbdef Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 13:19:50 +0100 Subject: [PATCH 177/419] Remove dead code. --- Source/Engine/Platform/Win32/Win32Network.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index a7b26a0ca..c594aa80d 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -235,7 +235,6 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) } const uint16 size = endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof sockaddr_in6 : sizeof sockaddr_in; - const sockaddr* addr = (sockaddr*)endPoint.Data; if (bind(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) { LOG(Error, "Unable to bind socket! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetLastErrorMessage().Get()); From 27a62f4411919d7fab1c43fc31016f3cbb799825 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 13:32:17 +0100 Subject: [PATCH 178/419] Add error. --- Source/Engine/Platform/Win32/Win32Network.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index c594aa80d..f50b042e0 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -65,7 +65,12 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) paddr = &((sockaddr_in6*)addr)->sin6_addr; else if (addr->sa_family == AF_INET) paddr = &((sockaddr_in*)addr)->sin_addr; - + else + { + LOG(Error, "Unable to create endpoint, sockaddr must be INET or INET6!"); + return true; + } + char ip[INET6_ADDRSTRLEN]; if (inet_ntop(addr->sa_family, paddr, ip, INET6_ADDRSTRLEN) == nullptr) { From c2af9164d6d9cd92676b4bc4d92e51ba60a48d16 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 13:34:18 +0100 Subject: [PATCH 179/419] Typo. --- Source/Engine/Platform/Win32/Win32Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index f50b042e0..d33df3ace 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -4,7 +4,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Collections/Array.h" #include -#include +#include #include #define SOCKOPT(OPTENUM, OPTLEVEL, OPTNAME) case OPTENUM: *level = OPTLEVEL; *name = OPTNAME; break; From a32658beea2fc9f1f4fe939faa5d32f4b443dbd6 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 14:02:55 +0100 Subject: [PATCH 180/419] Add GetErrorMessage(). --- Source/Engine/Platform/Win32/Win32Network.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index d33df3ace..feaa3f6a0 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -21,11 +21,11 @@ static WSAData _wsaData; * Even if dualstacking is enabled it's not possible to bind an Ipv4mappedIPv6 endpoint. windows limitation */ -static String GetLastErrorMessage() +static String GetErrorMessage(int error) { wchar_t* s = nullptr; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, WSAGetLastError(), + nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&s), 0, nullptr); String str(s); @@ -33,6 +33,11 @@ static String GetLastErrorMessage() return str; } +static String GetLastErrorMessage() +{ + return GetErrorMessage(WSAGetLastError()); +} + static int GetAddrSize(const sockaddr& addr) { return addr.sa_family == AF_INET6 ? sizeof sockaddr_in6 : sizeof sockaddr_in; From 8e2a8c045c17de45e33b82249b75e6ee331a7eed Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 17:51:52 +0100 Subject: [PATCH 181/419] Get rid off getnameinfo. --- Source/Engine/Platform/Win32/Win32Network.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index feaa3f6a0..48942f29b 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -67,12 +67,18 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) } void* paddr; if (addr->sa_family == AF_INET6) + { paddr = &((sockaddr_in6*)addr)->sin6_addr; + port = ntohs(((sockaddr_in6*)addr)->sin6_port); + } else if (addr->sa_family == AF_INET) + { paddr = &((sockaddr_in*)addr)->sin_addr; + port = ntohs(((sockaddr_in*)addr)->sin_port); + } else { - LOG(Error, "Unable to create endpoint, sockaddr must be INET or INET6!"); + LOG(Error, "Unable to create endpoint, sockaddr must be INET or INET6! Family : {0}", addr->sa_family); return true; } @@ -83,7 +89,9 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) return true; } endPoint.Address = String(ip); - endPoint.Port = String(service); + char strPort[6]; + _itoa(port, strPort, 10); + endPoint.Port = String(strPort); endPoint.IPVersion = GetIPVersionFromAddr(*addr); memcpy(endPoint.Data, addr, size); return false; From 330a588260634b7ef7a6d97245d06568a267fdcf Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 17:52:11 +0100 Subject: [PATCH 182/419] Get rid off getnameinfo. --- Source/Engine/Platform/Win32/Win32Network.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 48942f29b..77c0baf4d 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -57,14 +57,8 @@ static NetworkIPVersion GetIPVersionFromAddr(const sockaddr& addr) // getnameinfo return a name ( like JimiPC ), not the ip! static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) { - uint16 size = GetAddrSize(*addr); - char name[100]; - char service[20]; - if (getnameinfo(addr, size, name, sizeof name, service, sizeof service, 0) != 0) - { - LOG(Error, "Unable to extract info from sockaddr! Error : {0}", GetLastErrorMessage().Get()); - return true; - } + uint32 size = GetAddrSize(*addr); + uint16 port; void* paddr; if (addr->sa_family == AF_INET6) { From 67a824607797706f2058aa70fb629f0589a90103 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 17:54:16 +0100 Subject: [PATCH 183/419] Revamp IsReadable & IsWriteable. --- Source/Engine/Platform/Base/NetworkBase.cpp | 7 +++- Source/Engine/Platform/Base/NetworkBase.h | 3 +- Source/Engine/Platform/Win32/Win32Network.cpp | 42 ++++++++++++++++--- Source/Engine/Platform/Win32/Win32Network.h | 3 +- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index 73b649281..e7b700911 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -61,11 +61,16 @@ bool NetworkBase::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Netw return false; } -bool NetworkBase::IsReadable(NetworkSocket& socket, uint64* size) +bool NetworkBase::IsReadable(NetworkSocket& socket) { return false; } +bool NetworkBase::IsWriteable(NetworkSocket& socket) +{ + return true; +} + int32 NetworkBase::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) { return 0; diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index a568fd215..295100ffe 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -69,7 +69,8 @@ class FLAXENGINE_API NetworkBase static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool Listen(NetworkSocket& socket, uint16 queueSize); static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); - static bool IsReadable(NetworkSocket& socket, uint64* size); + static bool IsReadable(NetworkSocket& socket); + static bool IsWriteable(NetworkSocket& socket); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 77c0baf4d..e0f82de6b 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -288,15 +288,45 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net return false; } -bool Win32Network::IsReadable(NetworkSocket& socket, uint64* size) +bool Win32Network::IsReadable(NetworkSocket& socket) { - unsigned long value; - if (ioctlsocket(*(SOCKET*)socket.Data, FIONREAD, &value) != 0) + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(*(SOCKET*)socket.Data, &readfds); + timeval t; + t.tv_sec = 0; + t.tv_usec = 0; + if (select(0, &readfds, nullptr, nullptr, &t) == SOCKET_ERROR) { - LOG(Error, "Unable to query socket for readability! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); - return true; + int error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + return false; + LOG(Error, "Unable to check readability of socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); + return false; } - *size = value; + if (FD_ISSET(*(SOCKET*)socket.Data, &readfds)) + return true; + return false; +} + +bool Win32Network::IsWriteable(NetworkSocket& socket) +{ + fd_set writefds; + FD_ZERO(&writefds); + FD_SET(*(SOCKET*)socket.Data, &writefds); + timeval t; + t.tv_sec = 0; + t.tv_usec = 0; + if (select(0, nullptr, &writefds, nullptr, &t) == SOCKET_ERROR) + { + int error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + return false; + LOG(Error, "Unable to check writeability of socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); + return false; + } + if (FD_ISSET(*(SOCKET*)socket.Data, &writefds)) + return true; return false; } diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index 8cbcf57ec..bbf15a4c4 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -25,7 +25,8 @@ public: static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); static bool Listen(NetworkSocket& socket, uint16 queueSize); static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); - static bool IsReadable(NetworkSocket& socket, uint64* size); + static bool IsReadable(NetworkSocket& socket); + static bool IsWriteable(NetworkSocket& socket); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); From bbabd099835f8b32e204a1f550605361bb82bf76 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 17:54:52 +0100 Subject: [PATCH 184/419] Non-Blocking handling. --- Source/Engine/Platform/Win32/Win32Network.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index e0f82de6b..cff163f2a 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -232,7 +232,10 @@ bool Win32Network::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoin const uint16 size = GetAddrSizeFromEP(endPoint); if (connect(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) { - LOG(Error, "Unable to connect socket to address! Socket : {0} Address : {1} Port : {2}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get()); + int error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + return false; + LOG(Error, "Unable to connect socket to address! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetErrorMessage(error).Get()); return true; } return false; @@ -370,7 +373,10 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer { if ((size = recv(*(SOCKET*)socket.Data, (char*)buffer, bufferSize, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage().Get()); + const int32 error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + return 0; + LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetErrorMessage(error).Get()); return -1; } } From 029d9acaa2f66702dc87b7a9dade2581bf996124 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 25 Jan 2021 17:55:22 +0100 Subject: [PATCH 185/419] Fixes. --- Source/Engine/Platform/Win32/Win32Network.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index cff163f2a..d2e7e0944 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -276,17 +276,18 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net return true; } SOCKET sock; - sockaddr addr; - if ((sock = accept(*(SOCKET*)serverSock.Data, &addr, nullptr)) == INVALID_SOCKET) + sockaddr_in6 addr; + int32 size = sizeof sockaddr_in6; + if ((sock = accept(*(SOCKET*)serverSock.Data, (sockaddr*)&addr, &size)) == INVALID_SOCKET) { LOG(Warning, "Unable to accept incoming connection! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetLastErrorMessage().Get()); return true; } memcpy(newSock.Data, &sock, sizeof sock); - memcpy(newEndPoint.Data, &addr, GetAddrSize(addr)); + memcpy(newEndPoint.Data, &addr, size); newSock.Protocol = serverSock.Protocol; newSock.IPVersion = serverSock.IPVersion; - if (CreateEndPointFromAddr(&addr, newEndPoint)) + if (CreateEndPointFromAddr((sockaddr*)&addr, newEndPoint)) return true; return false; } @@ -335,7 +336,7 @@ bool Win32Network::IsWriteable(NetworkSocket& socket) int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) { - if (socket.IPVersion != endPoint->IPVersion) + if (endPoint != nullptr && socket.IPVersion != endPoint->IPVersion) { LOG(Error, "Unable to send data, Socket.IPVersion != EndPoint.IPVersion! Socket : {0}", *(SOCKET*)socket.Data); return 0; From f331191e3deb848547de738d78649ef25feab96e Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Mon, 25 Jan 2021 19:31:12 +0100 Subject: [PATCH 186/419] Refactored --- Source/Engine/Visject/ShaderGraph.cpp | 15 +++++---------- Source/Engine/Visject/VisjectGraph.cpp | 9 +++------ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 5e133eb8c..c447961c2 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -405,17 +405,12 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) // Remap case 48: { - auto inVal = tryGetValue(node->GetBox(0), node->Values[0].AsFloat); - auto rangeA = tryGetValue(node->GetBox(1), node->Values[1].AsVector2()); - auto rangeB = tryGetValue(node->GetBox(2), node->Values[2].AsVector2()); + const auto inVal = tryGetValue(node->GetBox(0), node->Values[0].AsFloat); + const auto rangeA = tryGetValue(node->GetBox(1), node->Values[1].AsVector2()); + const auto rangeB = tryGetValue(node->GetBox(2), node->Values[2].AsVector2()); - // Clamp value? - if (node->Values[3].AsBool) - { - value = writeLocal(ValueType::Float, String::Format(TEXT("clamp({2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x), {2}.x, {2}.y)"), inVal.Value, rangeA.Value, rangeB.Value), node); - break; - } - value = writeLocal(ValueType::Float, String::Format(TEXT("{2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x)"), inVal.Value, rangeA.Value, rangeB.Value), node); + const auto mapFunc = String::Format(TEXT("{2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x)"), inVal.Value, rangeA.Value, rangeB.Value); + value = writeLocal(ValueType::Float, node->Values[3].AsBool ? String::Format(TEXT("clamp({0}, {1}.x, {1}.y)"), mapFunc, rangeB.Value) : mapFunc, node); break; } default: diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index e4e8f69cf..b5ee3e29d 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -378,13 +378,10 @@ void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value) const Vector2 rangeA = tryGetValue(node->GetBox(1), node->Values[1]).AsVector2(); const Vector2 rangeB = tryGetValue(node->GetBox(2), node->Values[2]).AsVector2(); + auto mapFunc = rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X); + // Clamp value? - if (node->Values[3].AsBool) - { - value = Math::Clamp(rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X), rangeB.X, rangeB.Y); - break; - } - value = rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X); + value = node->Values[3].AsBool ? Math::Clamp(mapFunc, rangeB.X, rangeB.Y) : mapFunc; break; } default: From 5845bc3fca6908b9f2d679a90c5c3e819d461231 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Mon, 25 Jan 2021 22:03:30 +0100 Subject: [PATCH 187/419] Added Spheremask node --- Source/Editor/Surface/Archetypes/Material.cs | 29 +++++++++++++++++++ .../MaterialGenerator.Material.cpp | 21 ++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index ee70c2b5e..43110a9de 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -672,6 +672,35 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3), } }, + new NodeArchetype + { + TypeID = 28, + Title = "Sphere Mask", + Description = "Creates a sphere mask", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(150, 100), + ConnectionsHints = ConnectionsHint.Vector, + IndependentBoxes = new[] + { + 0, + 1 + }, + DefaultValues = new object[] + { + 0.3f, + 0.5f, + false + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "A", true, null, 0), + NodeElementArchetype.Factory.Input(1, "B", true, null, 1), + NodeElementArchetype.Factory.Input(2, "Radius", true, typeof(float), 2, 0), + NodeElementArchetype.Factory.Input(3, "Hardness", true, typeof(float), 3, 1), + NodeElementArchetype.Factory.Input(4, "Invert", true, typeof(bool), 4, 2), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), + } + }, }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index c4d7c39e5..44b465ad7 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -364,6 +364,27 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, dotB1.Value, dotB2.Value, center.Value), node); break; } + // SphereMask + case 28: + { + + Value a = tryGetValue(node->GetBox(0), 0, Value::Zero); + Value b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type); + + Value radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat(); + Value hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat(); + Value invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool(); + + // Get distance and apply radius + auto x1 = writeLocal(ValueType::Float, String::Format(TEXT("distance({0},{1}) * (1 / {2})"), a.Value, b.Value, radius.Value), node); + + // Apply hardness, use 0.991 since any value above will result in harsh aliasing + auto x2 = writeLocal(ValueType::Float, String::Format(TEXT("saturate((1 - {0}) * (1 / (1 - clamp({1}, 0, 0.991f))))"), x1.Value, hardness.Value), node); + + value = writeLocal(ValueType::Float, String::Format(TEXT("{0} ? (1 - {1}) : {1}"), invert.Value, x2.Value), node); + + break; + } default: break; } From 602fb44a23134e13c1b03738b48d597948225127 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Mon, 25 Jan 2021 22:30:55 +0100 Subject: [PATCH 188/419] Formatting --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 44b465ad7..79a18bde4 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -367,10 +367,8 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // SphereMask case 28: { - Value a = tryGetValue(node->GetBox(0), 0, Value::Zero); Value b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type); - Value radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat(); Value hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat(); Value invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool(); @@ -378,11 +376,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Get distance and apply radius auto x1 = writeLocal(ValueType::Float, String::Format(TEXT("distance({0},{1}) * (1 / {2})"), a.Value, b.Value, radius.Value), node); - // Apply hardness, use 0.991 since any value above will result in harsh aliasing + // Apply hardness, use 0.991 as max since any value above will result in harsh aliasing auto x2 = writeLocal(ValueType::Float, String::Format(TEXT("saturate((1 - {0}) * (1 / (1 - clamp({1}, 0, 0.991f))))"), x1.Value, hardness.Value), node); - + value = writeLocal(ValueType::Float, String::Format(TEXT("{0} ? (1 - {1}) : {1}"), invert.Value, x2.Value), node); - break; } default: From 5b4dbfa121c6471d4d9d3453022ee002f0cceb77 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 25 Jan 2021 23:52:07 +0100 Subject: [PATCH 189/419] Fix name (was causing docs build warning) --- .../Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs | 2 +- Source/Editor/CustomEditors/Editors/EnumEditor.cs | 6 +++--- Source/Editor/CustomEditors/Elements/EnumElement.cs | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs b/Source/Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs index 86dd45812..cf649552a 100644 --- a/Source/Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorStaticFlagsEditor.cs @@ -23,7 +23,7 @@ namespace FlaxEditor.CustomEditors.Editors /// protected override void OnValueChanged() { - var value = (StaticFlags)element.EnumComboBox.EnumTypeValue; + var value = (StaticFlags)element.ComboBox.EnumTypeValue; // If selected is single actor that has children, ask if apply flags to the sub objects as well if (Values.IsSingleObject && (StaticFlags)Values[0] != value && ParentEditor.Values[0] is Actor actor && actor.HasChildren) diff --git a/Source/Editor/CustomEditors/Editors/EnumEditor.cs b/Source/Editor/CustomEditors/Editors/EnumEditor.cs index e277324e5..7954e18bf 100644 --- a/Source/Editor/CustomEditors/Editors/EnumEditor.cs +++ b/Source/Editor/CustomEditors/Editors/EnumEditor.cs @@ -40,7 +40,7 @@ namespace FlaxEditor.CustomEditors.Editors { var enumType = Values.Type.Type != typeof(object) || Values[0] == null ? TypeUtils.GetType(Values.Type) : Values[0].GetType(); element = layout.Enum(enumType, null, mode); - element.EnumComboBox.ValueChanged += OnValueChanged; + element.ComboBox.ValueChanged += OnValueChanged; } } @@ -49,7 +49,7 @@ namespace FlaxEditor.CustomEditors.Editors /// protected virtual void OnValueChanged() { - SetValue(element.EnumComboBox.EnumTypeValue); + SetValue(element.ComboBox.EnumTypeValue); } /// @@ -63,7 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors } else { - element.EnumComboBox.EnumTypeValue = Values[0]; + element.ComboBox.EnumTypeValue = Values[0]; } } } diff --git a/Source/Editor/CustomEditors/Elements/EnumElement.cs b/Source/Editor/CustomEditors/Elements/EnumElement.cs index 27b4dfdb9..be4085f40 100644 --- a/Source/Editor/CustomEditors/Elements/EnumElement.cs +++ b/Source/Editor/CustomEditors/Elements/EnumElement.cs @@ -16,7 +16,7 @@ namespace FlaxEditor.CustomEditors.Elements /// /// The combo box used to show enum values. /// - public EnumComboBox EnumComboBox; + public EnumComboBox ComboBox; /// /// Initializes a new instance of the class. @@ -26,10 +26,10 @@ namespace FlaxEditor.CustomEditors.Elements /// The formatting mode. public EnumElement(Type type, EnumComboBox.BuildEntriesDelegate customBuildEntriesDelegate = null, EnumDisplayAttribute.FormatMode formatMode = EnumDisplayAttribute.FormatMode.Default) { - EnumComboBox = new EnumComboBox(type, customBuildEntriesDelegate, formatMode); + ComboBox = new EnumComboBox(type, customBuildEntriesDelegate, formatMode); } /// - public override Control Control => EnumComboBox; + public override Control Control => ComboBox; } } From ca32d0d8259cea4f0335f030db683e3632eea6dc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 25 Jan 2021 23:52:29 +0100 Subject: [PATCH 190/419] Add Mesh::UpdateMesh methods similar to C# API --- Source/Engine/Graphics/Models/Mesh.cpp | 247 +++++++++++++------------ Source/Engine/Graphics/Models/Mesh.h | 38 +++- 2 files changed, 169 insertions(+), 116 deletions(-) diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 0ba51b2d7..444d60540 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -10,6 +10,127 @@ #include "Engine/Serialization/MemoryReadStream.h" #include +namespace +{ + template + bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, Vector3* vertices, IndexType* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors) + { + auto model = mesh->GetModel(); + CHECK_RETURN(model && model->IsVirtual(), true); + CHECK_RETURN(triangles && vertices, true); + + // Pack mesh data into vertex buffers + Array vb1; + Array vb2; + vb1.Resize(vertexCount); + if (normals) + { + if (tangents) + { + for (uint32 i = 0; i < vertexCount; i++) + { + const Vector3 normal = normals[i]; + const Vector3 tangent = tangents[i]; + + // Calculate bitangent sign + Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent)); + byte sign = static_cast(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0); + + // Set tangent frame + vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign); + vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0); + } + } + else + { + for (uint32 i = 0; i < vertexCount; i++) + { + const Vector3 normal = normals[i]; + + // Calculate tangent + Vector3 c1 = Vector3::Cross(normal, Vector3::UnitZ); + Vector3 c2 = Vector3::Cross(normal, Vector3::UnitY); + Vector3 tangent; + if (c1.LengthSquared() > c2.LengthSquared()) + tangent = c1; + else + tangent = c2; + + // Calculate bitangent sign + Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent)); + byte sign = static_cast(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0); + + // Set tangent frame + vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign); + vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0); + } + } + } + else + { + // Set default tangent frame + const auto n = Float1010102(Vector3::UnitZ); + const auto t = Float1010102(Vector3::UnitX); + for (uint32 i = 0; i < vertexCount; i++) + { + vb1[i].Normal = n; + vb1[i].Tangent = t; + } + } + if (uvs) + { + for (uint32 i = 0; i < vertexCount; i++) + vb1[i].TexCoord = Half2(uvs[i]); + } + else + { + auto v = Half2(0, 0); + for (uint32 i = 0; i < vertexCount; i++) + vb1[i].TexCoord = v; + } + { + auto v = Half2(0, 0); + for (uint32 i = 0; i < vertexCount; i++) + vb1[i].LightmapUVs = v; + } + if (colors) + { + vb2.Resize(vertexCount); + for (uint32 i = 0; i < vertexCount; i++) + vb2[i].Color = colors[i]; + } + + return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vertices, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, triangles); + } + + template + bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) + { + ASSERT((uint32)mono_array_length(verticesObj) >= vertexCount); + ASSERT((uint32)mono_array_length(trianglesObj) / 3 >= triangleCount); + auto vertices = (Vector3*)(void*)mono_array_addr_with_size(verticesObj, sizeof(Vector3), 0); + auto triangles = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0); + const auto normals = normalsObj ? (Vector3*)(void*)mono_array_addr_with_size(normalsObj, sizeof(Vector3), 0) : nullptr; + const auto tangents = tangentsObj ? (Vector3*)(void*)mono_array_addr_with_size(tangentsObj, sizeof(Vector3), 0) : nullptr; + const auto uvs = uvObj ? (Vector2*)(void*)mono_array_addr_with_size(uvObj, sizeof(Vector2), 0) : nullptr; + const auto colors = colorsObj ? (Color32*)(void*)mono_array_addr_with_size(colorsObj, sizeof(Color32), 0) : nullptr; + return UpdateMesh(mesh, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); + } + + template + bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MonoArray* trianglesObj) + { + const auto model = mesh->GetModel(); + ASSERT(model && model->IsVirtual() && trianglesObj); + + // Get buffer data + ASSERT((int32)mono_array_length(trianglesObj) / 3 >= triangleCount); + auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0); + + return mesh->UpdateTriangles(triangleCount, ib); + } +} + bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices) { Unload(); @@ -31,6 +152,16 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* return failed; } +bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint16* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors) +{ + return ::UpdateMesh(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); +} + +bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals, Vector3* tangents, Vector2* uvs, Color32* colors) +{ + return ::UpdateMesh(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); +} + bool Mesh::UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices) { // Cache data @@ -384,108 +515,9 @@ ScriptingObject* Mesh::GetParentModel() return _model; } -template -bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) -{ - auto model = mesh->GetModel(); - ASSERT(model && model->IsVirtual() && verticesObj && trianglesObj); - - // Get buffers data - ASSERT((uint32)mono_array_length(verticesObj) >= vertexCount); - ASSERT((uint32)mono_array_length(trianglesObj) / 3 >= triangleCount); - auto vb0 = (Vector3*)(void*)mono_array_addr_with_size(verticesObj, sizeof(Vector3), 0); - auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0); - Array vb1; - Array vb2; - vb1.Resize(vertexCount); - if (normalsObj) - { - const auto normals = (Vector3*)(void*)mono_array_addr_with_size(normalsObj, sizeof(Vector3), 0); - if (tangentsObj) - { - const auto tangents = (Vector3*)(void*)mono_array_addr_with_size(tangentsObj, sizeof(Vector3), 0); - for (uint32 i = 0; i < vertexCount; i++) - { - // Peek normal and tangent - const Vector3 normal = normals[i]; - const Vector3 tangent = tangents[i]; - - // Calculate bitangent sign - Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent)); - byte sign = static_cast(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0); - - // Set tangent frame - vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign); - vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0); - } - } - else - { - for (uint32 i = 0; i < vertexCount; i++) - { - // Peek normal - const Vector3 normal = normals[i]; - - // Calculate tangent - Vector3 c1 = Vector3::Cross(normal, Vector3::UnitZ); - Vector3 c2 = Vector3::Cross(normal, Vector3::UnitY); - Vector3 tangent; - if (c1.LengthSquared() > c2.LengthSquared()) - tangent = c1; - else - tangent = c2; - - // Calculate bitangent sign - Vector3 bitangent = Vector3::Normalize(Vector3::Cross(normal, tangent)); - byte sign = static_cast(Vector3::Dot(Vector3::Cross(bitangent, normal), tangent) < 0.0f ? 1 : 0); - - // Set tangent frame - vb1[i].Tangent = Float1010102(tangent * 0.5f + 0.5f, sign); - vb1[i].Normal = Float1010102(normal * 0.5f + 0.5f, 0); - } - } - } - else - { - const auto n = Float1010102(Vector3::UnitZ); - const auto t = Float1010102(Vector3::UnitX); - for (uint32 i = 0; i < vertexCount; i++) - { - vb1[i].Normal = n; - vb1[i].Tangent = t; - } - } - if (uvObj) - { - const auto uvs = (Vector2*)(void*)mono_array_addr_with_size(uvObj, sizeof(Vector2), 0); - for (uint32 i = 0; i < vertexCount; i++) - vb1[i].TexCoord = Half2(uvs[i]); - } - else - { - auto v = Half2(0, 0); - for (uint32 i = 0; i < vertexCount; i++) - vb1[i].TexCoord = v; - } - { - auto v = Half2(0, 0); - for (uint32 i = 0; i < vertexCount; i++) - vb1[i].LightmapUVs = v; - } - if (colorsObj) - { - vb2.Resize(vertexCount); - const auto colors = (Color32*)(void*)mono_array_addr_with_size(colorsObj, sizeof(Color32), 0); - for (uint32 i = 0; i < vertexCount; i++) - vb2[i].Color = colors[i]; - } - - return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vb0, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, ib); -} - bool Mesh::UpdateMeshInt(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) { - return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); + return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); } bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* verticesObj, MonoArray* trianglesObj, MonoArray* normalsObj, MonoArray* tangentsObj, MonoArray* uvObj, MonoArray* colorsObj) @@ -493,22 +525,9 @@ bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MonoArray* v return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); } -template -bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MonoArray* trianglesObj) -{ - auto model = mesh->GetModel(); - ASSERT(model && model->IsVirtual() && trianglesObj); - - // Get buffer data - ASSERT((int32)mono_array_length(trianglesObj) / 3 >= triangleCount); - auto ib = (IndexType*)(void*)mono_array_addr_with_size(trianglesObj, sizeof(IndexType), 0); - - return mesh->UpdateTriangles(triangleCount, ib); -} - bool Mesh::UpdateTrianglesInt(int32 triangleCount, MonoArray* trianglesObj) { - return ::UpdateTriangles(this, triangleCount, trianglesObj); + return ::UpdateTriangles(this, triangleCount, trianglesObj); } bool Mesh::UpdateTrianglesUShort(int32 triangleCount, MonoArray* trianglesObj) diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 33e21aa5f..80d566fa3 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -218,7 +218,7 @@ public: /// The third vertex buffer data. /// The index buffer in clockwise order. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, int32* ib) + FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint32* ib) { return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, false); } @@ -240,6 +240,8 @@ public: /// /// Updates the model mesh (used by the virtual models created with Init rather than Load). + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. /// /// The amount of vertices in the vertex buffer. /// The amount of triangles in the index buffer. @@ -251,6 +253,38 @@ public: /// True if failed, otherwise false. bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices); + /// + /// Updates the model mesh (used by the virtual models created with Init rather than Load). + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. + /// + /// The amount of vertices in the vertex buffer. + /// The amount of triangles in the index buffer. + /// The mesh vertices positions. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. + /// The normal vectors (per vertex). + /// The normal vectors (per vertex). Use null to compute them from normal vectors. + /// The texture coordinates (per vertex). + /// The vertex colors (per vertex). + /// True if failed, otherwise false. + bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint16* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr); + + /// + /// Updates the model mesh (used by the virtual models created with Init rather than Load). + /// Can be used only for virtual assets (see and ). + /// Mesh data will be cached and uploaded to the GPU with a delay. + /// + /// The amount of vertices in the vertex buffer. + /// The amount of triangles in the index buffer. + /// The mesh vertices positions. Cannot be null. + /// The mesh index buffer (clockwise triangles). Uses 32-bit stride buffer. Cannot be null. + /// The normal vectors (per vertex). + /// The normal vectors (per vertex). Use null to compute them from normal vectors. + /// The texture coordinates (per vertex). + /// The vertex colors (per vertex). + /// True if failed, otherwise false. + bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Vector3* vertices, uint32* triangles, Vector3* normals = nullptr, Vector3* tangents = nullptr, Vector2* uvs = nullptr, Color32* colors = nullptr); + public: /// @@ -259,7 +293,7 @@ public: /// The amount of triangles in the index buffer. /// The index buffer. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, int32* ib) + FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, uint32* ib) { return UpdateTriangles(triangleCount, ib, false); } From 44d636520bc50dacd655f9a7d2ebdcfdec173226 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Tue, 26 Jan 2021 00:04:04 +0100 Subject: [PATCH 191/419] Applied review changes --- Source/Engine/Visject/VisjectGraph.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index b5ee3e29d..939013ebd 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -371,17 +371,18 @@ void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value) if (value.Type.Type == VariantType::Enum) value.AsUint64 = value.AsUint64 | (uint64)tryGetValue(node->GetBox(1), Value::Zero); break; - + // Remap case 48: { const float inVal = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat; const Vector2 rangeA = tryGetValue(node->GetBox(1), node->Values[1]).AsVector2(); const Vector2 rangeB = tryGetValue(node->GetBox(2), node->Values[2]).AsVector2(); + const bool clamp = tryGetValue(node->GetBox(3), node->Values[3]).AsBool; auto mapFunc = rangeB.X + (inVal - rangeA.X) * (rangeB.Y - rangeB.X) / (rangeA.Y - rangeA.X); // Clamp value? - value = node->Values[3].AsBool ? Math::Clamp(mapFunc, rangeB.X, rangeB.Y) : mapFunc; + value = clamp ? Math::Clamp(mapFunc, rangeB.X, rangeB.Y) : mapFunc; break; } default: From 5260efc7b825d4ca6c25d75a8ee5cdb87e151ebe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 00:11:20 +0100 Subject: [PATCH 192/419] Fixes for editor viewport View context menu --- Source/Editor/Viewport/Cameras/FPSCamera.cs | 2 +- .../Editor/Viewport/Cameras/ViewportCamera.cs | 9 ------ Source/Editor/Viewport/EditorViewport.cs | 32 ++++++++----------- .../Viewport/Previews/AnimatedModelPreview.cs | 2 +- .../Editor/Viewport/Previews/ModelPreview.cs | 2 +- .../Previews/ParticleEmitterPreview.cs | 2 +- 6 files changed, 18 insertions(+), 31 deletions(-) diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index 8a84390eb..b5098696d 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -188,7 +188,7 @@ namespace FlaxEditor.Viewport.Cameras if (input.IsPanning) { var panningSpeed = 0.8f; - if (_invertPanning) + if (Viewport.InvertPanning) { position += up * (mouseDelta.Y * panningSpeed); position += right * (mouseDelta.X * panningSpeed); diff --git a/Source/Editor/Viewport/Cameras/ViewportCamera.cs b/Source/Editor/Viewport/Cameras/ViewportCamera.cs index 21993f55c..a01000035 100644 --- a/Source/Editor/Viewport/Cameras/ViewportCamera.cs +++ b/Source/Editor/Viewport/Cameras/ViewportCamera.cs @@ -12,7 +12,6 @@ namespace FlaxEditor.Viewport.Cameras public abstract class ViewportCamera : IViewportCamera { private EditorViewport _viewport; - protected bool _invertPanning; /// /// Gets the parent viewport. @@ -28,14 +27,6 @@ namespace FlaxEditor.Viewport.Cameras /// public virtual bool UseMovementSpeed => true; - /// - /// Sets if the panning direction is inverted. - /// - public bool InvertPanning - { - set => _invertPanning = value; - } - /// /// Sets view orientation and position to match the arc ball camera style view for the given target object bounds. /// diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index fc777d1a9..a9a10c2d7 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -465,7 +465,7 @@ namespace FlaxEditor.Viewport var button = camSpeedCM.AddButton(v.ToString()); button.Tag = v; } - camSpeedCM.ButtonClicked += (button) => MovementSpeed = (float)button.Tag; + camSpeedCM.ButtonClicked += button => MovementSpeed = (float)button.Tag; camSpeedCM.VisibleChanged += WidgetCamSpeedShowHide; camSpeedButton.Parent = camSpeed; camSpeed.Parent = this; @@ -526,9 +526,9 @@ namespace FlaxEditor.Viewport // Orthographic { var ortho = ViewWidgetButtonMenu.AddButton("Orthographic"); - var orthoValue = new CheckBox(80, 2, _isOrtho); + var orthoValue = new CheckBox(90, 2, _isOrtho); orthoValue.Parent = ortho; - orthoValue.StateChanged += (checkBox) => + orthoValue.StateChanged += checkBox => { if (checkBox.Checked != _isOrtho) { @@ -554,10 +554,10 @@ namespace FlaxEditor.Viewport // Field of View { var fov = ViewWidgetButtonMenu.AddButton("Field Of View"); - var fovValue = new FloatValueBox(1, 80, 2, 50.0f, 35.0f, 160.0f, 0.1f); + var fovValue = new FloatValueBox(1, 90, 2, 70.0f, 35.0f, 160.0f, 0.1f); fovValue.Parent = fov; fovValue.ValueChanged += () => _fieldOfView = fovValue.Value; - ViewWidgetButtonMenu.VisibleChanged += (control) => + ViewWidgetButtonMenu.VisibleChanged += control => { fov.Visible = !_isOrtho; fovValue.Value = _fieldOfView; @@ -567,10 +567,10 @@ namespace FlaxEditor.Viewport // Ortho Scale { var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale"); - var orthoSizeValue = new FloatValueBox(_orthoSize, 80, 2, 50.0f, 0.001f, 100000.0f, 0.01f); + var orthoSizeValue = new FloatValueBox(_orthoSize, 90, 2, 70.0f, 0.001f, 100000.0f, 0.01f); orthoSizeValue.Parent = orthoSize; orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value; - ViewWidgetButtonMenu.VisibleChanged += (control) => + ViewWidgetButtonMenu.VisibleChanged += control => { orthoSize.Visible = _isOrtho; orthoSizeValue.Value = _orthoSize; @@ -580,7 +580,7 @@ namespace FlaxEditor.Viewport // Near Plane { var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane"); - var nearPlaneValue = new FloatValueBox(2.0f, 80, 2, 50.0f, 0.001f, 1000.0f); + var nearPlaneValue = new FloatValueBox(2.0f, 90, 2, 70.0f, 0.001f, 1000.0f); nearPlaneValue.Parent = nearPlane; nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane; @@ -589,7 +589,7 @@ namespace FlaxEditor.Viewport // Far Plane { var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane"); - var farPlaneValue = new FloatValueBox(1000, 80, 2, 50.0f, 10.0f); + var farPlaneValue = new FloatValueBox(1000, 90, 2, 70.0f, 10.0f); farPlaneValue.Parent = farPlane; farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane; @@ -598,7 +598,7 @@ namespace FlaxEditor.Viewport // Brightness { var brightness = ViewWidgetButtonMenu.AddButton("Brightness"); - var brightnessValue = new FloatValueBox(1.0f, 80, 2, 50.0f, 0.001f, 10.0f, 0.001f); + var brightnessValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.001f, 10.0f, 0.001f); brightnessValue.Parent = brightness; brightnessValue.ValueChanged += () => Brightness = brightnessValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => brightnessValue.Value = Brightness; @@ -607,7 +607,7 @@ namespace FlaxEditor.Viewport // Resolution { var resolution = ViewWidgetButtonMenu.AddButton("Resolution"); - var resolutionValue = new FloatValueBox(1.0f, 80, 2, 50.0f, 0.1f, 4.0f, 0.001f); + var resolutionValue = new FloatValueBox(1.0f, 90, 2, 70.0f, 0.1f, 4.0f, 0.001f); resolutionValue.Parent = resolution; resolutionValue.ValueChanged += () => ResolutionScale = resolutionValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale; @@ -616,9 +616,9 @@ namespace FlaxEditor.Viewport // Invert Panning { var invert = ViewWidgetButtonMenu.AddButton("Invert Panning"); - var invertValue = new CheckBox(80, 2, _invertPanning); + var invertValue = new CheckBox(90, 2, _invertPanning); invertValue.Parent = invert; - invertValue.StateChanged += (checkBox) => + invertValue.StateChanged += checkBox => { if (checkBox.Checked != _invertPanning) { @@ -900,11 +900,7 @@ namespace FlaxEditor.Viewport protected virtual void UpdateView(float dt, ref Vector3 moveDelta, ref Vector2 mouseDelta, out bool centerMouse) { centerMouse = true; - if (_camera != null) - { - _camera.InvertPanning = _invertPanning; - _camera.UpdateView(dt, ref moveDelta, ref mouseDelta, out centerMouse); - } + _camera?.UpdateView(dt, ref moveDelta, ref mouseDelta, out centerMouse); } /// diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs index 24ae53261..b85895528 100644 --- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs @@ -84,7 +84,7 @@ namespace FlaxEditor.Viewport.Previews // Preview LOD { var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); - var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f); + var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f); previewLODValue.Parent = previewLOD; previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD; diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs index e4789bdb8..aa03d0ee2 100644 --- a/Source/Editor/Viewport/Previews/ModelPreview.cs +++ b/Source/Editor/Viewport/Previews/ModelPreview.cs @@ -53,7 +53,7 @@ namespace FlaxEditor.Viewport.Previews // Preview LOD { var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); - var previewLODValue = new IntValueBox(-1, 75, 2, 50.0f, -1, 10, 0.02f); + var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f); previewLODValue.Parent = previewLOD; previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD; diff --git a/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs b/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs index e7021050b..638c8324c 100644 --- a/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs +++ b/Source/Editor/Viewport/Previews/ParticleEmitterPreview.cs @@ -67,7 +67,7 @@ namespace FlaxEditor.Viewport.Previews if (useWidgets) { var playbackDuration = ViewWidgetButtonMenu.AddButton("Duration"); - var playbackDurationValue = new FloatValueBox(_playbackDuration, 75, 2, 50.0f, 0.1f, 1000000.0f, 0.1f); + var playbackDurationValue = new FloatValueBox(_playbackDuration, 90, 2, 70.0f, 0.1f, 1000000.0f, 0.1f); playbackDurationValue.Parent = playbackDuration; playbackDurationValue.ValueChanged += () => PlaybackDuration = playbackDurationValue.Value; ViewWidgetButtonMenu.VisibleChanged += control => playbackDurationValue.Value = PlaybackDuration; From 4be7e2ad464c16066bde737b7e67eb200d98bfeb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 00:11:25 +0100 Subject: [PATCH 193/419] Fix --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 79a18bde4..5a93c71a7 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -364,7 +364,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, dotB1.Value, dotB2.Value, center.Value), node); break; } - // SphereMask + // Sphere Mask case 28: { Value a = tryGetValue(node->GetBox(0), 0, Value::Zero); @@ -378,7 +378,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Apply hardness, use 0.991 as max since any value above will result in harsh aliasing auto x2 = writeLocal(ValueType::Float, String::Format(TEXT("saturate((1 - {0}) * (1 / (1 - clamp({1}, 0, 0.991f))))"), x1.Value, hardness.Value), node); - + value = writeLocal(ValueType::Float, String::Format(TEXT("{0} ? (1 - {1}) : {1}"), invert.Value, x2.Value), node); break; } From c703064add51ea88080fabb5bbd89a690d4b26c1 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Tue, 26 Jan 2021 00:12:43 +0100 Subject: [PATCH 194/419] Added also tryGetValue to ShaderGraph Since @mafiesto4 requested in the review to use tryGetValye in the VisjectGraph to get the bool param I also did it to the shader one --- Source/Engine/Visject/ShaderGraph.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index c447961c2..c8a8782e9 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -408,9 +408,10 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) const auto inVal = tryGetValue(node->GetBox(0), node->Values[0].AsFloat); const auto rangeA = tryGetValue(node->GetBox(1), node->Values[1].AsVector2()); const auto rangeB = tryGetValue(node->GetBox(2), node->Values[2].AsVector2()); + const auto clamp = tryGetValue(node->GetBox(3), node->Values[3]).AsBool(); const auto mapFunc = String::Format(TEXT("{2}.x + ({0} - {1}.x) * ({2}.y - {2}.x) / ({1}.y - {1}.x)"), inVal.Value, rangeA.Value, rangeB.Value); - value = writeLocal(ValueType::Float, node->Values[3].AsBool ? String::Format(TEXT("clamp({0}, {1}.x, {1}.y)"), mapFunc, rangeB.Value) : mapFunc, node); + value = writeLocal(ValueType::Float, String::Format(TEXT("{2} ? clamp({0}, {1}.x, {1}.y) : {0}"), mapFunc, rangeB.Value, clamp.Value), node); break; } default: From 023cdced0a178dd72d61dba9cdb66e0f8d36a65a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 09:32:41 +0100 Subject: [PATCH 195/419] Add utility to make spline curve linear or smooth --- .../CustomEditors/Dedicated/SplineEditor.cs | 64 ++++++++++++++++ .../Editor/Undo/Actions/EditSplineAction.cs | 73 +++++++++++++++++++ Source/Engine/Debug/DebugDraw.cpp | 14 +--- Source/Engine/Level/Actors/Spline.cpp | 61 ++++++++++++++-- Source/Engine/Level/Actors/Spline.h | 14 +++- 5 files changed, 206 insertions(+), 20 deletions(-) create mode 100644 Source/Editor/CustomEditors/Dedicated/SplineEditor.cs create mode 100644 Source/Editor/Undo/Actions/EditSplineAction.cs diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs new file mode 100644 index 000000000..9eedd0ed3 --- /dev/null +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using FlaxEditor.Actions; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.CustomEditors.Dedicated +{ + /// + /// Custom editor for . + /// + /// + [CustomEditor(typeof(Spline)), DefaultEditor] + public class SplineEditor : ActorEditor + { + /// + public override void Initialize(LayoutElementsContainer layout) + { + base.Initialize(layout); + + if (Values.HasDifferentTypes == false) + { + layout.Space(10); + var grid = layout.CustomContainer(); + grid.CustomControl.SlotsHorizontally = 2; + grid.CustomControl.SlotsVertically = 1; + grid.Button("Set Linear Tangents").Button.Clicked += OnSetTangentsLinear; + grid.Button("Set Smooth Tangents").Button.Clicked += OnSetTangentsSmooth; + } + } + + private void OnSetTangentsLinear() + { + var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; + for (int i = 0; i < Values.Count; i++) + { + if (Values[i] is Spline spline) + { + var before = enableUndo ? (BezierCurve.Keyframe[])spline.SplineKeyframes.Clone() : null; + spline.SetTangentsLinear(); + if (enableUndo) + Presenter.Undo.AddAction(new EditSplineAction(spline, before)); + Editor.Instance.Scene.MarkSceneEdited(spline.Scene); + } + } + } + + private void OnSetTangentsSmooth() + { + var enableUndo = Presenter.Undo != null && Presenter.Undo.Enabled; + for (int i = 0; i < Values.Count; i++) + { + if (Values[i] is Spline spline) + { + var before = enableUndo ? (BezierCurve.Keyframe[])spline.SplineKeyframes.Clone() : null; + spline.SetTangentsSmooth(); + if (enableUndo) + Presenter.Undo.AddAction(new EditSplineAction(spline, before)); + Editor.Instance.Scene.MarkSceneEdited(spline.Scene); + } + } + } + } +} diff --git a/Source/Editor/Undo/Actions/EditSplineAction.cs b/Source/Editor/Undo/Actions/EditSplineAction.cs new file mode 100644 index 000000000..3bb8dac5c --- /dev/null +++ b/Source/Editor/Undo/Actions/EditSplineAction.cs @@ -0,0 +1,73 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; +using FlaxEditor.Modules; +using FlaxEngine; + +namespace FlaxEditor.Actions +{ + /// + /// Change keyframes undo action. + /// + /// + /// + [Serializable] + public class EditSplineAction : IUndoAction, ISceneEditAction + { + [Serialize] + private Guid _splineId; + + [Serialize] + private BezierCurve.Keyframe[] _before; + + [Serialize] + private BezierCurve.Keyframe[] _after; + + /// + /// Initializes a new instance of the class. + /// + /// The spline. + /// The spline keyframes state before editing it. + public EditSplineAction(Spline spline, BezierCurve.Keyframe[] before) + { + _splineId = spline.ID; + _before = before; + _after = (BezierCurve.Keyframe[])spline.SplineKeyframes.Clone(); + } + + /// + public string ActionString => "Edit spline keyframes"; + + /// + public void Do() + { + var spline = FlaxEngine.Object.Find(ref _splineId); + if (spline == null) + return; + spline.SplineKeyframes = _after; + } + + /// + public void Undo() + { + var spline = FlaxEngine.Object.Find(ref _splineId); + if (spline == null) + return; + spline.SplineKeyframes = _before; + } + + /// + public void Dispose() + { + _before = _after = null; + } + + /// + public void MarkSceneEdited(SceneModule sceneModule) + { + var spline = FlaxEngine.Object.Find(ref _splineId); + if (spline != null) + sceneModule.MarkSceneEdited(spline.Scene); + } + } +} diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 457c90411..596956dea 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -650,16 +650,6 @@ void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, c } } -static Vector3 GetPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) -{ - const float oneMinusT = 1.0f - t; - return - oneMinusT * oneMinusT * oneMinusT * p0 + - 3.0f * oneMinusT * oneMinusT * t * p1 + - 3.0f * oneMinusT * t * t * p2 + - t * t * t * p3; -} - void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Color& color, float duration, bool depthTest) { // Create draw call entry @@ -676,13 +666,13 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& const Vector3 d3 = p4 - p3; const float len = d1.Length() + d2.Length() + d3.Length(); const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); - const float segmentCountInv = 1.0f / segmentCount; + const float segmentCountInv = 1.0f / (float)segmentCount; list->EnsureCapacity(list->Count() + segmentCount + 2); // Draw segmented curve for (int32 i = 0; i <= segmentCount; i++) { - const float t = i * segmentCountInv; + const float t = (float)i * segmentCountInv; AnimationUtils::Bezier(p1, p2, p3, p4, t, l.End); list->Add(l); l.Start = l.End; diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 509c2f389..5e9e7fe94 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -154,8 +154,6 @@ float Spline::GetSplineTimeClosestToPoint(const Vector3& point) const float bestTime = 0.0f; for (int32 i = 1; i < pointsCount; i++) FindTimeClosestToPoint(localPoint, Curve[i - 1], Curve[i], bestDistanceSquared, bestTime); - if (_loop) - FindTimeClosestToPoint(localPoint, Curve[pointsCount - 1], Curve[0], bestDistanceSquared, bestTime); return bestTime; } @@ -228,8 +226,61 @@ void Spline::AddSplineLocalPoint(const Transform& point, bool updateSpline) UpdateSpline(); } +void Spline::SetTangentsLinear() +{ + const int32 count = Curve.GetKeyframes().Count(); + if (count < 2) + return; + + if (_loop) + Curve[count - 1].Value = Curve[0].Value; + for (int32 i = 0; i < count; i++) + { + auto& k = Curve[i]; + k.TangentIn = k.TangentOut = Transform::Identity; + } + + UpdateSpline(); +} + +void Spline::SetTangentsSmooth() +{ + const int32 count = Curve.GetKeyframes().Count(); + if (count < 2) + return; + + auto& keys = Curve.GetKeyframes(); + const int32 last = count - 2; + if (_loop) + Curve[count - 1].Value = Curve[0].Value; + for (int32 i = 0; i <= last; i++) + { + auto& key = keys[i]; + const auto& prevKey = keys[i == 0 ? (_loop ? last : 0) : i - 1]; + const auto& nextKey = keys[i == last ? (_loop ? 0 : last) : i + 1]; + const float prevTime = _loop && i == 0 ? key.Time : prevKey.Time; + const float nextTime = _loop && i == last ? key.Time : nextKey.Time; + const Vector3 slope = key.Value.Translation - prevKey.Value.Translation + nextKey.Value.Translation - key.Value.Translation; + const Vector3 tangent = slope / Math::Max(nextTime - prevTime, ZeroTolerance); + key.TangentIn.Translation = -tangent; + key.TangentOut.Translation = tangent; + } + + UpdateSpline(); +} + void Spline::UpdateSpline() { + // Always keep last point in the loop + const int32 count = Curve.GetKeyframes().Count(); + if (_loop && count > 1) + { + auto& first = Curve[0]; + auto& last = Curve[count - 1]; + last.Value = first.Value; + last.TangentIn = first.TangentIn; + last.TangentOut = first.TangentOut; + } } void Spline::GetKeyframes(MonoArray* data) @@ -256,9 +307,9 @@ namespace const auto& startKey = spline->Curve[start]; const auto& endKey = spline->Curve[end]; const Vector3 startPos = transform.LocalToWorld(startKey.Value.Translation); - const Vector3 startTangent = transform.LocalToWorld(startKey.TangentOut.Translation); + const Vector3& startTangent = startKey.TangentOut.Translation; const Vector3 endPos = transform.LocalToWorld(endKey.Value.Translation); - const Vector3 endTangent = transform.LocalToWorld(endKey.TangentIn.Translation); + const Vector3& endTangent = endKey.TangentIn.Translation; const float d = (endKey.Time - startKey.Time) / 3.0f; DEBUG_DRAW_BEZIER(startPos, startPos + startTangent * d, endPos + endTangent * d, endPos, color, 0.0f, depthTest); } @@ -273,8 +324,6 @@ namespace if (i != 0) DrawSegment(spline, i - 1, i, color, transform, depthTest); } - if (spline->GetIsLoop() && count > 1) - DrawSegment(spline, count - 1, 0, color, transform, depthTest); } } diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index a94804ea7..fd489b823 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -25,7 +25,7 @@ public: BezierCurve Curve; /// - /// Whether to use spline as closed loop. + /// Whether to use spline as closed loop. In that case, ensure to place start and end at the same location. /// API_PROPERTY(Attributes="EditorOrder(0), EditorDisplay(\"Spline\")") FORCE_INLINE bool GetIsLoop() const @@ -34,7 +34,7 @@ public: } /// - /// Whether to use spline as closed loop. + /// Whether to use spline as closed loop. In that case, ensure to place start and end at the same location. /// API_PROPERTY() void SetIsLoop(bool value); @@ -212,6 +212,16 @@ public: /// True if update spline after adding the point, otherwise false. API_FUNCTION() void AddSplineLocalPoint(const Transform& point, bool updateSpline = true); + /// + /// Updates the curve tangent points to make curve linear. + /// + API_FUNCTION() void SetTangentsLinear(); + + /// + /// Updates the curve tangent points to make curve smooth. + /// + API_FUNCTION() void SetTangentsSmooth(); + public: /// From dda5f26d8993da9bbc47204537d383238afaed46 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 12:11:41 +0100 Subject: [PATCH 196/419] Add `Transform.LocalToWorldVector` and `Transform.WorldToLocalVector` --- Source/Engine/Core/Math/Transform.cpp | 25 +++++++++++++++++++ Source/Engine/Core/Math/Transform.cs | 35 +++++++++++++++++++++++++++ Source/Engine/Core/Math/Transform.h | 14 +++++++++++ 3 files changed, 74 insertions(+) diff --git a/Source/Engine/Core/Math/Transform.cpp b/Source/Engine/Core/Math/Transform.cpp index 5ac71779b..728fc6a90 100644 --- a/Source/Engine/Core/Math/Transform.cpp +++ b/Source/Engine/Core/Math/Transform.cpp @@ -94,6 +94,13 @@ Vector3 Transform::LocalToWorld(const Vector3& point) const return result + Translation; } +Vector3 Transform::LocalToWorldVector(const Vector3& vector) const +{ + Vector3 result = vector * Scale; + Vector3::Transform(result, Orientation, result); + return result; +} + void Transform::LocalToWorld(const Vector3& point, Vector3& result) const { Vector3 tmp = point * Scale; @@ -171,6 +178,24 @@ Vector3 Transform::WorldToLocal(const Vector3& point) const return result * invScale; } +Vector3 Transform::WorldToLocalVector(const Vector3& vector) const +{ + Vector3 invScale = Scale; + if (invScale.X != 0.0f) + invScale.X = 1.0f / invScale.X; + if (invScale.Y != 0.0f) + invScale.Y = 1.0f / invScale.Y; + if (invScale.Z != 0.0f) + invScale.Z = 1.0f / invScale.Z; + + const Quaternion invRotation = Orientation.Conjugated(); + + Vector3 result; + Vector3::Transform(vector, invRotation, result); + + return result * invScale; +} + void Transform::WorldToLocal(const Vector3* points, int32 pointsCount, Vector3* result) const { Vector3 invScale = Scale; diff --git a/Source/Engine/Core/Math/Transform.cs b/Source/Engine/Core/Math/Transform.cs index 21ed705ff..7065309e3 100644 --- a/Source/Engine/Core/Math/Transform.cs +++ b/Source/Engine/Core/Math/Transform.cs @@ -197,6 +197,18 @@ namespace FlaxEngine return point + Translation; } + /// + /// Performs transformation of the given vector in local space to the world space of this transform. + /// + /// The local space vector. + /// The world space vector. + public Vector3 LocalToWorldVector(Vector3 vector) + { + vector *= Scale; + Vector3.Transform(ref vector, ref Orientation, out vector); + return vector; + } + /// /// Perform transformation of the given points in local space /// @@ -259,6 +271,29 @@ namespace FlaxEngine return result * invScale; } + /// + /// Perform transformation of the given vector in world space + /// + /// World space vector + /// Local space vector + public Vector3 WorldToLocalVector(Vector3 vector) + { + Vector3 invScale = Scale; + if (invScale.X != 0.0f) + invScale.X = 1.0f / invScale.X; + if (invScale.Y != 0.0f) + invScale.Y = 1.0f / invScale.Y; + if (invScale.Z != 0.0f) + invScale.Z = 1.0f / invScale.Z; + + Quaternion invRotation = Orientation; + invRotation.Invert(); + + Vector3.Transform(ref vector, ref invRotation, out var result); + + return result * invScale; + } + /// /// Perform transformation of the given points in world space /// diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index c5e67c986..5a39c2d86 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -184,6 +184,13 @@ public: /// The world space point. Vector3 LocalToWorld(const Vector3& point) const; + /// + /// Performs transformation of the given vector in local space to the world space of this transform. + /// + /// The local space vector. + /// The world space vector. + Vector3 LocalToWorldVector(const Vector3& vector) const; + /// /// Performs transformation of the given point in local space to the world space of this transform. /// @@ -220,6 +227,13 @@ public: /// The local space point. Vector3 WorldToLocal(const Vector3& point) const; + /// + /// Performs transformation of the given vector in world space to the local space of this transform. + /// + /// The world space vector. + /// The local space vector. + Vector3 WorldToLocalVector(const Vector3& vector) const; + /// /// Performs transformation of the given points in world space to the local space of this transform. /// From b674aae970b97bd8fcf42fde9fb592cb8ffa0c2d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 12:12:16 +0100 Subject: [PATCH 197/419] Fix spline length calculation to include bezier shape --- Source/Engine/Level/Actors/Spline.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 5e9e7fe94..4c2680e79 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -114,8 +114,30 @@ float Spline::GetSplineDuration() const float Spline::GetSplineLength() const { float sum = 0.0f; + const int32 slices = 20; + const float step = 1.0f / (float)slices; + Vector3 prevPoint = Vector3::Zero; for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++) - sum += Vector3::DistanceSquared(Curve[i - 1].Value.Translation * _transform.Scale, Curve[i].Value.Translation * _transform.Scale); + { + const auto& a = Curve[i = 1]; + const auto& b = Curve[i]; + + const float length = Math::Abs(b.Time - a.Time); + Vector3 leftTangent, rightTangent; + AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent); + AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent); + + // TODO: implement sth more analytical than brute-force solution + for (int32 slice = 0; slice < slices; slice++) + { + const float t = (float)slice * step; + Vector3 pos; + AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos); + pos *= _transform.Scale; + sum += Vector3::DistanceSquared(pos, prevPoint); + prevPoint = pos; + } + } return Math::Sqrt(sum); } From 937248794ae209172ae15ddf86f44434a606c06e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 12:32:16 +0100 Subject: [PATCH 198/419] Add first derivative calculation utilities for Curve --- Source/Engine/Animations/AnimationUtils.h | 135 ++++++++++++---------- Source/Engine/Animations/Curve.h | 91 +++++++++++++-- 2 files changed, 155 insertions(+), 71 deletions(-) diff --git a/Source/Engine/Animations/AnimationUtils.h b/Source/Engine/Animations/AnimationUtils.h index a7f85f124..c90afcea3 100644 --- a/Source/Engine/Animations/AnimationUtils.h +++ b/Source/Engine/Animations/AnimationUtils.h @@ -84,21 +84,21 @@ namespace AnimationUtils } template - FORCE_INLINE static void Interpolate(const T& a, const T& b, float alpha, T& result) + FORCE_INLINE static void Interpolate(const T& a, const T& b, float t, T& result) { - result = (T)(a + alpha * (b - a)); + result = (T)(a + t * (b - a)); } template<> - FORCE_INLINE void Interpolate(const Vector3& a, const Vector3& b, float alpha, Vector3& result) + FORCE_INLINE void Interpolate(const Vector3& a, const Vector3& b, float t, Vector3& result) { - Vector3::Lerp(a, b, alpha, result); + Vector3::Lerp(a, b, t, result); } template<> - FORCE_INLINE void Interpolate(const Quaternion& a, const Quaternion& b, float alpha, Quaternion& result) + FORCE_INLINE void Interpolate(const Quaternion& a, const Quaternion& b, float t, Quaternion& result) { - Quaternion::Slerp(a, b, alpha, result); + Quaternion::Slerp(a, b, t, result); } static void WrapTime(float& time, float start, float end, bool loop) @@ -134,98 +134,111 @@ namespace AnimationUtils /// Evaluates a cubic Hermite curve at a specific point. /// /// The time parameter that at which to evaluate the curve, in range [0, 1]. - /// The starting point (at t=0). - /// The ending point (at t=1). - /// The starting tangent (at t=0). - /// The ending tangent (at t = 1). + /// The starting point (at t=0). + /// The ending point (at t=1). + /// The starting tangent (at t=0). + /// The ending tangent (at t = 1). /// The evaluated value. template - static void CubicHermite(const float t, const T& pointA, const T& pointB, const T& tangentA, const T& tangentB, T* result) + static void CubicHermite(const T& p0, const T& p1, const T& t0, const T& t1, float t, T& result) { - const float t2 = t * t; - const float t3 = t2 * t; - - float a = 2 * t3 - 3 * t2 + 1; - float b = t3 - 2 * t2 + t; - float c = -2 * t3 + 3 * t2; - float d = t3 - t2; - - *result = a * pointA + b * tangentA + c * pointB + d * tangentB; + const float tt = t * t; + const float ttt = tt * t; + result = (2 * ttt - 3 * tt + 1) * p0 + (ttt - 2 * tt + t) * t0 + (-2 * ttt + 3 * tt) * p1 + (ttt - tt) * t1; } /// /// Evaluates the first derivative of a cubic Hermite curve at a specific point. /// /// The time parameter that at which to evaluate the curve, in range [0, 1]. - /// The starting point (at t=0). - /// The ending point (at t=1). - /// The starting tangent (at t=0). - /// The ending tangent (at t = 1). + /// The starting point (at t=0). + /// The ending point (at t=1). + /// The starting tangent (at t=0). + /// The ending tangent (at t=1). /// The evaluated value. template - static void CubicHermiteD1(const float t, const T& pointA, const T& pointB, const T& tangentA, const T& tangentB, T* result) + static void CubicHermiteFirstDerivative(const T& p0, const T& p1, const T& t0, const T& t1, float t, T& result) { - const float t2 = t * t; - - float a = 6 * t2 - 6 * t; - float b = 3 * t2 - 4 * t + 1; - float c = -6 * t2 + 6 * t; - float d = 3 * t2 - 2 * t; - - *result = a * pointA + b * tangentA + c * pointB + d * tangentB; + const float tt = t * t; + result = (6 * tt - 6 * t) * p0 + (3 * tt - 4 * t + 1) * t0 + (-6 * tt + 6 * t) * p1 + (3 * tt - 2 * t) * t1; } template - static void Bezier(const T& p0, const T& p1, const T& p2, const T& p3, float alpha, T& result) + static void Bezier(const T& p0, const T& p1, const T& p2, const T& p3, float t, T& result) { T p01, p12, p23, p012, p123; - Interpolate(p0, p1, alpha, p01); - Interpolate(p1, p2, alpha, p12); - Interpolate(p2, p3, alpha, p23); - Interpolate(p01, p12, alpha, p012); - Interpolate(p12, p23, alpha, p123); - Interpolate(p012, p123, alpha, result); + Interpolate(p0, p1, t, p01); + Interpolate(p1, p2, t, p12); + Interpolate(p2, p3, t, p23); + Interpolate(p01, p12, t, p012); + Interpolate(p12, p23, t, p123); + Interpolate(p012, p123, t, result); } template<> - void Bezier(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3, float alpha, Vector2& result) + void Bezier(const Vector2& p0, const Vector2& p1, const Vector2& p2, const Vector2& p3, float t, Vector2& result) { - const float u = 1.0f - alpha; - const float tt = alpha * alpha; + const float u = 1.0f - t; + const float tt = t * t; const float uu = u * u; const float uuu = uu * u; - const float ttt = tt * alpha; - result = uuu * p0 + 3 * uu * alpha * p1 + 3 * u * tt * p2 + ttt * p3; + const float ttt = tt * t; + result = uuu * p0 + 3 * uu * t * p1 + 3 * u * tt * p2 + ttt * p3; } template<> - void Bezier(const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3, float alpha, Vector3& result) + void Bezier(const Vector3& p0, const Vector3& p1, const Vector3& p2, const Vector3& p3, float t, Vector3& result) { - const float u = 1.0f - alpha; - const float tt = alpha * alpha; + const float u = 1.0f - t; + const float tt = t * t; const float uu = u * u; const float uuu = uu * u; - const float ttt = tt * alpha; - result = uuu * p0 + 3 * uu * alpha * p1 + 3 * u * tt * p2 + ttt * p3; + const float ttt = tt * t; + result = uuu * p0 + 3 * uu * t * p1 + 3 * u * tt * p2 + ttt * p3; } template<> - void Bezier(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float alpha, Quaternion& result) + void Bezier(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float t, Quaternion& result) { Quaternion p01, p12, p23, p012, p123; - Quaternion::Slerp(p0, p1, alpha, p01); - Quaternion::Slerp(p1, p2, alpha, p12); - Quaternion::Slerp(p2, p3, alpha, p23); - Quaternion::Slerp(p01, p12, alpha, p012); - Quaternion::Slerp(p12, p23, alpha, p123); - Quaternion::Slerp(p012, p123, alpha, result); + Quaternion::Slerp(p0, p1, t, p01); + Quaternion::Slerp(p1, p2, t, p12); + Quaternion::Slerp(p2, p3, t, p23); + Quaternion::Slerp(p01, p12, t, p012); + Quaternion::Slerp(p12, p23, t, p123); + Quaternion::Slerp(p012, p123, t, result); } template<> - void Bezier(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float alpha, Transform& result) + void Bezier(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float t, Transform& result) { - Bezier(p0.Translation, p1.Translation, p2.Translation, p3.Translation, alpha, result.Translation); - Bezier(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, alpha, result.Orientation); - Bezier(p0.Scale, p1.Scale, p2.Scale, p3.Scale, alpha, result.Scale); + Bezier(p0.Translation, p1.Translation, p2.Translation, p3.Translation, t, result.Translation); + Bezier(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, t, result.Orientation); + Bezier(p0.Scale, p1.Scale, p2.Scale, p3.Scale, t, result.Scale); + } + + template + static void BezierFirstDerivative(const T& p0, const T& p1, const T& p2, const T& p3, float t, T& result) + { + const float u = 1.0f - t; + const float tt = t * t; + const float uu = u * u; + result = 3.0f * uu * (p1 - p0) + 6.0f * u * t * (p2 - p1) + 3.0f * tt * (p3 - p2); + } + + template<> + static void BezierFirstDerivative(const Quaternion& p0, const Quaternion& p1, const Quaternion& p2, const Quaternion& p3, float t, Quaternion& result) + { + Vector3 euler; + BezierFirstDerivative(p0.GetEuler(), p1.GetEuler(), p2.GetEuler(), p3.GetEuler(), t, euler); + result = Quaternion::Euler(euler); + } + + template<> + static void BezierFirstDerivative(const Transform& p0, const Transform& p1, const Transform& p2, const Transform& p3, float t, Transform& result) + { + BezierFirstDerivative(p0.Translation, p1.Translation, p2.Translation, p3.Translation, t, result.Translation); + BezierFirstDerivative(p0.Orientation, p1.Orientation, p2.Orientation, p3.Orientation, t, result.Orientation); + BezierFirstDerivative(p0.Scale, p1.Scale, p2.Scale, p3.Scale, t, result.Scale); } } diff --git a/Source/Engine/Animations/Curve.h b/Source/Engine/Animations/Curve.h index 618e0ee0c..24dcd87a8 100644 --- a/Source/Engine/Animations/Curve.h +++ b/Source/Engine/Animations/Curve.h @@ -48,6 +48,11 @@ public: result = a.Value; } + FORCE_INLINE static void InterpolateFirstDerivative(const StepCurveKeyframe& a, const StepCurveKeyframe& b, float alpha, float length, T& result) + { + result = AnimationUtils::GetZero(); + } + FORCE_INLINE static void InterpolateKey(const StepCurveKeyframe& a, const StepCurveKeyframe& b, float alpha, float length, StepCurveKeyframe& result) { result = a; @@ -97,6 +102,11 @@ public: AnimationUtils::Interpolate(a.Value, b.Value, alpha, result); } + FORCE_INLINE static void InterpolateFirstDerivative(const LinearCurveKeyframe& a, const LinearCurveKeyframe& b, float alpha, float length, T& result) + { + result = b.Value - a.Value; + } + FORCE_INLINE static void InterpolateKey(const LinearCurveKeyframe& a, const LinearCurveKeyframe& b, float alpha, float length, LinearCurveKeyframe& result) { result.Time = a.Time + (b.Time - a.Time) * alpha; @@ -157,20 +167,23 @@ public: { T leftTangent = a.Value + a.TangentOut * length; T rightTangent = b.Value + b.TangentIn * length; + AnimationUtils::CubicHermite( a.Value, b.Value, leftTangent, rightTangent, alpha, result); + } - AnimationUtils::CubicHermite(alpha, a.Value, b.Value, leftTangent, rightTangent, result); + static void InterpolateFirstDerivative(const HermiteCurveKeyframe& a, const HermiteCurveKeyframe& b, float alpha, float length, T& result) + { + T leftTangent = a.Value + a.TangentOut * length; + T rightTangent = b.Value + b.TangentIn * length; + AnimationUtils::CubicHermiteFirstDerivative( a.Value, b.Value, leftTangent, rightTangent, alpha, result); } static void InterpolateKey(const HermiteCurveKeyframe& a, const HermiteCurveKeyframe& b, float alpha, float length, HermiteCurveKeyframe& result) { result.Time = a.Time + length * alpha; - T leftTangent = a.Value + a.TangentOut * length; T rightTangent = b.Value + b.TangentIn * length; - - AnimationUtils::CubicHermite(alpha, a.Value, b.Value, leftTangent, rightTangent, result.Value); - AnimationUtils::CubicHermiteD1(alpha, a.Value, b.Value, leftTangent, rightTangent, result.TangentIn); - + AnimationUtils::CubicHermite(a.Value, b.Value, leftTangent, rightTangent, alpha, result.Value); + AnimationUtils::CubicHermiteFirstDerivative(a.Value, b.Value, leftTangent, rightTangent, alpha, result.TangentIn); result.TangentIn /= length; result.TangentOut = result.TangentIn; } @@ -238,20 +251,24 @@ public: T leftTangent, rightTangent; AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent); AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent); - AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result); } + static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result) + { + T leftTangent, rightTangent; + AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent); + AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent); + AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result); + } + static void InterpolateKey(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, BezierCurveKeyframe& result) { result.Time = a.Time + length * alpha; - T leftTangent, rightTangent; AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent); AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent); - AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value); - result.TangentIn = a.TangentOut; result.TangentOut = b.TangentIn; } @@ -366,6 +383,48 @@ public: KeyFrame::Interpolate(leftKey, rightKey, t, length, result); } + /// + /// Evaluates the first derivative of the animation curve at the specified time (aka velocity). + /// + /// The keyframes data container. + /// The calculated first derivative from the curve at provided time. + /// The time to evaluate the curve at. + /// If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped. + void EvaluateFirstDerivative(const KeyFrameData& data, T& result, float time, bool loop = true) const + { + const int32 count = data.Length(); + if (count == 0) + { + result = _default; + return; + } + + const float start = 0; + const float end = data[count - 1].Time; + AnimationUtils::WrapTime(time, start, end, loop); + + int32 leftKeyIdx; + int32 rightKeyIdx; + FindKeys(data, time, leftKeyIdx, rightKeyIdx); + + const KeyFrame& leftKey = data[leftKeyIdx]; + const KeyFrame& rightKey = data[rightKeyIdx]; + + if (leftKeyIdx == rightKeyIdx) + { + result = leftKey.Value; + return; + } + + const float length = rightKey.Time - leftKey.Time; + + // Scale from arbitrary range to [0, 1] + float t = Math::NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length; + + // Evaluate the derivative at the curve + KeyFrame::InterpolateFirstDerivative(leftKey, rightKey, t, length, result); + } + /// /// Evaluates the animation curve key at the specified time. /// @@ -586,6 +645,18 @@ public: Base::Evaluate(data, result, time, loop); } + /// + /// Evaluates the first derivative of the animation curve at the specified time (aka velocity). + /// + /// The calculated first derivative from the curve at provided time. + /// The time to evaluate the curve at. + /// If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped. + void EvaluateFirstDerivative(T& result, float time, bool loop = true) const + { + typename Base::KeyFrameData data(_keyframes); + Base::EvaluateFirstDerivative(data, result, time, loop); + } + /// /// Evaluates the animation curve key at the specified time. /// From aa2eeb82437363680415e83562a83bbf64d291d3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 12:32:35 +0100 Subject: [PATCH 199/419] Add GetSplineDirection util to Spline --- Source/Engine/Level/Actors/Spline.cpp | 15 +++++++++++++++ Source/Engine/Level/Actors/Spline.h | 14 ++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 4c2680e79..9f47aa06e 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -77,6 +77,21 @@ Transform Spline::GetSplineLocalTransform(float time) const return t; } +Vector3 Spline::GetSplineDirection(float time) const +{ + return _transform.LocalToWorldVector(GetSplineLocalDirection(time)); +} + +Vector3 Spline::GetSplineLocalDirection(float time) const +{ + if (Curve.GetKeyframes().Count() == 0) + return Vector3::Forward; + Transform t; + Curve.EvaluateFirstDerivative(t, time, _loop); + t.Translation.Normalize(); + return t.Translation; +} + Vector3 Spline::GetSplinePoint(int32 index) const { CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index fd489b823..cc4226414 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -96,6 +96,20 @@ public: /// The calculated curve point transformation (local-space). API_FUNCTION() Transform GetSplineLocalTransform(float time) const; + /// + /// Evaluates the spline curve direction (forward vector, aka position 1st derivative) at the given time in 3D (world-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve direction (world-space). + API_FUNCTION() Vector3 GetSplineDirection(float time) const; + + /// + /// Evaluates the spline curve direction (forward vector, aka position 1st derivative) at the given time in 3D (local-space). + /// + /// The time value. Can be negative or larger than curve length (curve will loop or clamp). + /// The calculated curve direction (local-space). + API_FUNCTION() Vector3 GetSplineLocalDirection(float time) const; + /// /// Evaluates the spline curve at the given index (world-space). /// From 8180877269887e94e264f99ec95d3026db560d81 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 12:52:03 +0100 Subject: [PATCH 200/419] Add support for copy/paste/rever values for groups in Custom Editor (eg. array editor) --- .../CustomEditors/LayoutElementsContainer.cs | 20 ++++++++++ Source/Engine/UI/GUI/Panels/DropPanel.cs | 40 +++++++++++++------ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 9df2de8be..59058056d 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.GUI; using FlaxEditor.GUI; +using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.Assertions; using FlaxEngine.GUI; @@ -553,6 +554,8 @@ namespace FlaxEditor.CustomEditors var group = Group(name, true); group.Panel.Close(false); group.Panel.TooltipText = tooltip; + group.Panel.Tag = editor; + group.Panel.MouseButtonRightClicked += OnGroupPanelMouseButtonRightClicked; return group.Object(values, editor); } @@ -560,6 +563,23 @@ namespace FlaxEditor.CustomEditors return property.Object(values, editor); } + private void OnGroupPanelMouseButtonRightClicked(DropPanel groupPanel, Vector2 location) + { + var linkedEditor = (CustomEditor)groupPanel.Tag; + var menu = new ContextMenu(); + + var revertToPrefab = menu.AddButton("Revert to Prefab", linkedEditor.RevertToReferenceValue); + revertToPrefab.Enabled = linkedEditor.CanRevertReferenceValue; + var resetToDefault = menu.AddButton("Reset to default", linkedEditor.RevertToDefaultValue); + resetToDefault.Enabled = linkedEditor.CanRevertDefaultValue; + menu.AddSeparator(); + menu.AddButton("Copy", linkedEditor.Copy); + var paste = menu.AddButton("Paste", linkedEditor.Paste); + paste.Enabled = linkedEditor.CanPaste; + + menu.Show(groupPanel, location); + } + /// /// Adds object property editor. Selects proper based on overrides. /// diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index fa5ff7f59..04493c7ef 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -31,9 +31,14 @@ namespace FlaxEngine.GUI protected bool _mouseOverHeader; /// - /// The 'mouse down' flag (over header). + /// The 'mouse down' flag (over header) for the left mouse button. /// - protected bool _mouseDown; + protected bool _mouseButtonLeftDown; + + /// + /// The 'mouse down' flag (over header) for the right mouse button. + /// + protected bool _mouseButtonRightDown; /// /// The animation progress (normalized). @@ -126,6 +131,11 @@ namespace FlaxEngine.GUI [EditorDisplay("Style"), EditorOrder(2000)] public bool EnableDropDownIcon { get; set; } + /// + /// Occurs when mouse right-clicks over the header. + /// + public event Action MouseButtonRightClicked; + /// /// Occurs when drop panel is opened or closed. /// @@ -430,10 +440,14 @@ namespace FlaxEngine.GUI return true; _mouseOverHeader = HeaderRectangle.Contains(location); - if (button == MouseButton.Left && _mouseOverHeader) { - _mouseDown = true; + _mouseButtonLeftDown = true; + return true; + } + if (button == MouseButton.Right && _mouseOverHeader) + { + _mouseButtonRightDown = true; return true; } @@ -455,16 +469,17 @@ namespace FlaxEngine.GUI return true; _mouseOverHeader = HeaderRectangle.Contains(location); - - if (button == MouseButton.Left && _mouseDown) + if (button == MouseButton.Left && _mouseButtonLeftDown) { - _mouseDown = false; - + _mouseButtonLeftDown = false; if (_mouseOverHeader) - { Toggle(); - } - + return true; + } + if (button == MouseButton.Right && _mouseButtonRightDown) + { + _mouseButtonRightDown = false; + MouseButtonRightClicked?.Invoke(this, location); return true; } @@ -474,7 +489,8 @@ namespace FlaxEngine.GUI /// public override void OnMouseLeave() { - _mouseDown = false; + _mouseButtonLeftDown = false; + _mouseButtonRightDown = false; _mouseOverHeader = false; base.OnMouseLeave(); From 51a2c1e868fbaff710af17a0eedddeefefa55a77 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 13:41:23 +0100 Subject: [PATCH 201/419] Add NetworkSocketGroup. --- Source/Engine/Platform/Base/NetworkBase.cpp | 20 ++++++++++++++++++++ Source/Engine/Platform/Base/NetworkBase.h | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index e7b700911..b4f1110ab 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -71,6 +71,26 @@ bool NetworkBase::IsWriteable(NetworkSocket& socket) return true; } +int32 NetworkBase::Poll(NetworkSocketGroup& group) +{ + return 0; +} + +bool NetworkBase::GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state) +{ + return false; +} + +int32 NetworkBase::AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket) +{ + return 0; +} + +void NetworkBase::ClearGroup(NetworkSocketGroup& group) +{ + group.Count = 0; +} + int32 NetworkBase::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) { return 0; diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 295100ffe..0f0f89cb7 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -5,6 +5,8 @@ #include "Engine/Core/Types/BaseTypes.h" #include "Engine/Core/Types/String.h" API_INJECT_CPP_CODE("#include \"Engine/Platform/Network.h\""); +#define SOCKGROUP_MAXCOUNT 64 +#define SOCKGROUP_ITEMSIZE 16 enum class FLAXENGINE_API NetworkProtocolType { @@ -54,6 +56,21 @@ enum FLAXENGINE_API NetworkSocketOption IPv6Only }; +struct FLAXENGINE_API NetworkSocketState +{ + bool Error = false; + bool Invalid = false; + bool Disconnected = false; + bool Readable = false; + bool Writeable = false; +}; + +struct FLAXENGINE_API NetworkSocketGroup +{ + uint32 Count; + byte Data[SOCKGROUP_MAXCOUNT * 16] = {}; +}; + class FLAXENGINE_API NetworkBase { public: @@ -71,6 +88,10 @@ class FLAXENGINE_API NetworkBase static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket); static bool IsWriteable(NetworkSocket& socket); + static int32 Poll(NetworkSocketGroup& group); + static bool GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state); + static int32 AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket); + static void ClearGroup(NetworkSocketGroup& group); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); From 45ba15487775410d79f3b8b35f703424e274a8c0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 13:41:44 +0100 Subject: [PATCH 202/419] Add NetworkSocketGroup window impl. --- Source/Engine/Platform/Win32/Win32Network.cpp | 45 +++++++++++++++++++ Source/Engine/Platform/Win32/Win32Network.h | 4 ++ 2 files changed, 49 insertions(+) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index d2e7e0944..a9fa388dc 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -334,6 +334,51 @@ bool Win32Network::IsWriteable(NetworkSocket& socket) return false; } +static thread_local int32 pollret; +int32 Win32Network::Poll(NetworkSocketGroup& group) +{ + pollret = WSAPoll((pollfd*)group.Data, group.Count, 0); + if (pollret == SOCKET_ERROR) + LOG(Error, "Unable to poll socket group! Error : {0}", GetLastErrorMessage().Get()); + return pollret; +} + +static thread_local pollfd* pollptr; +bool Win32Network::GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state) +{ + if (index >= SOCKGROUP_MAXCOUNT) + return true; + pollptr = (pollfd*)&group.Data[index * SOCKGROUP_ITEMSIZE]; + if (pollptr->revents & POLLERR) + state.Error = true; + if (pollptr->revents & POLLHUP) + state.Disconnected = true; + if (pollptr->revents & POLLNVAL) + state.Invalid = true; + if (pollptr->revents & POLLRDNORM) + state.Readable = true; + if (pollptr->revents & POLLWRNORM) + state.Writeable = true; + return false; +} + +static thread_local pollfd pollinfo; +int32 Win32Network::AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket) +{ + if (group.Count >= SOCKGROUP_MAXCOUNT) + return -1; + pollinfo.fd = *(SOCKET*)socket.Data; + pollinfo.events = POLLRDNORM | POLLWRNORM; + *(pollfd*)&group.Data[group.Count * SOCKGROUP_ITEMSIZE] = pollinfo; + group.Count++; + return group.Count-1; +} + +void Win32Network::ClearGroup(NetworkSocketGroup& group) +{ + group.Count = 0; +} + int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) { if (endPoint != nullptr && socket.IPVersion != endPoint->IPVersion) diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index bbf15a4c4..8700d9f19 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -27,6 +27,10 @@ public: static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket); static bool IsWriteable(NetworkSocket& socket); + static int32 Poll(NetworkSocketGroup& group); + static bool GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state); + static int32 AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket); + static void ClearGroup(NetworkSocketGroup& group); static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); From ecb20afaa45a2c760cff9e80a8f975351c1f9b1c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 13:52:38 +0100 Subject: [PATCH 203/419] Reimpl. SiReadable & IsWriteable. --- Source/Engine/Platform/Win32/Win32Network.cpp | 34 ++++++------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index a9fa388dc..a2038def5 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -294,42 +294,28 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net bool Win32Network::IsReadable(NetworkSocket& socket) { - fd_set readfds; - FD_ZERO(&readfds); - FD_SET(*(SOCKET*)socket.Data, &readfds); - timeval t; - t.tv_sec = 0; - t.tv_usec = 0; - if (select(0, &readfds, nullptr, nullptr, &t) == SOCKET_ERROR) + pollfd entry; + entry.events = POLLRDNORM; + if (WSAPoll(&entry, 1, 0) == SOCKET_ERROR) { - int error = WSAGetLastError(); - if (error == WSAEWOULDBLOCK) - return false; - LOG(Error, "Unable to check readability of socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); + LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); return false; } - if (FD_ISSET(*(SOCKET*)socket.Data, &readfds)) + if (entry.revents & POLLRDNORM) return true; return false; } bool Win32Network::IsWriteable(NetworkSocket& socket) { - fd_set writefds; - FD_ZERO(&writefds); - FD_SET(*(SOCKET*)socket.Data, &writefds); - timeval t; - t.tv_sec = 0; - t.tv_usec = 0; - if (select(0, nullptr, &writefds, nullptr, &t) == SOCKET_ERROR) + pollfd entry; + entry.events = POLLWRNORM; + if (WSAPoll(&entry, 1, 0) == SOCKET_ERROR) { - int error = WSAGetLastError(); - if (error == WSAEWOULDBLOCK) - return false; - LOG(Error, "Unable to check writeability of socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); + LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); return false; } - if (FD_ISSET(*(SOCKET*)socket.Data, &writefds)) + if (entry.revents & POLLWRNORM) return true; return false; } From 1b2dd0aff50d225311b0ce2cadd99f689ed5116a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 14:14:27 +0100 Subject: [PATCH 204/419] Fixes. --- Source/Engine/Platform/Win32/Win32Network.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index a2038def5..5d981ea2c 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -292,13 +292,18 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net return false; } +static thread_local int32 error; bool Win32Network::IsReadable(NetworkSocket& socket) { pollfd entry; + entry.fd = *(SOCKET*)socket.Data; entry.events = POLLRDNORM; if (WSAPoll(&entry, 1, 0) == SOCKET_ERROR) { - LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); + error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + return false; + LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); return false; } if (entry.revents & POLLRDNORM) @@ -309,10 +314,14 @@ bool Win32Network::IsReadable(NetworkSocket& socket) bool Win32Network::IsWriteable(NetworkSocket& socket) { pollfd entry; + entry.fd = *(SOCKET*)socket.Data; entry.events = POLLWRNORM; if (WSAPoll(&entry, 1, 0) == SOCKET_ERROR) { - LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); + error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + return false; + LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); return false; } if (entry.revents & POLLWRNORM) From a51d47a4bb97bdf12349270d013e824a88844939 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 14:29:53 +0100 Subject: [PATCH 205/419] Add partial docs. --- Source/Engine/Platform/Base/NetworkBase.h | 85 ++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 0f0f89cb7..99a93ebf5 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #pragma once @@ -74,17 +74,100 @@ struct FLAXENGINE_API NetworkSocketGroup class FLAXENGINE_API NetworkBase { public: + /// + /// Initialize the network module. + /// + /// Return true on error. Otherwise false. static bool Init(); + + /// + /// Deinitialize the network module. + /// static void Exit(); + + /// + /// Create a new native socket. + /// + /// The socket struct to fill in. + /// The protocol. + /// The ip version. + /// Return true on error. Otherwise false. static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); + + /// + /// Close native socket. + /// + /// The socket. + /// Return true on error. Otherwise false. static bool DestroySocket(NetworkSocket& socket); + + /// + /// Set the specified socket option. + /// + /// The socket. + /// The option. + /// The value. + /// Return true on error. Otherwise false. static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value); + + /// + /// Set the specified socket option. + /// + /// The socket. + /// The option. + /// The value. + /// Return true on error. Otherwise false. static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value); + + /// + /// Get the specified socket option. + /// + /// The socket. + /// The option. + /// The returned value. + /// Return true on error. Otherwise false. static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value); + + /// + /// Get the specified socket option. + /// + /// The socket. + /// The option. + /// The returned value. + /// Return true on error. Otherwise false. static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value); + + /// + /// Connect a socket to the specified end point. + /// + /// The socket. + /// The end point. + /// Return true on error. Otherwise false. static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + + /// + /// Bind a socket to the specified end point. + /// + /// The socket. + /// The end point. + /// Return true on error. Otherwise false. static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); + + /// + /// Listen for incoming connection. + /// + /// The socket. + /// Pending connection queue size. + /// Return true on error. Otherwise false. static bool Listen(NetworkSocket& socket, uint16 queueSize); + + /// + /// Accept a pending connection. + /// + /// The socket. + /// The newly connected socket. + /// The end point of the new socket. + /// Return true on error. Otherwise false. static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); static bool IsReadable(NetworkSocket& socket); static bool IsWriteable(NetworkSocket& socket); From 1df47bd8c5dd6c33655e8148abf7cc8aaefad856 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 14:50:45 +0100 Subject: [PATCH 206/419] Add partial docs. --- Source/Engine/Platform/Base/NetworkBase.h | 72 +++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 99a93ebf5..3dff55a56 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -169,15 +169,87 @@ class FLAXENGINE_API NetworkBase /// The end point of the new socket. /// Return true on error. Otherwise false. static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); + + /// + /// Check for socket readability. + /// + /// The socket. + /// Return true when data is available. Otherwise false. static bool IsReadable(NetworkSocket& socket); + + /// + /// Check for socket writeability. + /// + /// The socket. + /// Return true when data can be written. Otherwise false. static bool IsWriteable(NetworkSocket& socket); + + /// + /// Update sockets states. + /// + /// The sockets group. + /// Return -1 on error, The number of elements where states are nonzero, otherwise 0. static int32 Poll(NetworkSocketGroup& group); + + /// + /// Retrieve socket state. + /// + /// The group. + /// The socket index in group. + /// The returned state. + /// Return true on error. Otherwise false. static bool GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state); + + /// + /// Add a socket to a group. + /// + /// The group. + /// The socket. + /// Return the socket index in group or -1 on error. static int32 AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket); + + /// + /// Clear the socket group. + /// + /// The group. static void ClearGroup(NetworkSocketGroup& group); + + /// + /// Write data to the socket. + /// + /// The socket. + /// The data to write. + /// The length of data. + /// If protocol is UDP , the destination end point. Otherwise nullptr. + /// Return -1 on error, otherwise bytes written. static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); + + /// + /// Read data on the socket. + /// + /// The socket. + /// The buffer. + /// Size of the buffer. + /// If UDP, the end point from where data is coming. Otherwise nullptr. + /// Return -1 on error, otherwise bytes read. static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); + + /// + /// Create an end point. + /// + /// The address (hostname, IPv4 or IPv6). + /// The port. + /// The ip version. + /// The created end point. + /// True if the end point will be connected or binded. + /// Return true on error. Otherwise false. static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); + + /// + /// Remap an ipv4 end point to an ipv6 one. + /// + /// The ipv4 end point. + /// The ipv6 end point. static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint& endPoint); }; From 21190752406ff33a5f9be89e7378fdfa9468923c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 14:51:01 +0100 Subject: [PATCH 207/419] Fix. --- Source/Engine/Platform/Win32/Win32Network.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 5d981ea2c..ea2e7c46d 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -379,7 +379,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, if (endPoint != nullptr && socket.IPVersion != endPoint->IPVersion) { LOG(Error, "Unable to send data, Socket.IPVersion != EndPoint.IPVersion! Socket : {0}", *(SOCKET*)socket.Data); - return 0; + return -1; } uint32 size; if (endPoint == nullptr && socket.Protocol == NetworkProtocolType::Tcp) From 01a3a8010323323990ef6c093734893e14de8388 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 15:34:57 +0100 Subject: [PATCH 208/419] Fix compilation --- Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp index 0f650da2c..fa47c5ee5 100644 --- a/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Linux/LinuxPlatformTools.cpp @@ -36,6 +36,7 @@ ArchitectureType LinuxPlatformTools::GetArchitecture() const bool LinuxPlatformTools::OnDeployBinaries(CookingData& data) { + const auto gameSettings = GameSettings::Get(); const auto platformSettings = LinuxPlatformSettings::Get(); const auto outputPath = data.OutputPath; @@ -64,7 +65,7 @@ bool LinuxPlatformTools::OnDeployBinaries(CookingData& data) // Apply game executable file name #if !BUILD_DEBUG const String outputExePath = outputPath / TEXT("FlaxGame"); - const String gameExePath = outputPath / GameSettings::ProductName; + const String gameExePath = outputPath / gameSettings->ProductName; if (FileSystem::FileExists(outputExePath) && gameExePath.Compare(outputExePath, StringSearchCase::IgnoreCase) == 0) { if (FileSystem::MoveFile(gameExePath, outputExePath, true)) From 2bcbf23bca7061db9611ed29adec517d81fc3333 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 15:37:33 +0100 Subject: [PATCH 209/419] All base functions return true. --- Source/Engine/Platform/Base/NetworkBase.cpp | 36 ++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index b4f1110ab..ab05a4c06 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -4,7 +4,7 @@ bool NetworkBase::Init() { - return false; + return true; } void NetworkBase::Exit() @@ -13,57 +13,57 @@ void NetworkBase::Exit() bool NetworkBase::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv) { - return false; + return true; } bool NetworkBase::DestroySocket(NetworkSocket& socket) { - return false; + return true; } bool NetworkBase::SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value) { - return false; + return true; } bool NetworkBase::SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value) { - return false; + return true; } bool NetworkBase::GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value) { - return false; + return true; } bool NetworkBase::GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value) { - return false; + return true; } bool NetworkBase::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) { - return false; + return true; } bool NetworkBase::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) { - return false; + return true; } bool NetworkBase::Listen(NetworkSocket& socket, uint16 queueSize) { - return false; + return true; } bool NetworkBase::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint) { - return false; + return true; } bool NetworkBase::IsReadable(NetworkSocket& socket) { - return false; + return true; } bool NetworkBase::IsWriteable(NetworkSocket& socket) @@ -73,17 +73,17 @@ bool NetworkBase::IsWriteable(NetworkSocket& socket) int32 NetworkBase::Poll(NetworkSocketGroup& group) { - return 0; + return -1; } bool NetworkBase::GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state) { - return false; + return true; } int32 NetworkBase::AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket) { - return 0; + return -1; } void NetworkBase::ClearGroup(NetworkSocketGroup& group) @@ -93,17 +93,17 @@ void NetworkBase::ClearGroup(NetworkSocketGroup& group) int32 NetworkBase::WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint) { - return 0; + return -1; } int32 NetworkBase::ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint) { - return 0; + return -1; } bool NetworkBase::CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable) { - return false; + return true; } NetworkEndPoint NetworkBase::RemapEndPointToIPv6(NetworkEndPoint& endPoint) From 8ac0e7491aa8e223cf5d5859073eb1d3da0f24d7 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 15:41:39 +0100 Subject: [PATCH 210/419] Typo Initialize -> Initializes, and so on ... --- Source/Engine/Platform/Base/NetworkBase.h | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 3dff55a56..4b9c10196 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -75,18 +75,18 @@ class FLAXENGINE_API NetworkBase { public: /// - /// Initialize the network module. + /// Initializes the network module. /// /// Return true on error. Otherwise false. static bool Init(); /// - /// Deinitialize the network module. + /// Deinitializes the network module. /// static void Exit(); /// - /// Create a new native socket. + /// Creates a new native socket. /// /// The socket struct to fill in. /// The protocol. @@ -95,14 +95,14 @@ class FLAXENGINE_API NetworkBase static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); /// - /// Close native socket. + /// Closes native socket. /// /// The socket. /// Return true on error. Otherwise false. static bool DestroySocket(NetworkSocket& socket); /// - /// Set the specified socket option. + /// Sets the specified socket option. /// /// The socket. /// The option. @@ -111,7 +111,7 @@ class FLAXENGINE_API NetworkBase static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value); /// - /// Set the specified socket option. + /// Sets the specified socket option. /// /// The socket. /// The option. @@ -120,7 +120,7 @@ class FLAXENGINE_API NetworkBase static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value); /// - /// Get the specified socket option. + /// Gets the specified socket option. /// /// The socket. /// The option. @@ -129,7 +129,7 @@ class FLAXENGINE_API NetworkBase static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value); /// - /// Get the specified socket option. + /// Gets the specified socket option. /// /// The socket. /// The option. @@ -138,7 +138,7 @@ class FLAXENGINE_API NetworkBase static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value); /// - /// Connect a socket to the specified end point. + /// Connects a socket to the specified end point. /// /// The socket. /// The end point. @@ -146,7 +146,7 @@ class FLAXENGINE_API NetworkBase static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); /// - /// Bind a socket to the specified end point. + /// Binds a socket to the specified end point. /// /// The socket. /// The end point. @@ -154,7 +154,7 @@ class FLAXENGINE_API NetworkBase static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); /// - /// Listen for incoming connection. + /// Listens for incoming connection. /// /// The socket. /// Pending connection queue size. @@ -162,7 +162,7 @@ class FLAXENGINE_API NetworkBase static bool Listen(NetworkSocket& socket, uint16 queueSize); /// - /// Accept a pending connection. + /// Accepts a pending connection. /// /// The socket. /// The newly connected socket. @@ -171,28 +171,28 @@ class FLAXENGINE_API NetworkBase static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); /// - /// Check for socket readability. + /// Checks for socket readability. /// /// The socket. /// Return true when data is available. Otherwise false. static bool IsReadable(NetworkSocket& socket); /// - /// Check for socket writeability. + /// Checks for socket writeability. /// /// The socket. /// Return true when data can be written. Otherwise false. static bool IsWriteable(NetworkSocket& socket); /// - /// Update sockets states. + /// Updates sockets states. /// /// The sockets group. /// Return -1 on error, The number of elements where states are nonzero, otherwise 0. static int32 Poll(NetworkSocketGroup& group); /// - /// Retrieve socket state. + /// Retrieves socket state. /// /// The group. /// The socket index in group. @@ -201,7 +201,7 @@ class FLAXENGINE_API NetworkBase static bool GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state); /// - /// Add a socket to a group. + /// Adds a socket to a group. /// /// The group. /// The socket. @@ -209,13 +209,13 @@ class FLAXENGINE_API NetworkBase static int32 AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket); /// - /// Clear the socket group. + /// Clears the socket group. /// /// The group. static void ClearGroup(NetworkSocketGroup& group); /// - /// Write data to the socket. + /// Writes data to the socket. /// /// The socket. /// The data to write. @@ -225,7 +225,7 @@ class FLAXENGINE_API NetworkBase static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); /// - /// Read data on the socket. + /// Reads data on the socket. /// /// The socket. /// The buffer. @@ -235,7 +235,7 @@ class FLAXENGINE_API NetworkBase static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); /// - /// Create an end point. + /// Creates an end point. /// /// The address (hostname, IPv4 or IPv6). /// The port. @@ -246,7 +246,7 @@ class FLAXENGINE_API NetworkBase static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); /// - /// Remap an ipv4 end point to an ipv6 one. + /// Remaps an ipv4 end point to an ipv6 one. /// /// The ipv4 end point. /// The ipv6 end point. From 15d24308e87fe13c351edff7d88b6d6242e6d748 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 15:44:08 +0100 Subject: [PATCH 211/419] Fix "Returns true on error, otherwise false." & Return"s". --- Source/Engine/Platform/Base/NetworkBase.h | 36 +++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 4b9c10196..805ca246e 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -91,14 +91,14 @@ class FLAXENGINE_API NetworkBase /// The socket struct to fill in. /// The protocol. /// The ip version. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); /// /// Closes native socket. /// /// The socket. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool DestroySocket(NetworkSocket& socket); /// @@ -107,7 +107,7 @@ class FLAXENGINE_API NetworkBase /// The socket. /// The option. /// The value. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value); /// @@ -116,7 +116,7 @@ class FLAXENGINE_API NetworkBase /// The socket. /// The option. /// The value. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value); /// @@ -125,7 +125,7 @@ class FLAXENGINE_API NetworkBase /// The socket. /// The option. /// The returned value. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool* value); /// @@ -134,7 +134,7 @@ class FLAXENGINE_API NetworkBase /// The socket. /// The option. /// The returned value. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32* value); /// @@ -142,7 +142,7 @@ class FLAXENGINE_API NetworkBase /// /// The socket. /// The end point. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); /// @@ -150,7 +150,7 @@ class FLAXENGINE_API NetworkBase /// /// The socket. /// The end point. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint); /// @@ -158,7 +158,7 @@ class FLAXENGINE_API NetworkBase /// /// The socket. /// Pending connection queue size. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool Listen(NetworkSocket& socket, uint16 queueSize); /// @@ -167,28 +167,28 @@ class FLAXENGINE_API NetworkBase /// The socket. /// The newly connected socket. /// The end point of the new socket. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint); /// /// Checks for socket readability. /// /// The socket. - /// Return true when data is available. Otherwise false. + /// Returns true when data is available. Otherwise false. static bool IsReadable(NetworkSocket& socket); /// /// Checks for socket writeability. /// /// The socket. - /// Return true when data can be written. Otherwise false. + /// Returns true when data can be written. Otherwise false. static bool IsWriteable(NetworkSocket& socket); /// /// Updates sockets states. /// /// The sockets group. - /// Return -1 on error, The number of elements where states are nonzero, otherwise 0. + /// Returns -1 on error, The number of elements where states are nonzero, otherwise 0. static int32 Poll(NetworkSocketGroup& group); /// @@ -197,7 +197,7 @@ class FLAXENGINE_API NetworkBase /// The group. /// The socket index in group. /// The returned state. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state); /// @@ -205,7 +205,7 @@ class FLAXENGINE_API NetworkBase /// /// The group. /// The socket. - /// Return the socket index in group or -1 on error. + /// Returns the socket index in group or -1 on error. static int32 AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket); /// @@ -221,7 +221,7 @@ class FLAXENGINE_API NetworkBase /// The data to write. /// The length of data. /// If protocol is UDP , the destination end point. Otherwise nullptr. - /// Return -1 on error, otherwise bytes written. + /// Returns -1 on error, otherwise bytes written. static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr); /// @@ -231,7 +231,7 @@ class FLAXENGINE_API NetworkBase /// The buffer. /// Size of the buffer. /// If UDP, the end point from where data is coming. Otherwise nullptr. - /// Return -1 on error, otherwise bytes read. + /// Returns -1 on error, otherwise bytes read. static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr); /// @@ -242,7 +242,7 @@ class FLAXENGINE_API NetworkBase /// The ip version. /// The created end point. /// True if the end point will be connected or binded. - /// Return true on error. Otherwise false. + /// Returns true on error, otherwise false. static bool CreateEndPoint(String* address, String* port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = false); /// From af1dfc0d08d187c7f9fdb97157c9b69b76385d7f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 15:45:35 +0100 Subject: [PATCH 212/419] Remove "Place holder" comments. --- Source/Engine/Platform/Network.h | 10 +++++----- Source/Engine/Platform/Types.h | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Source/Engine/Platform/Network.h b/Source/Engine/Platform/Network.h index a361eb6bf..b8c198770 100644 --- a/Source/Engine/Platform/Network.h +++ b/Source/Engine/Platform/Network.h @@ -5,15 +5,15 @@ #if PLATFORM_WINDOWS #include "Win32/Win32Network.h" #elif PLATFORM_UWP -#include "Win32/Win32Network.h" // place holder +#include "Win32/Win32Network.h" #elif PLATFORM_LINUX -#include "Base/NetworkBase.h" // place holder +#include "Base/NetworkBase.h" #elif PLATFORM_PS4 -#include "Base/NetworkBase.h" // place holder +#include "Base/NetworkBase.h" #elif PLATFORM_XBOX_SCARLETT -#include "Win32/Win32Network.h" // it's probably isn't working +#include "Win32/Win32Network.h" #elif PLATFORM_ANDROID -#include "Base/NetworkBase.h" // place holder +#include "Base/NetworkBase.h" #else #error Missing Network implementation! #endif diff --git a/Source/Engine/Platform/Types.h b/Source/Engine/Platform/Types.h index cc1bcafcf..ad67e0b01 100644 --- a/Source/Engine/Platform/Types.h +++ b/Source/Engine/Platform/Types.h @@ -45,8 +45,8 @@ class Win32Thread; typedef Win32Thread Thread; class UWPWindow; typedef UWPWindow Window; -class NetworkBase; // place holder -typedef NetworkBase Network; // place holder +class NetworkBase; +typedef NetworkBase Network; #elif PLATFORM_LINUX @@ -68,8 +68,8 @@ class LinuxThread; typedef LinuxThread Thread; class LinuxWindow; typedef LinuxWindow Window; -class NetworkBase; // place holder -typedef NetworkBase Network; // place holder +class NetworkBase; +typedef NetworkBase Network; #elif PLATFORM_PS4 @@ -91,8 +91,8 @@ class PS4Thread; typedef PS4Thread Thread; class PS4Window; typedef PS4Window Window; -class NetworkBase; // place holder -typedef NetworkBase Network; // place holder +class NetworkBase; +typedef NetworkBase Network; #elif PLATFORM_XBOX_SCARLETT @@ -114,8 +114,8 @@ class Win32Thread; typedef Win32Thread Thread; class XboxScarlettWindow; typedef XboxScarlettWindow Window; -class NetworkBase; // place holder -typedef NetworkBase Network; // place holder +class NetworkBase; +typedef NetworkBase Network; #elif PLATFORM_ANDROID @@ -137,8 +137,8 @@ class AndroidThread; typedef AndroidThread Thread; class AndroidWindow; typedef AndroidWindow Window; -class NetworkBase; // place holder -typedef NetworkBase Network; // place holder +class NetworkBase; +typedef NetworkBase Network; #else From aaac0aec963e4efebfc79b8916a67781819b2c5b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 15:46:27 +0100 Subject: [PATCH 213/419] Brace to the next line. --- Source/Engine/Platform/Win32/Win32Network.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index ea2e7c46d..508374365 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -97,11 +97,13 @@ static void PrintAddrFromInfo(addrinfoW& info) for (curr = &info; curr != nullptr; curr = curr->ai_next) { void* addr; - if (curr->ai_family == AF_INET) { + if (curr->ai_family == AF_INET) + { sockaddr_in* ipv4 = (struct sockaddr_in*)curr->ai_addr; addr = &(ipv4->sin_addr); } - else { + else + { sockaddr_in6* ipv6 = (struct sockaddr_in6*)curr->ai_addr; addr = &(ipv6->sin6_addr); } From 54b9eaa9692184b535c182fa4bb6be4dbdd9bf7f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 15:48:48 +0100 Subject: [PATCH 214/419] Move static var to local. --- Source/Engine/Platform/Win32/Win32Network.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 508374365..a25a8653a 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -294,7 +294,6 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net return false; } -static thread_local int32 error; bool Win32Network::IsReadable(NetworkSocket& socket) { pollfd entry; @@ -302,7 +301,7 @@ bool Win32Network::IsReadable(NetworkSocket& socket) entry.events = POLLRDNORM; if (WSAPoll(&entry, 1, 0) == SOCKET_ERROR) { - error = WSAGetLastError(); + int32 error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) return false; LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); @@ -320,7 +319,7 @@ bool Win32Network::IsWriteable(NetworkSocket& socket) entry.events = POLLWRNORM; if (WSAPoll(&entry, 1, 0) == SOCKET_ERROR) { - error = WSAGetLastError(); + int32 error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) return false; LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); @@ -331,21 +330,19 @@ bool Win32Network::IsWriteable(NetworkSocket& socket) return false; } -static thread_local int32 pollret; int32 Win32Network::Poll(NetworkSocketGroup& group) { - pollret = WSAPoll((pollfd*)group.Data, group.Count, 0); + int32 pollret = WSAPoll((pollfd*)group.Data, group.Count, 0); if (pollret == SOCKET_ERROR) LOG(Error, "Unable to poll socket group! Error : {0}", GetLastErrorMessage().Get()); return pollret; } -static thread_local pollfd* pollptr; bool Win32Network::GetSocketState(NetworkSocketGroup& group, uint32 index, NetworkSocketState& state) { if (index >= SOCKGROUP_MAXCOUNT) return true; - pollptr = (pollfd*)&group.Data[index * SOCKGROUP_ITEMSIZE]; + pollfd* pollptr = (pollfd*)&group.Data[index * SOCKGROUP_ITEMSIZE]; if (pollptr->revents & POLLERR) state.Error = true; if (pollptr->revents & POLLHUP) @@ -359,11 +356,11 @@ bool Win32Network::GetSocketState(NetworkSocketGroup& group, uint32 index, Netwo return false; } -static thread_local pollfd pollinfo; int32 Win32Network::AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& socket) { if (group.Count >= SOCKGROUP_MAXCOUNT) return -1; + pollfd pollinfo; pollinfo.fd = *(SOCKET*)socket.Data; pollinfo.events = POLLRDNORM | POLLWRNORM; *(pollfd*)&group.Data[group.Count * SOCKGROUP_ITEMSIZE] = pollinfo; From c20f0fa430c325a9f38b82e690bc1ea006910d5c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 15:55:51 +0100 Subject: [PATCH 215/419] Remove Init & Exit, logic is now in Win32Platform.cpp. --- Source/Engine/Platform/Base/NetworkBase.cpp | 9 -------- Source/Engine/Platform/Base/NetworkBase.h | 11 ---------- Source/Engine/Platform/Win32/Win32Network.cpp | 13 ----------- Source/Engine/Platform/Win32/Win32Network.h | 2 -- .../Engine/Platform/Win32/Win32Platform.cpp | 22 +++++++++++++++---- 5 files changed, 18 insertions(+), 39 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index ab05a4c06..6a83cd9f6 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -2,15 +2,6 @@ #include "NetworkBase.h" -bool NetworkBase::Init() -{ - return true; -} - -void NetworkBase::Exit() -{ -} - bool NetworkBase::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv) { return true; diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 805ca246e..e5c816b77 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -74,17 +74,6 @@ struct FLAXENGINE_API NetworkSocketGroup class FLAXENGINE_API NetworkBase { public: - /// - /// Initializes the network module. - /// - /// Return true on error. Otherwise false. - static bool Init(); - - /// - /// Deinitializes the network module. - /// - static void Exit(); - /// /// Creates a new native socket. /// diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index a25a8653a..3783cd879 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -14,7 +14,6 @@ static_assert(sizeof NetworkEndPoint::Data >= sizeof sockaddr_in6, "NetworkEndPo static const IN6_ADDR v4MappedPrefix = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } }; -static WSAData _wsaData; /* * Known issues : @@ -136,18 +135,6 @@ static void TranslateSockOptToNative(NetworkSocketOption option, int32* level, i } } -bool Win32Network::Init() -{ - if (WSAStartup(MAKEWORD(2, 0), &_wsaData) != 0) - return true; - return false; -} - -void Win32Network::Exit() -{ - WSACleanup(); -} - bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv) { socket.Protocol = proto; diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index 8700d9f19..b0e313056 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -13,8 +13,6 @@ class FLAXENGINE_API Win32Network : public NetworkBase public: // [NetworkBase] - static bool Init(); - static void Exit(); static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); static bool DestroySocket(NetworkSocket& socket); static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value); diff --git a/Source/Engine/Platform/Win32/Win32Platform.cpp b/Source/Engine/Platform/Win32/Win32Platform.cpp index db6151a17..f74485748 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.cpp +++ b/Source/Engine/Platform/Win32/Win32Platform.cpp @@ -12,6 +12,7 @@ #include "IncludeWindowsHeaders.h" #include "Engine/Core/Collections/HashFunctions.h" #include "Engine/Platform/Network.h" +#include "Engine/Core/Log.h" #include #include @@ -28,6 +29,8 @@ namespace double CyclesToSeconds; } +static WSAData _wsaData; + // Helper function to count set bits in the processor mask DWORD CountSetBits(ULONG_PTR bitMask) { @@ -45,6 +48,18 @@ DWORD CountSetBits(ULONG_PTR bitMask) return bitSetCount; } +static String GetLastErrorMessage() +{ + wchar_t* s = nullptr; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&s), 0, nullptr); + String str(s); + LocalFree(s); + return str; +} + bool Win32Platform::Init() { if (PlatformBase::Init()) @@ -213,15 +228,14 @@ bool Win32Platform::Init() DeviceId.D = (uint32)cpuInfo.ClockSpeed * cpuInfo.LogicalProcessorCount * cpuInfo.ProcessorCoreCount * cpuInfo.CacheLineSize; } - //TODO: log error if true - Win32Network::Init(); - + if (WSAStartup(MAKEWORD(2, 0), &_wsaData) != 0) + LOG(Error, "Unable to initializes native network! Error : {0}", GetLastErrorMessage().Get()); return false; } void Win32Platform::Exit() { - Network::Exit(); + WSACleanup(); } void Win32Platform::MemoryBarrier() From 254bdacc73a038de74bd67458b16a889dc03c3fc Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 17:07:37 +0100 Subject: [PATCH 216/419] Cleanup. --- Source/Engine/Platform/Win32/Win32Network.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 3783cd879..fe1ef9d48 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -52,8 +52,6 @@ static NetworkIPVersion GetIPVersionFromAddr(const sockaddr& addr) return addr.sa_family == AF_INET6 ? NetworkIPVersion::IPv6 : NetworkIPVersion::IPv4;; } -//TODO: can be simplified by casting addr to the right struct and ntohl the port, so we can get rid of getnameinfo -// getnameinfo return a name ( like JimiPC ), not the ip! static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) { uint32 size = GetAddrSize(*addr); From 96e5e20e66e3db283dd25ca5aa70fe7a7b8e133a Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Tue, 26 Jan 2021 19:26:38 +0100 Subject: [PATCH 217/419] Added Node --- Source/Editor/Surface/Archetypes/Material.cs | 20 +++++++++++++++++++ .../MaterialGenerator.Material.cpp | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 43110a9de..1862ed58e 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -701,6 +701,26 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 5), } }, + new NodeArchetype + { + TypeID = 29, + Title = "UV Tiling & Offset", + Description = "Takes UVs and applies tiling and offset", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(150, 60), + DefaultValues = new object[] + { + new Vector2(1, 1), + new Vector2(0, 0) + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "UV", true, typeof(Vector2), 0), + NodeElementArchetype.Factory.Input(1, "Tiling", true, typeof(Vector2), 1, 0), + NodeElementArchetype.Factory.Input(2, "Offset", true, typeof(Vector2), 2, 1), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3), + } + } }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 5a93c71a7..b042336ff 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -381,6 +381,16 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) value = writeLocal(ValueType::Float, String::Format(TEXT("{0} ? (1 - {1}) : {1}"), invert.Value, x2.Value), node); break; + } + // Tiling & Offset + case 29: + { + auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2(); + auto tiling = tryGetValue(node->GetBox(1), node->Values[0]).AsVector2(); + auto offset = tryGetValue(node->GetBox(2), node->Values[1]).AsVector2(); + + value = writeLocal(ValueType::Vector2, String::Format(TEXT("{0} * {1} + {2}"), uv.Value, tiling.Value, offset.Value), node); + break; } default: break; From 62b3f7c9c2012a0b72607119d374506479a63848 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Tue, 26 Jan 2021 19:37:06 +0100 Subject: [PATCH 218/419] Adjusted node size --- Source/Editor/Surface/Archetypes/Material.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 1862ed58e..c3fb4f428 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -707,7 +707,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "UV Tiling & Offset", Description = "Takes UVs and applies tiling and offset", Flags = NodeFlags.MaterialGraph, - Size = new Vector2(150, 60), + Size = new Vector2(175, 60), DefaultValues = new object[] { new Vector2(1, 1), From db17c80e0abf481e0096ac9c7e9ebfc978457410 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Tue, 26 Jan 2021 19:41:29 +0100 Subject: [PATCH 219/419] Use approriate fields instead --- Source/Editor/Surface/Archetypes/Material.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index c3fb4f428..09fd8e5fa 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -710,8 +710,8 @@ namespace FlaxEditor.Surface.Archetypes Size = new Vector2(175, 60), DefaultValues = new object[] { - new Vector2(1, 1), - new Vector2(0, 0) + Vector2.One, + Vector2.Zero }, Elements = new[] { From 674d1fbcc5eb149be868783294eb332f2b9fa229 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 20:06:47 +0100 Subject: [PATCH 220/419] Handle error. --- Source/Engine/Platform/Win32/Win32Network.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index fe1ef9d48..fbbe19b58 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -267,7 +267,10 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net int32 size = sizeof sockaddr_in6; if ((sock = accept(*(SOCKET*)serverSock.Data, (sockaddr*)&addr, &size)) == INVALID_SOCKET) { - LOG(Warning, "Unable to accept incoming connection! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetLastErrorMessage().Get()); + int32 error = WSAGetLastError(); + if (error == WSAEWOULDBLOCK) + return false; + LOG(Warning, "Unable to accept incoming connection! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetErrorMessage(error).Get()); return true; } memcpy(newSock.Data, &sock, sizeof sock); From 09049990bd71a0f52749dbfde41575e5e974772d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 20:07:09 +0100 Subject: [PATCH 221/419] Default value. --- Source/Engine/Platform/Base/NetworkBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index e5c816b77..7e039e2a6 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -67,7 +67,7 @@ struct FLAXENGINE_API NetworkSocketState struct FLAXENGINE_API NetworkSocketGroup { - uint32 Count; + uint32 Count = 0; byte Data[SOCKGROUP_MAXCOUNT * 16] = {}; }; From 5f85e9ae96753251c8b4cf7034d1f4e01d33e99e Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Tue, 26 Jan 2021 20:07:35 +0100 Subject: [PATCH 222/419] Reset state. --- Source/Engine/Platform/Win32/Win32Network.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index fbbe19b58..edadd2267 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -331,6 +331,7 @@ bool Win32Network::GetSocketState(NetworkSocketGroup& group, uint32 index, Netwo if (index >= SOCKGROUP_MAXCOUNT) return true; pollfd* pollptr = (pollfd*)&group.Data[index * SOCKGROUP_ITEMSIZE]; + memset(&state, 0, sizeof state); if (pollptr->revents & POLLERR) state.Error = true; if (pollptr->revents & POLLHUP) From 269f1fc8c1d83213887f2cd2b6c63415b50af128 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Jan 2021 14:28:44 +0100 Subject: [PATCH 223/419] Fix ResizeAuto in SurfaceNode to include custom controls --- Source/Editor/Surface/SurfaceNode.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 1989992df..7cbf60c09 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -185,9 +185,9 @@ namespace FlaxEditor.Surface var rightWidth = 40.0f; var boxLabelFont = Style.Current.FontSmall; var titleLabelFont = Style.Current.FontLarge; - for (int i = 0; i < Elements.Count; i++) + for (int i = 0; i < Children.Count; i++) { - if (Elements[i] is InputBox inputBox) + if (Children[i] is InputBox inputBox) { var boxWidth = boxLabelFont.MeasureText(inputBox.Text).X + 20; if (inputBox.DefaultValueEditor != null) @@ -195,15 +195,23 @@ namespace FlaxEditor.Surface leftWidth = Mathf.Max(leftWidth, boxWidth); leftHeight = Mathf.Max(leftHeight, inputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } - else if (Elements[i] is OutputBox outputBox) + else if (Children[i] is OutputBox outputBox) { rightWidth = Mathf.Max(rightWidth, boxLabelFont.MeasureText(outputBox.Text).X + 20); rightHeight = Mathf.Max(rightHeight, outputBox.Archetype.Position.Y - Constants.NodeMarginY - Constants.NodeHeaderSize + 20.0f); } - else if (Elements[i] is Control control) + else if (Children[i] is Control control) { - width = Mathf.Max(width, control.Width + 10); - height = Mathf.Max(height, control.Height + 10); + if (control.AnchorPreset == AnchorPresets.TopLeft) + { + width = Mathf.Max(width, control.Right + 4 - Constants.NodeMarginX); + height = Mathf.Max(height, control.Bottom + 4 - Constants.NodeMarginY - Constants.NodeHeaderSize); + } + else + { + width = Mathf.Max(width, control.Width + 4); + height = Mathf.Max(height, control.Height + 4); + } } } width = Mathf.Max(width, leftWidth + rightWidth + 10); From 59858ecb277c7f9d9c982d5067bcb80aee1e5d42 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 22:52:08 +0100 Subject: [PATCH 224/419] Fix Visual Script invoke method node signature check for output params passed via reference --- Source/Editor/Surface/Archetypes/Function.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 14850a37d..9f7e3757a 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -867,8 +867,15 @@ namespace FlaxEditor.Surface.Archetypes for (int i = 0; i < signature.Params.Length; i++) { ref var param = ref signature.Params[i]; - if (param.Type != memberParameters[i].Type || param.IsOut != memberParameters[i].IsOut) + ref var paramMember = ref memberParameters[i]; + if (param.Type != paramMember.Type || param.IsOut != paramMember.IsOut) { + // Special case: param.Type is serialized as just a type while paramMember.Type might be a reference for output parameters (eg. `out Int32` vs `out Int32&`) + var paramMemberTypeName = paramMember.Type.TypeName; + if (param.IsOut && param.IsOut == paramMember.IsOut && paramMember.Type.IsReference && !param.Type.IsReference && + paramMemberTypeName.Substring(0, paramMemberTypeName.Length - 1) == param.Type.TypeName) + continue; + isInvalid = true; break; } From 854d0147dd36c8b4b236074d25afa7953a6640ab Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Jan 2021 22:52:35 +0100 Subject: [PATCH 225/419] Fix Visject Node layout when adding boxes from code --- Source/Editor/Surface/SurfaceNode.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 7cbf60c09..abbd6a6a0 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -314,12 +314,16 @@ namespace FlaxEditor.Surface { if (type == ScriptType.Null) type = new ScriptType(typeof(object)); + + // Try to reuse box var box = GetBox(id); if ((isOut && box is InputBox) || (!isOut && box is OutputBox)) { box.Dispose(); box = null; } + + // Create new if missing if (box == null) { if (isOut) @@ -330,10 +334,15 @@ namespace FlaxEditor.Surface } else { + // Sync properties for exiting box box.Text = text; box.CurrentType = type; box.Y = Constants.NodeMarginY + Constants.NodeHeaderSize + yLevel * Constants.LayoutOffsetY; } + + // Update box + box.OnConnectionsChanged(); + return box; } From cb8ba2d757cf3f44431ffb0ea1b20799820824a9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 00:27:32 +0100 Subject: [PATCH 226/419] Fix WindowsFileSystem::ShowBrowseFolderDialog path length --- .../Editor/Windows/ContentWindow.ContextMenu.cs | 2 +- .../Platform/Windows/WindowsFileSystem.cpp | 17 +++-------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 1b4d2be31..f81220ac4 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -224,7 +224,7 @@ namespace FlaxEditor.Windows /// private void ExportSelection() { - if(FileSystem.ShowBrowseFolderDialog(Editor.Windows.MainWindow, null, "Select the output folder", out var outputFolder)) + if (FileSystem.ShowBrowseFolderDialog(Editor.Windows.MainWindow, null, "Select the output folder", out var outputFolder)) return; var selection = _view.Selection; diff --git a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp index e30747961..1816c9552 100644 --- a/Source/Engine/Platform/Windows/WindowsFileSystem.cpp +++ b/Source/Engine/Platform/Windows/WindowsFileSystem.cpp @@ -297,11 +297,6 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin // Randomly generated GUID used for storing the last location of this dialog const Guid folderGuid(0x53890ed9, 0xa55e47ba, 0xa970bdae, 0x72acedff); - // Allocate memory for the filenames - int32 maxPathSize = 2 * MAX_PATH; - Array pathBuffer; - pathBuffer.EnsureCapacity(maxPathSize); - ComPtr fd; if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&fd)))) { @@ -327,15 +322,9 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin LPWSTR resultPath; if (SUCCEEDED(si->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &resultPath))) { - int32 resultPathLength = StringUtils::Length(resultPath); - if (resultPathLength < pathBuffer.Capacity()) - { - StringUtils::Copy(pathBuffer.Get(), resultPath, resultPathLength); - CoTaskMemFree(resultPath); - - path = pathBuffer.Get(); - result = false; - } + path = resultPath; + CoTaskMemFree(resultPath); + result = false; } } } From f1763840c936a643ed785ffe061415888087c5a5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 00:28:38 +0100 Subject: [PATCH 227/419] Fix exporting textures in format R10G10B10A2 and R11G11B10 on Windows --- .../Tools/TextureTool/TextureTool.DirectXTex.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp index dbf51e965..0c423fed1 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp @@ -190,6 +190,16 @@ bool TextureTool::ExportTextureDirectXTex(ImageType type, const StringView& path } img = tmp.GetImage(0, 0, 0); } + else if (image.format == DXGI_FORMAT_R10G10B10A2_UNORM || image.format == DXGI_FORMAT_R11G11B10_FLOAT) + { + result = DirectX::Convert(image, DXGI_FORMAT_R8G8B8A8_UNORM, DirectX::TEX_FILTER_DEFAULT, DirectX::TEX_THRESHOLD_DEFAULT, tmp); + if (FAILED(result)) + { + LOG(Error, "Cannot convert texture, error: {0:x}", static_cast(result)); + return true; + } + img = tmp.GetImage(0, 0, 0); + } DirectX::WICCodecs codec; switch (type) From c8b0773661414a125f6d9809ed8dd754821da41c Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 27 Jan 2021 11:07:36 +1100 Subject: [PATCH 228/419] Fixed a bug --- Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 1e71a028d..57f669f7f 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -27,7 +27,7 @@ enum MaterialTemplateInputsMapping MaterialValue MaterialGenerator::getUVs(VariantType::Vector2, TEXT("input.TexCoord")); MaterialValue MaterialGenerator::getTime(VariantType::Float, TEXT("TimeParam")); MaterialValue MaterialGenerator::getNormal(VariantType::Vector3, TEXT("input.TBN[2]")); -MaterialValue MaterialGenerator::getNormalZero(VariantType::Vector3, TEXT("float3(.5, .5, 1)")); +MaterialValue MaterialGenerator::getNormalZero(VariantType::Vector3, TEXT("float3(0, 0, 1)")); MaterialValue MaterialGenerator::getVertexColor(VariantType::Vector4, TEXT("GetVertexColor(input)")); MaterialGenerator::MaterialGenerator() From 1925fe57245722d22c63f2a553897208b715875a Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Wed, 27 Jan 2021 01:39:50 +0100 Subject: [PATCH 229/419] Added DDX / DDY nodes --- Source/Editor/Surface/Archetypes/Material.cs | 34 ++++++++++++++++++- .../MaterialGenerator.Material.cpp | 16 +++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 09fd8e5fa..6e828fca4 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -720,7 +720,39 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(2, "Offset", true, typeof(Vector2), 2, 1), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector2), 3), } - } + }, + new NodeArchetype + { + TypeID = 30, + Title = "DDX", + Description = "Returns the partial derivative of the specified value with respect to the screen-space x-coordinate.", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + DependentBoxes = new[] { 1 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), + } + }, + new NodeArchetype + { + TypeID = 31, + Title = "DDY", + Description = "Returns the partial derivative of the specified value with respect to the screen-space y-coordinate.", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + DependentBoxes = new[] { 1 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), + } + }, }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index b042336ff..0d8a0e226 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -391,6 +391,22 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) value = writeLocal(ValueType::Vector2, String::Format(TEXT("{0} * {1} + {2}"), uv.Value, tiling.Value, offset.Value), node); break; + } + // DDX + case 30: + { + auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(inValue.Type, String::Format(TEXT("ddx({0})"), inValue.Value), node); + break; + } + // DDY + case 31: + { + auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(inValue.Type, String::Format(TEXT("ddy({0})"), inValue.Value), node); + break; } default: break; From cd2ae08da05f97c2f318dbc7edcbb393a91a5a60 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 10:37:15 +0100 Subject: [PATCH 230/419] Fixes and tweaks to the networking impl --- Source/Engine/Platform/Base/NetworkBase.cpp | 3 +- Source/Engine/Platform/Base/NetworkBase.h | 35 ++++++-- Source/Engine/Platform/UWP/UWPPlatform.cpp | 1 + Source/Engine/Platform/Win32/Win32Network.cpp | 90 ++++++++++--------- .../Engine/Platform/Win32/Win32Platform.cpp | 46 +++------- 5 files changed, 87 insertions(+), 88 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index 6a83cd9f6..06f297cc1 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "NetworkBase.h" @@ -101,4 +101,3 @@ NetworkEndPoint NetworkBase::RemapEndPointToIPv6(NetworkEndPoint& endPoint) { return NetworkEndPoint(); } - diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 7e039e2a6..6e6821ab4 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -5,20 +5,27 @@ #include "Engine/Core/Types/BaseTypes.h" #include "Engine/Core/Types/String.h" API_INJECT_CPP_CODE("#include \"Engine/Platform/Network.h\""); + #define SOCKGROUP_MAXCOUNT 64 #define SOCKGROUP_ITEMSIZE 16 enum class FLAXENGINE_API NetworkProtocolType { + /// Not specified. Undefined, + /// User Datagram Protocol. Udp, + /// Transmission Control Protocol. Tcp }; enum class FLAXENGINE_API NetworkIPVersion { + /// Not specified. Undefined, + /// Internet Protocol version 4. IPv4, + /// Internet Protocol version 6. IPv6 }; @@ -26,7 +33,7 @@ struct FLAXENGINE_API NetworkSocket { NetworkProtocolType Protocol = NetworkProtocolType::Undefined; NetworkIPVersion IPVersion = NetworkIPVersion::Undefined; - byte Data[8] = {}; // sizeof SOCKET = unsigned __int64 + byte Data[8] = {}; }; struct FLAXENGINE_API NetworkEndPoint @@ -34,26 +41,41 @@ struct FLAXENGINE_API NetworkEndPoint NetworkIPVersion IPVersion = NetworkIPVersion::Undefined; String Address; String Port; - byte Data[28] = {}; // sizeof sockaddr_in6 , biggest sockaddr that we will use + byte Data[28] = {}; }; -enum FLAXENGINE_API NetworkSocketOption +enum class FLAXENGINE_API NetworkSocketOption { + /// Enables debugging info recording. Debug, + /// Allows local address reusing. ReuseAddr, + /// Keeps connections alive. KeepAlive, + /// Indicates that outgoing data should be sent on whatever interface the socket is bound to and not a routed on some other interface. DontRoute, + /// Allows for sending broadcast data. Broadcast, + /// Uses the local loopback address when sending data from this socket. UseLoopback, + /// Lingers on close if data present. Linger, + /// Allows out-of-bound data to be returned in-line with regular data. OOBInline, + /// Socket send data buffer size. SendBuffer, + /// Socket receive data buffer size. RecvBuffer, + /// The timeout in milliseconds for blocking send calls. SendTimeout, + /// The timeout in milliseconds for blocking receive calls. RecvTimeout, + /// The last socket error code. Error, + /// Enables the Nagle algorithm for TCP sockets. NoDelay, - IPv6Only + /// Enables IPv6 communication only for TCP socket. + IPv6Only, }; struct FLAXENGINE_API NetworkSocketState @@ -68,12 +90,12 @@ struct FLAXENGINE_API NetworkSocketState struct FLAXENGINE_API NetworkSocketGroup { uint32 Count = 0; - byte Data[SOCKGROUP_MAXCOUNT * 16] = {}; + byte Data[SOCKGROUP_MAXCOUNT * SOCKGROUP_ITEMSIZE] = {}; }; class FLAXENGINE_API NetworkBase { - public: +public: /// /// Creates a new native socket. /// @@ -241,4 +263,3 @@ class FLAXENGINE_API NetworkBase /// The ipv6 end point. static NetworkEndPoint RemapEndPointToIPv6(NetworkEndPoint& endPoint); }; - diff --git a/Source/Engine/Platform/UWP/UWPPlatform.cpp b/Source/Engine/Platform/UWP/UWPPlatform.cpp index 3bd654f10..ed01948aa 100644 --- a/Source/Engine/Platform/UWP/UWPPlatform.cpp +++ b/Source/Engine/Platform/UWP/UWPPlatform.cpp @@ -111,6 +111,7 @@ void UWPPlatform::BeforeExit() void UWPPlatform::Exit() { + Win32Platform::Exit(); } int32 UWPPlatform::GetDpi() diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index edadd2267..2da68957b 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Win32Network.h" #include "Engine/Core/Log.h" @@ -12,8 +12,10 @@ static_assert(sizeof NetworkSocket::Data >= sizeof SOCKET, "NetworkSocket::Data is not big enough to contains SOCKET !"); static_assert(sizeof NetworkEndPoint::Data >= sizeof sockaddr_in6, "NetworkEndPoint::Data is not big enough to contains sockaddr_in6 !"); +// @formatter:off static const IN6_ADDR v4MappedPrefix = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } }; +// @formatter:on /* * Known issues : @@ -24,9 +26,9 @@ static String GetErrorMessage(int error) { wchar_t* s = nullptr; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&s), 0, nullptr); + nullptr, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&s), 0, nullptr); String str(s); LocalFree(s); return str; @@ -72,11 +74,11 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint) LOG(Error, "Unable to create endpoint, sockaddr must be INET or INET6! Family : {0}", addr->sa_family); return true; } - + char ip[INET6_ADDRSTRLEN]; if (inet_ntop(addr->sa_family, paddr, ip, INET6_ADDRSTRLEN) == nullptr) { - LOG(Error, "Unable to extract address from sockaddr! Error : {0}", GetLastErrorMessage().Get()); + LOG(Error, "Unable to extract address from sockaddr! Error : {0}", GetLastErrorMessage()); return true; } endPoint.Address = String(ip); @@ -107,7 +109,7 @@ static void PrintAddrFromInfo(addrinfoW& info) char str[INET6_ADDRSTRLEN]; inet_ntop(curr->ai_family, addr, str, INET6_ADDRSTRLEN); - LOG(Info, "ADDR INFO family : {0} socktype : {1}, proto : {2} address : {3}", curr->ai_family, curr->ai_socktype, curr->ai_protocol, StringAnsi(str).ToString().Get()); + LOG(Info, "ADDR INFO family : {0} socktype : {1}, proto : {2} address : {3}", curr->ai_family, curr->ai_socktype, curr->ai_protocol, StringAnsi(str).ToString()); } } @@ -115,21 +117,21 @@ static void TranslateSockOptToNative(NetworkSocketOption option, int32* level, i { switch (option) { - SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) - SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) - SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) - SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) - SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) - SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) - SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) - SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) - SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) - SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) - SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) - SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) - SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) - SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) - SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) + SOCKOPT(NetworkSocketOption::Debug, SOL_SOCKET, SO_DEBUG) + SOCKOPT(NetworkSocketOption::ReuseAddr, SOL_SOCKET, SO_REUSEADDR) + SOCKOPT(NetworkSocketOption::KeepAlive, SOL_SOCKET, SO_KEEPALIVE) + SOCKOPT(NetworkSocketOption::DontRoute, SOL_SOCKET, SO_DONTROUTE) + SOCKOPT(NetworkSocketOption::Broadcast, SOL_SOCKET, SO_BROADCAST) + SOCKOPT(NetworkSocketOption::UseLoopback, SOL_SOCKET, SO_USELOOPBACK) + SOCKOPT(NetworkSocketOption::Linger, SOL_SOCKET, SO_LINGER) + SOCKOPT(NetworkSocketOption::OOBInline, SOL_SOCKET, SO_OOBINLINE) + SOCKOPT(NetworkSocketOption::SendBuffer, SOL_SOCKET, SO_SNDBUF) + SOCKOPT(NetworkSocketOption::RecvBuffer, SOL_SOCKET, SO_RCVBUF) + SOCKOPT(NetworkSocketOption::SendTimeout, SOL_SOCKET, SO_SNDTIMEO) + SOCKOPT(NetworkSocketOption::RecvTimeout, SOL_SOCKET, SO_RCVTIMEO) + SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) + SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) + SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) } } @@ -144,14 +146,14 @@ bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto if ((sock = ::socket(family, stype, prot)) == INVALID_SOCKET) { - LOG(Error, "Can't create native socket! Error : {0}", GetLastErrorMessage().Get()); + LOG(Error, "Can't create native socket! Error : {0}", GetLastErrorMessage()); return true; } memcpy(socket.Data, &sock, sizeof sock); unsigned long value = 1; if (ioctlsocket(sock, FIONBIO, &value) == SOCKET_ERROR) { - LOG(Error, "Can't set socket to NON-BLOCKING type! Error : {0}", GetLastErrorMessage().Get()); + LOG(Error, "Can't set socket to NON-BLOCKING type! Error : {0}", GetLastErrorMessage()); return true; // Support using blocking socket , need to test it } return false; @@ -181,10 +183,10 @@ bool Win32Network::SetSocketOption(NetworkSocket& socket, NetworkSocketOption op int32 optnme = 0; TranslateSockOptToNative(option, &optlvl, &optnme); - + if (setsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)&value, sizeof value) == SOCKET_ERROR) { - LOG(Warning, "Unable to set socket option ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); + LOG(Warning, "Unable to set socket option ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage()); return true; } return false; @@ -202,13 +204,13 @@ bool Win32Network::GetSocketOption(NetworkSocket& socket, NetworkSocketOption op { int32 optlvl = 0; int32 optnme = 0; - + TranslateSockOptToNative(option, &optlvl, &optnme); - + int32 size; if (getsockopt(*(SOCKET*)socket.Data, optlvl, optnme, (char*)value, &size) == SOCKET_ERROR) { - LOG(Warning, "Unable to get socket option ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage().Get()); + LOG(Warning, "Unable to get socket option ! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetLastErrorMessage()); return true; } return false; @@ -222,7 +224,7 @@ bool Win32Network::ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoin int error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) return false; - LOG(Error, "Unable to connect socket to address! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetErrorMessage(error).Get()); + LOG(Error, "Unable to connect socket to address! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address, endPoint.Port, GetErrorMessage(error)); return true; } return false; @@ -239,7 +241,7 @@ bool Win32Network::BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint) const uint16 size = endPoint.IPVersion == NetworkIPVersion::IPv6 ? sizeof sockaddr_in6 : sizeof sockaddr_in; if (bind(*(SOCKET*)socket.Data, (const sockaddr*)endPoint.Data, size) == SOCKET_ERROR) { - LOG(Error, "Unable to bind socket! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address.Get(), endPoint.Port.Get(), GetLastErrorMessage().Get()); + LOG(Error, "Unable to bind socket! Socket : {0} Address : {1} Port : {2} Error : {3}", *(SOCKET*)socket.Data, endPoint.Address, endPoint.Port, GetLastErrorMessage()); return true; } return false; @@ -249,7 +251,7 @@ bool Win32Network::Listen(NetworkSocket& socket, uint16 queueSize) { if (listen(*(SOCKET*)socket.Data, (int32)queueSize) == SOCKET_ERROR) { - LOG(Error, "Unable to listen ! Socket : {0} Error : {1}", GetLastErrorMessage().Get()); + LOG(Error, "Unable to listen ! Socket : {0} Error : {1}", GetLastErrorMessage()); return true; } return false; @@ -270,7 +272,7 @@ bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, Net int32 error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) return false; - LOG(Warning, "Unable to accept incoming connection! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetErrorMessage(error).Get()); + LOG(Warning, "Unable to accept incoming connection! Socket : {0} Error : {1}", *(SOCKET*)serverSock.Data, GetErrorMessage(error)); return true; } memcpy(newSock.Data, &sock, sizeof sock); @@ -292,7 +294,7 @@ bool Win32Network::IsReadable(NetworkSocket& socket) int32 error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) return false; - LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); + LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error)); return false; } if (entry.revents & POLLRDNORM) @@ -310,7 +312,7 @@ bool Win32Network::IsWriteable(NetworkSocket& socket) int32 error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) return false; - LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error).Get()); + LOG(Error, "Unable to poll socket! Socket : {0} Error : {1}", *(SOCKET*)socket.Data, GetErrorMessage(error)); return false; } if (entry.revents & POLLWRNORM) @@ -322,7 +324,7 @@ int32 Win32Network::Poll(NetworkSocketGroup& group) { int32 pollret = WSAPoll((pollfd*)group.Data, group.Count, 0); if (pollret == SOCKET_ERROR) - LOG(Error, "Unable to poll socket group! Error : {0}", GetLastErrorMessage().Get()); + LOG(Error, "Unable to poll socket group! Error : {0}", GetLastErrorMessage()); return pollret; } @@ -354,7 +356,7 @@ int32 Win32Network::AddSocketToGroup(NetworkSocketGroup& group, NetworkSocket& s pollinfo.events = POLLRDNORM | POLLWRNORM; *(pollfd*)&group.Data[group.Count * SOCKGROUP_ITEMSIZE] = pollinfo; group.Count++; - return group.Count-1; + return group.Count - 1; } void Win32Network::ClearGroup(NetworkSocketGroup& group) @@ -374,7 +376,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = send(*(SOCKET*)socket.Data, (const char*)data, length, 0)) == SOCKET_ERROR) { - LOG(Error, "Unable to send data! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, GetLastErrorMessage().Get()); + LOG(Error, "Unable to send data! Socket : {0} Data Length : {1} Error : {2}", *(SOCKET*)socket.Data, length, GetLastErrorMessage()); return -1; } } @@ -382,13 +384,13 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, { if ((size = sendto(*(SOCKET*)socket.Data, (const char*)data, length, 0, (const sockaddr*)endPoint->Data, GetAddrSizeFromEP(*endPoint))) == SOCKET_ERROR) { - LOG(Error, "Unable to send data! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, GetLastErrorMessage().Get()); + LOG(Error, "Unable to send data! Socket : {0} Address : {1} Port : {2} Data Length : {3} Error : {4}", *(SOCKET*)socket.Data, endPoint->Address, endPoint->Port, length, GetLastErrorMessage()); return -1; } } else { - //TODO: better explanation + // TODO: better explanation LOG(Error, "Unable to send data! Socket : {0} Data Length : {1}", *(SOCKET*)socket.Data, length); return -1; } @@ -405,7 +407,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer const int32 error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) return 0; - LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetErrorMessage(error).Get()); + LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetErrorMessage(error)); return -1; } } @@ -415,7 +417,7 @@ int32 Win32Network::ReadSocket(NetworkSocket socket, byte* buffer, uint32 buffer sockaddr_in6 addr; if ((size = recvfrom(*(SOCKET*)socket.Data, (char*)buffer, bufferSize, 0, (sockaddr*)&addr, &addrsize)) == SOCKET_ERROR) { - LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage().Get()); + LOG(Error, "Unable to read data! Socket : {0} Buffer Size : {1} Error : {2}", *(SOCKET*)socket.Data, bufferSize, GetLastErrorMessage()); return -1; } if (CreateEndPointFromAddr((sockaddr*)&addr, *endPoint)) @@ -439,13 +441,13 @@ bool Win32Network::CreateEndPoint(String* address, String* port, NetworkIPVersio // consider using NUMERICHOST/NUMERICSERV if address is a valid Ipv4 or IPv6 so we can skip some look up ( potentially slow when resolving host names ) if ((status = GetAddrInfoW(address == nullptr ? nullptr : address->Get(), port->Get(), &hints, &info)) != 0) { - LOG(Error, "Unable to query info for address : {0} Error : {1}", address != nullptr ? address->Get() : String("ANY").Get(), gai_strerror(status)); + LOG(Error, "Unable to query info for address : {0} Error : {1}", address ? address->Get() : String("ANY"), gai_strerror(status)); return true; } if (info == nullptr) { - LOG(Error, "Unable to resolve address! Address : {0}", address != nullptr ? address->Get() : String("ANY").Get()); + LOG(Error, "Unable to resolve address! Address : {0}", address ? address->Get() : String("ANY")); return true; } diff --git a/Source/Engine/Platform/Win32/Win32Platform.cpp b/Source/Engine/Platform/Win32/Win32Platform.cpp index f74485748..b01b96feb 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.cpp +++ b/Source/Engine/Platform/Win32/Win32Platform.cpp @@ -9,10 +9,9 @@ #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/Math/Math.h" -#include "IncludeWindowsHeaders.h" #include "Engine/Core/Collections/HashFunctions.h" -#include "Engine/Platform/Network.h" #include "Engine/Core/Log.h" +#include "IncludeWindowsHeaders.h" #include #include @@ -27,10 +26,9 @@ namespace CPUInfo CpuInfo; uint64 ClockFrequency; double CyclesToSeconds; + WSAData WsaData; } -static WSAData _wsaData; - // Helper function to count set bits in the processor mask DWORD CountSetBits(ULONG_PTR bitMask) { @@ -52,9 +50,9 @@ static String GetLastErrorMessage() { wchar_t* s = nullptr; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, WSAGetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&s), 0, nullptr); + nullptr, WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&s), 0, nullptr); String str(s); LocalFree(s); return str; @@ -72,6 +70,7 @@ bool Win32Platform::Init() ClockFrequency = frequency.QuadPart; CyclesToSeconds = 1.0 / static_cast(frequency.QuadPart); + // Count CPUs BOOL done = FALSE; PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = nullptr; PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr; @@ -84,12 +83,10 @@ bool Win32Platform::Init() DWORD processorPackageCount = 0; DWORD byteOffset = 0; PCACHE_DESCRIPTOR cache; - while (!done) { DWORD rc = GetLogicalProcessorInformation(buffer, &returnLength); - - if (FALSE == rc) + if (rc == FALSE) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { @@ -97,9 +94,7 @@ bool Win32Platform::Init() { free(buffer); } - buffer = static_cast(malloc(returnLength)); - if (buffer == nullptr) { return true; @@ -115,23 +110,16 @@ bool Win32Platform::Init() done = TRUE; } } - ptr = buffer; - while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) { switch (ptr->Relationship) { case RelationProcessorCore: - processorCoreCount++; - - // A hyper threaded core supplies more than one logical processor logicalProcessorCount += CountSetBits(ptr->ProcessorMask); break; - case RelationCache: - // Cache data is in ptr->Cache, one CACHE_DESCRIPTOR structure for each cache. cache = &ptr->Cache; if (cache->Level == 1) { @@ -146,9 +134,7 @@ bool Win32Platform::Init() processorL3CacheSize += cache->Size; } break; - case RelationProcessorPackage: - // Logical processors share a physical package processorPackageCount++; break; } @@ -228,8 +214,10 @@ bool Win32Platform::Init() DeviceId.D = (uint32)cpuInfo.ClockSpeed * cpuInfo.LogicalProcessorCount * cpuInfo.ProcessorCoreCount * cpuInfo.CacheLineSize; } - if (WSAStartup(MAKEWORD(2, 0), &_wsaData) != 0) - LOG(Error, "Unable to initializes native network! Error : {0}", GetLastErrorMessage().Get()); + // Init networking + if (WSAStartup(MAKEWORD(2, 0), &WsaData) != 0) + LOG(Error, "Unable to initializes native network! Error : {0}", GetLastErrorMessage()); + return false; } @@ -240,19 +228,8 @@ void Win32Platform::Exit() void Win32Platform::MemoryBarrier() { - // NOTE: _ReadWriteBarrier and friends only prevent the - // compiler from reordering loads and stores. To prevent - // the CPU from doing the same, we have to use the - // MemoryBarrier macro which expands to e.g. a serializing - // XCHG instruction on x86. Also note that the MemoryBarrier - // macro does *not* imply _ReadWriteBarrier, so that call - // cannot be eliminated. - _ReadWriteBarrier(); - - // MemoryBarrier macro (we use undef to hide some symbols from Windows headers) #if PLATFORM_64BITS - #ifdef _AMD64_ __faststorefence(); #elif defined(_IA64_) @@ -260,7 +237,6 @@ void Win32Platform::MemoryBarrier() #else #error "Invalid platform." #endif - #else LONG barrier; __asm { From 53601d8a85c34de27ea34456e379934ab67a6740 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 12:11:59 +0100 Subject: [PATCH 231/419] Optimize and improve ActorChildNodes handling --- Source/Editor/Gizmo/TransformGizmo.cs | 2 +- Source/Editor/SceneGraph/ActorChildNode.cs | 17 +++++++++++++++++ Source/Editor/SceneGraph/ActorNode.cs | 19 ++++++++++++++----- .../Editor/SceneGraph/Actors/BoxVolumeNode.cs | 4 ++-- .../Editor/SceneGraph/Actors/NavLinkNode.cs | 4 ++-- .../Editor/Viewport/PrefabWindowViewport.cs | 2 +- 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmo.cs b/Source/Editor/Gizmo/TransformGizmo.cs index eca5475ed..f7aed5597 100644 --- a/Source/Editor/Gizmo/TransformGizmo.cs +++ b/Source/Editor/Gizmo/TransformGizmo.cs @@ -71,7 +71,7 @@ namespace FlaxEditor.Gizmo if (hit != null) { // For child actor nodes (mesh, link or sth) we need to select it's owning actor node first or any other child node (but not a child actor) - if (hit is ActorChildNode actorChildNode) + if (hit is ActorChildNode actorChildNode && !actorChildNode.CanBeSelectedDirectly) { var parentNode = actorChildNode.ParentNode; bool canChildBeSelected = sceneEditing.Selection.Contains(parentNode); diff --git a/Source/Editor/SceneGraph/ActorChildNode.cs b/Source/Editor/SceneGraph/ActorChildNode.cs index 0711aa20a..328ca277a 100644 --- a/Source/Editor/SceneGraph/ActorChildNode.cs +++ b/Source/Editor/SceneGraph/ActorChildNode.cs @@ -19,6 +19,11 @@ namespace FlaxEditor.SceneGraph /// public readonly int Index; + /// + /// Gets a value indicating whether this node can be selected directly without selecting parent actor node first. + /// + public virtual bool CanBeSelectedDirectly => false; + /// /// Initializes a new instance of the class. /// @@ -66,6 +71,18 @@ namespace FlaxEditor.SceneGraph /// public override object UndoRecordObject => ParentNode.UndoRecordObject; + + /// + public override void Dispose() + { + // Unlink from the parent + if (parentNode is ActorNode parentActorNode && parentActorNode.ActorChildNodes != null) + { + parentActorNode.ActorChildNodes.Remove(this); + } + + base.Dispose(); + } } /// diff --git a/Source/Editor/SceneGraph/ActorNode.cs b/Source/Editor/SceneGraph/ActorNode.cs index 753440378..5cc5f5337 100644 --- a/Source/Editor/SceneGraph/ActorNode.cs +++ b/Source/Editor/SceneGraph/ActorNode.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.SceneGraph /// /// The actor child nodes used to represent special parts of the actor (meshes, links, surfaces). /// - public readonly List ActorChildNodes = new List(); + public List ActorChildNodes; /// /// Initializes a new instance of the class. @@ -108,6 +108,8 @@ namespace FlaxEditor.SceneGraph /// The node public ActorChildNode AddChildNode(ActorChildNode node) { + if (ActorChildNodes == null) + ActorChildNodes = new List(); ActorChildNodes.Add(node); node.ParentNode = this; return node; @@ -125,9 +127,12 @@ namespace FlaxEditor.SceneGraph root.OnActorChildNodesDispose(this); } - for (int i = 0; i < ActorChildNodes.Count; i++) - ActorChildNodes[i].Dispose(); - ActorChildNodes.Clear(); + if (ActorChildNodes != null) + { + for (int i = 0; i < ActorChildNodes.Count; i++) + ActorChildNodes[i].Dispose(); + ActorChildNodes.Clear(); + } } /// @@ -275,8 +280,12 @@ namespace FlaxEditor.SceneGraph /// public override void Dispose() { - // Cleanup UI _treeNode.Dispose(); + if (ActorChildNodes != null) + { + ActorChildNodes.Clear(); + ActorChildNodes = null; + } base.Dispose(); } diff --git a/Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs b/Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs index 92b252138..24b035bc8 100644 --- a/Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs +++ b/Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs @@ -78,14 +78,14 @@ namespace FlaxEditor.SceneGraph.Actors { get { - var actor = (BoxVolume)((BoxVolumeNode)ParentNode).Actor; + var actor = (BoxVolume)_actor.Actor; var localOffset = _offset * actor.Size; Transform localTrans = new Transform(localOffset); return actor.Transform.LocalToWorld(localTrans); } set { - var actor = (BoxVolume)((BoxVolumeNode)ParentNode).Actor; + var actor = (BoxVolume)_actor.Actor; Transform localTrans = actor.Transform.WorldToLocal(value); var prevLocalOffset = _offset * actor.Size; var localOffset = Vector3.Abs(_offset) * 2.0f * localTrans.Translation; diff --git a/Source/Editor/SceneGraph/Actors/NavLinkNode.cs b/Source/Editor/SceneGraph/Actors/NavLinkNode.cs index 8eb5fbc8b..941e39267 100644 --- a/Source/Editor/SceneGraph/Actors/NavLinkNode.cs +++ b/Source/Editor/SceneGraph/Actors/NavLinkNode.cs @@ -37,13 +37,13 @@ namespace FlaxEditor.SceneGraph.Actors { get { - var actor = (NavLink)((NavLinkNode)ParentNode).Actor; + var actor = (NavLink)_actor.Actor; Transform localTrans = new Transform(_isStart ? actor.Start : actor.End); return actor.Transform.LocalToWorld(localTrans); } set { - var actor = (NavLink)((NavLinkNode)ParentNode).Actor; + var actor = (NavLink)_actor.Actor; Transform localTrans = actor.Transform.WorldToLocal(value); if (_isStart) actor.Start = localTrans.Translation; diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 94b994ac8..b5a19b1ec 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -513,7 +513,7 @@ namespace FlaxEditor.Viewport if (hit != null) { // For child actor nodes (mesh, link or sth) we need to select it's owning actor node first or any other child node (but not a child actor) - if (hit is ActorChildNode actorChildNode) + if (hit is ActorChildNode actorChildNode && !actorChildNode.CanBeSelectedDirectly) { var parentNode = actorChildNode.ParentNode; bool canChildBeSelected = _window.Selection.Contains(parentNode); From d0c0259ee23cdc1f6495b52ecb7e4ec5f34e3202 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 12:12:40 +0100 Subject: [PATCH 232/419] Add setter methods for spline points --- Source/Engine/Level/Actors/Spline.cpp | 36 +++++++++++++++++++++++++-- Source/Engine/Level/Actors/Spline.h | 32 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 9f47aa06e..3bb08b65c 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -231,9 +231,41 @@ void Spline::ClearSpline() UpdateSpline(); } +void Spline::SetSplinePoint(int32 index, const Vector3& point, bool updateSpline) +{ + CHECK(index >= 0 && index < GetSplinePointsCount()); + Curve[index].Value.Translation = _transform.WorldToLocal(point); + if (updateSpline) + UpdateSpline(); +} + +void Spline::SetSplineLocalPoint(int32 index, const Vector3& point, bool updateSpline) +{ + CHECK(index >= 0 && index < GetSplinePointsCount()); + Curve[index].Value.Translation = point; + if (updateSpline) + UpdateSpline(); +} + +void Spline::SetSplineTransform(int32 index, const Transform& point, bool updateSpline) +{ + CHECK(index >= 0 && index < GetSplinePointsCount()); + Curve[index].Value = _transform.WorldToLocal(point); + if (updateSpline) + UpdateSpline(); +} + +void Spline::SetSplineLocalTransform(int32 index, const Transform& point, bool updateSpline) +{ + CHECK(index >= 0 && index < GetSplinePointsCount()); + Curve[index].Value = point; + if (updateSpline) + UpdateSpline(); +} + void Spline::AddSplinePoint(const Vector3& point, bool updateSpline) { - const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(point)); + const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(_transform.WorldToLocal(point))); Curve.GetKeyframes().Add(k); if (updateSpline) UpdateSpline(); @@ -241,7 +273,7 @@ void Spline::AddSplinePoint(const Vector3& point, bool updateSpline) void Spline::AddSplineLocalPoint(const Vector3& point, bool updateSpline) { - const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(_transform.WorldToLocal(point))); + const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(point)); Curve.GetKeyframes().Add(k); if (updateSpline) UpdateSpline(); diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index cc4226414..76f1eddab 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -198,6 +198,38 @@ public: /// API_FUNCTION() void ClearSpline(); + /// + /// Sets the spline curve at the given index (world-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The location of the point to set (world-space). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void SetSplinePoint(int32 index, const Vector3& point, bool updateSpline = true); + + /// + /// Sets the spline curve at the given index (local-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The location of the point to set (local-space). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void SetSplineLocalPoint(int32 index, const Vector3& point, bool updateSpline = true); + + /// + /// Sets the spline curve at the given index (world-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The location of the point to set (world-space). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void SetSplineTransform(int32 index, const Transform& point, bool updateSpline = true); + + /// + /// Sets the spline curve at the given index (local-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The location of the point to set (local-space). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void SetSplineLocalTransform(int32 index, const Transform& point, bool updateSpline = true); + /// /// Adds the point to the spline curve (at the end). /// From fd96957819de53d2112b3aecf0f9ce73ca4ac205 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 12:13:00 +0100 Subject: [PATCH 233/419] Add gizmo handles for spline points --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index ea826c29e..03d4c5736 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -1,6 +1,8 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; using FlaxEngine; +using FlaxEngine.Json; namespace FlaxEditor.SceneGraph.Actors { @@ -10,10 +12,78 @@ namespace FlaxEditor.SceneGraph.Actors [HideInEditor] public sealed class SplineNode : ActorNode { + private sealed class SplinePointNode : ActorChildNode + { + public SplinePointNode(SplineNode actor, Guid id, int index) + : base(actor, id, index) + { + } + + public override bool CanBeSelectedDirectly => true; + + public override Transform Transform + { + get + { + var actor = (Spline)_actor.Actor; + return actor.GetSplineTransform(Index); + } + set + { + var actor = (Spline)_actor.Actor; + actor.SetSplineTransform(Index, value); + } + } + + public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal) + { + var actor = (Spline)_actor.Actor; + var pos = actor.GetSplinePoint(Index); + normal = -ray.Ray.Direction; + return new BoundingSphere(pos, 5.0f).Intersects(ref ray.Ray, out distance); + } + } + /// public SplineNode(Actor actor) : base(actor) { + OnUpdate(); + FlaxEngine.Scripting.Update += OnUpdate; + } + + private unsafe void OnUpdate() + { + // Sync spline points with gizmo handles + var actor = (Spline)Actor; + var dstCount = actor.SplinePointsCount; + if (dstCount > 1 && actor.IsLoop) + dstCount--; // The last point is the same as the first one for loop mode + var srcCount = ActorChildNodes?.Count ?? 0; + if (dstCount != srcCount) + { + // Remove unused points + while (srcCount > dstCount) + ActorChildNodes[srcCount-- - 1].Dispose(); + + // Add new points + var id = ID; + var g = (JsonSerializer.GuidInterop*)&id; + g->D += (uint)srcCount; + while (srcCount < dstCount) + { + g->D += 1; + AddChildNode(new SplinePointNode(this, id, srcCount++)); + } + } + } + + /// + public override void OnDispose() + { + FlaxEngine.Scripting.Update -= OnUpdate; + + base.OnDispose(); } } } From 37e3a85080844b689af26f6e10d9f260017c8acd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 12:33:32 +0100 Subject: [PATCH 234/419] Optimize splines debug rendering performance --- Source/Engine/Debug/DebugDraw.cpp | 1 + Source/Engine/Level/Actors/Spline.cpp | 31 +++++++++++---------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 596956dea..320a991a3 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -593,6 +593,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount) { + PROFILE_CPU(); if (selectedActors) { for (int32 i = 0; i < selectedActorsCount; i++) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 3bb08b65c..aaccd13fe 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -371,27 +371,23 @@ void Spline::SetKeyframes(MonoArray* data) namespace { - void DrawSegment(Spline* spline, int32 start, int32 end, const Color& color, const Transform& transform, bool depthTest) - { - const auto& startKey = spline->Curve[start]; - const auto& endKey = spline->Curve[end]; - const Vector3 startPos = transform.LocalToWorld(startKey.Value.Translation); - const Vector3& startTangent = startKey.TangentOut.Translation; - const Vector3 endPos = transform.LocalToWorld(endKey.Value.Translation); - const Vector3& endTangent = endKey.TangentIn.Translation; - const float d = (endKey.Time - startKey.Time) / 3.0f; - DEBUG_DRAW_BEZIER(startPos, startPos + startTangent * d, endPos + endTangent * d, endPos, color, 0.0f, depthTest); - } - void DrawSpline(Spline* spline, const Color& color, const Transform& transform, bool depthTest) { const int32 count = spline->Curve.GetKeyframes().Count(); - for (int32 i = 0; i < count; i++) + if (count == 0) + return; + Spline::Keyframe* prev = spline->Curve.GetKeyframes().Get(); + Vector3 prevPos = transform.LocalToWorld(prev->Value.Translation); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(prevPos, 5.0f), color, 0.0f, depthTest); + for (int32 i = 1; i < count; i++) { - Vector3 p = transform.LocalToWorld(spline->Curve[i].Value.Translation); - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(p, 5.0f), color, 0.0f, true); - if (i != 0) - DrawSegment(spline, i - 1, i, color, transform, depthTest); + Spline::Keyframe* next = prev + 1; + Vector3 nextPos = transform.LocalToWorld(next->Value.Translation); + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, 5.0f), color, 0.0f, depthTest); + const float d = (next->Time - prev->Time) / 3.0f; + DEBUG_DRAW_BEZIER(prevPos, prevPos + prev->TangentOut.Translation * d, nextPos + next->TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); + prev = next; + prevPos = nextPos; } } } @@ -409,7 +405,6 @@ void Spline::OnDebugDrawSelected() { const Color color = GetSplineColor(); DrawSpline(this, color.AlphaMultiplied(0.3f), _transform, false); - DrawSpline(this, color, _transform, true); // Base Actor::OnDebugDrawSelected(); From 590fcebaec80240518eb0cece5f888a5bb3e09af Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 12:33:46 +0100 Subject: [PATCH 235/419] Fix drawing spline when spline point gizmo is selected --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 03d4c5736..09d50e706 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -42,6 +42,11 @@ namespace FlaxEditor.SceneGraph.Actors normal = -ray.Ray.Direction; return new BoundingSphere(pos, 5.0f).Intersects(ref ray.Ray, out distance); } + + public override void OnDebugDraw(ViewportDebugDrawData data) + { + ParentNode.OnDebugDraw(data); + } } /// From 3dc6afef624a509880f207fac17f301a4ea5c339 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 12:38:03 +0100 Subject: [PATCH 236/419] Improve selecting spline gizmos and add highlight sphere --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 09d50e706..aa7fff53a 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -40,12 +40,16 @@ namespace FlaxEditor.SceneGraph.Actors var actor = (Spline)_actor.Actor; var pos = actor.GetSplinePoint(Index); normal = -ray.Ray.Direction; - return new BoundingSphere(pos, 5.0f).Intersects(ref ray.Ray, out distance); + return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance); } public override void OnDebugDraw(ViewportDebugDrawData data) { ParentNode.OnDebugDraw(data); + + var actor = (Spline)_actor.Actor; + var pos = actor.GetSplinePoint(Index); + DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.Yellow, 0, false); } } From da3a54d184c60da9ad862c12f310352f89ffdf02 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 27 Jan 2021 14:06:36 +0100 Subject: [PATCH 237/419] Pause on error On Windows, because people using Windows will double click on the file --- GenerateProjectFiles.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/GenerateProjectFiles.bat b/GenerateProjectFiles.bat index 5db8aaf55..aeeac1c67 100644 --- a/GenerateProjectFiles.bat +++ b/GenerateProjectFiles.bat @@ -16,6 +16,7 @@ exit /B 0 :BuildToolFailed echo Flax.Build tool failed. +pause goto Exit :Exit From 80e0b0b0e5829c832bc51ab93dc85766d716b622 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 27 Jan 2021 14:32:10 +0100 Subject: [PATCH 238/419] Document the "set as startup project" --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ad9f7ce4..a91acb56e 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ Follow the instructions below to compile and run the engine from source. * Clone repo (with LFS) * Run **GenerateProjectFiles.bat** * Open `Flax.sln` and set solution configuration to **Editor.Development** and solution platform to **Win64** -* Compile Flax project (hit F7 key) +* Set Flax or FlaxEngine as startup project +* Compile Flax project (hit F7 or CTRL+Shift+B) * Run Flax (hit F5 key) ## Linux From 81865bd97e2946e6f863110e92e862958cacbccf Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 27 Jan 2021 14:32:25 +0100 Subject: [PATCH 239/419] Move Flax plugin up, since it's currently required --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a91acb56e..9c5b83aae 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ This repository contains full source code of the Flax (excluding NDA-protected p Follow the instructions below to compile and run the engine from source. +## Flax plugin for Visual Studio + +Flax Visual Studio extension provides better programming workflow, C# scripts debugging functionality and allows to attach to running engine instance to debug C# source. This extension is available to download [here](https://marketplace.visualstudio.com/items?itemName=Flax.FlaxVS). + ## Windows * Install Visual Studio 2015 or newer @@ -50,10 +54,6 @@ Follow the instructions below to compile and run the engine from source. * Open workspace with Visual Code * Build and run -# Flax plugin for Visual Studio - -Flax Visual Studio extension provides better programming workflow, C# scripts debugging functionality and allows to attach to running engine instance to debug C# source. This extension is available to download [here](https://marketplace.visualstudio.com/items?itemName=Flax.FlaxVS). - ## Workspace directory - **Binaries/** - executable files From 691d9d9b882ae95548d7ae22c9cb2fe1a6cd2217 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 27 Jan 2021 14:34:38 +0100 Subject: [PATCH 240/419] Document the GitHub basics --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8eb1eb47..236fa6d67 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,6 +18,8 @@ Go check out our [Trello](https://trello.com/b/NQjLXRCP/flax-roadmap). ## **Want to contribute?** +* Fork the FlaxEngine, create a new branch and push your changes there. Then, create a pull request. + * When creating a PR for fixing an issue/bug make sure to describe as to what led to the fix for better understanding, for small and obvious fixes this is not really needed. However make sure to mention the relevant issue where it was first reported if possible. From 5a402df3ff72c91654f62c324aa045e39fb64666 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:07:02 +0100 Subject: [PATCH 241/419] Add Add/Subtract methods to Transform --- Source/Engine/Core/Math/Quaternion.cs | 8 +++ Source/Engine/Core/Math/Transform.cpp | 15 +++-- Source/Engine/Core/Math/Transform.cs | 59 +++++++++++++++++-- Source/Engine/Core/Math/Transform.h | 12 ++++ .../Tools/FlaxEngine.Tests/TestTransform.cs | 24 +++++++- 5 files changed, 108 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Core/Math/Quaternion.cs b/Source/Engine/Core/Math/Quaternion.cs index bc86fa2c9..0e809f271 100644 --- a/Source/Engine/Core/Math/Quaternion.cs +++ b/Source/Engine/Core/Math/Quaternion.cs @@ -324,6 +324,14 @@ namespace FlaxEngine Z = -Z; } + /// + /// Gets the conjugated quaternion. + /// + public Quaternion Conjugated() + { + return new Quaternion(-X, -Y, -Z, W); + } + /// /// Conjugates and renormalizes the quaternion. /// diff --git a/Source/Engine/Core/Math/Transform.cpp b/Source/Engine/Core/Math/Transform.cpp index 728fc6a90..35f616236 100644 --- a/Source/Engine/Core/Math/Transform.cpp +++ b/Source/Engine/Core/Math/Transform.cpp @@ -43,37 +43,42 @@ void Transform::GetWorld(Matrix& result) const Transform Transform::Add(const Vector3& translation) const { Transform result; - result.Orientation = Orientation; result.Scale = Scale; Vector3::Add(Translation, translation, result.Translation); - return result; } Transform Transform::Add(const Transform& other) const { Transform result; - Quaternion::Multiply(Orientation, other.Orientation, result.Orientation); result.Orientation.Normalize(); Vector3::Multiply(Scale, other.Scale, result.Scale); Vector3::Add(Translation, other.Translation, result.Translation); + return result; +} +Transform Transform::Subtract(const Transform& other) const +{ + Transform result; + Vector3::Subtract(Translation, other.Translation, result.Translation); + const Quaternion invRotation = other.Orientation.Conjugated(); + Quaternion::Multiply(Orientation, invRotation, result.Orientation); + result.Orientation.Normalize(); + Vector3::Divide(Scale, other.Scale, result.Scale); return result; } Transform Transform::LocalToWorld(const Transform& other) const { Transform result; - Quaternion::Multiply(Orientation, other.Orientation, result.Orientation); result.Orientation.Normalize(); Vector3::Multiply(Scale, other.Scale, result.Scale); Vector3 tmp = other.Translation * Scale; Vector3::Transform(tmp, Orientation, tmp); Vector3::Add(tmp, Translation, result.Translation); - return result; } diff --git a/Source/Engine/Core/Math/Transform.cs b/Source/Engine/Core/Math/Transform.cs index 7065309e3..129ddf92a 100644 --- a/Source/Engine/Core/Math/Transform.cs +++ b/Source/Engine/Core/Math/Transform.cs @@ -169,6 +169,37 @@ namespace FlaxEngine Matrix.Transformation(ref Scale, ref Orientation, ref Translation, out result); } + /// + /// Adds two transforms. + /// + /// The first transform to add. + /// The second transform to add. + /// The sum of the two transforms. + public static Transform Add(Transform left, Transform right) + { + Transform result; + Quaternion.Multiply(ref left.Orientation, ref right.Orientation, out result.Orientation); + Vector3.Multiply(ref left.Scale, ref right.Scale, out result.Scale); + Vector3.Add(ref left.Translation, ref right.Translation, out result.Translation); + return result; + } + + /// + /// Subtracts two transforms. + /// + /// The first transform to subtract from. + /// The second transform to subtract. + /// The difference of the two transforms. + public static Transform Subtract(Transform left, Transform right) + { + Transform result; + Vector3.Subtract(ref left.Translation, ref right.Translation, out result.Translation); + Quaternion invRotation = right.Orientation.Conjugated(); + Quaternion.Multiply(ref left.Orientation, ref invRotation, out result.Orientation); + Vector3.Divide(ref left.Scale, ref right.Scale, out result.Scale); + return result; + } + /// /// Perform transformation of the given transform in local space /// @@ -176,12 +207,10 @@ namespace FlaxEngine /// World space transform public Transform LocalToWorld(Transform other) { - Transform result = new Transform(Vector3.Zero); - + Transform result; Quaternion.Multiply(ref Orientation, ref other.Orientation, out result.Orientation); Vector3.Multiply(ref Scale, ref other.Scale, out result.Scale); result.Translation = LocalToWorld(other.Translation); - return result; } @@ -229,7 +258,6 @@ namespace FlaxEngine /// Local space transform public Transform WorldToLocal(Transform other) { - Transform result = new Transform(Vector3.Zero); Vector3 invScale = Scale; if (invScale.X != 0.0f) invScale.X = 1.0f / invScale.X; @@ -238,6 +266,7 @@ namespace FlaxEngine if (invScale.Z != 0.0f) invScale.Z = 1.0f / invScale.Z; + Transform result; result.Orientation = Orientation; result.Orientation.Invert(); Quaternion.Multiply(ref result.Orientation, ref other.Orientation, out result.Orientation); @@ -404,6 +433,28 @@ namespace FlaxEngine return !left.Equals(ref right); } + /// + /// Adds two transformations. + /// + /// The first transform to add. + /// The second transform to add. + /// The sum of the two transformations. + public static Transform operator +(Transform left, Transform right) + { + return Add(left, right); + } + + /// + /// Subtracts two transformations. + /// + /// The first transform to subtract from. + /// The second transform to subtract. + /// The difference of the two transformations. + public static Transform operator -(Transform left, Transform right) + { + return Subtract(left, right); + } + /// /// Returns a that represents this instance. /// diff --git a/Source/Engine/Core/Math/Transform.h b/Source/Engine/Core/Math/Transform.h index 5a39c2d86..069a49992 100644 --- a/Source/Engine/Core/Math/Transform.h +++ b/Source/Engine/Core/Math/Transform.h @@ -163,6 +163,13 @@ public: /// The sum of two transformations. Transform Add(const Transform& other) const; + /// + /// Subtracts transformation from this transform. + /// + /// The other transformation. + /// The different of two transformations. + Transform Subtract(const Transform& other) const; + /// /// Performs transformation of the given transform in local space to the world space of this transform. /// @@ -254,6 +261,11 @@ public: return Add(other); } + FORCE_INLINE Transform operator-(const Transform& other) const + { + return Subtract(other); + } + FORCE_INLINE Transform operator+(const Vector3& other) const { return Add(other); diff --git a/Source/Tools/FlaxEngine.Tests/TestTransform.cs b/Source/Tools/FlaxEngine.Tests/TestTransform.cs index d947c295e..38f71e217 100644 --- a/Source/Tools/FlaxEngine.Tests/TestTransform.cs +++ b/Source/Tools/FlaxEngine.Tests/TestTransform.cs @@ -143,7 +143,29 @@ namespace FlaxEngine.Tests Transform ab = a.LocalToWorld(b); Transform ba = a.WorldToLocal(ab); - Assert.IsTrue(Transform.NearEqual(ref b, ref ba, 0.00001f)); + Assert.IsTrue(Transform.NearEqual(ref b, ref ba, 0.00001f), $"Got: {b} but expected {ba}"); + } + } + + /// + /// Test conversions between transform local/world space + /// + [Test] + public void TestAddSubtract() + { + var rand = new Random(10); + for (int i = 0; i < 10; i++) + { + Transform a = new Transform(rand.NextVector3(), Quaternion.Euler(i * 10, 0, i), rand.NextVector3() * 10.0f); + Transform b = new Transform(rand.NextVector3(), Quaternion.Euler(i, 1, 22), rand.NextVector3() * 0.3f); + + Transform ab = a + b; + Transform newA = ab - b; + Assert.IsTrue(Transform.NearEqual(ref a, ref newA, 0.00001f), $"Got: {newA} but expected {a}"); + + Transform ba = b + a; + Transform newB = ba - a; + Assert.IsTrue(Transform.NearEqual(ref b, ref newB, 0.00001f), $"Got: {newB} but expected {b}"); } } } From c0e8b735ad18adcbf9e932733346b1d8ab3305f9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:12:02 +0100 Subject: [PATCH 242/419] Fix Get Node Transform ndoe in anim graph --- Source/Editor/Surface/Archetypes/Animation.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index e067dceb4..15096e696 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -882,8 +882,7 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 160, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), - NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0), - NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1), + NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), } }, new NodeArchetype @@ -924,8 +923,7 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), - NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0), - NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1), + NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), } }, new NodeArchetype From 560b6cf5daeb7390f93e7d79c4560db3d8aa68c4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:12:29 +0100 Subject: [PATCH 243/419] Fix error when animated model skeleton has duplicated node name --- Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs b/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs index 0d8d676c7..d73590ac7 100644 --- a/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs +++ b/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs @@ -92,7 +92,7 @@ namespace FlaxEditor.Surface.Elements { sb.Clear(); var node = nodes[nodeIndex]; - _nodeNameToIndex.Add(node.Name, nodeIndex); + _nodeNameToIndex[node.Name] = nodeIndex; int parent = node.ParentIndex; while (parent != -1) { From f0c7fbb0cf04735af77059450d3ebefaf6feca0b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:28:26 +0100 Subject: [PATCH 244/419] Add more utilities for Spline --- Source/Engine/Level/Actors/Spline.cpp | 40 +++++++++++++++++++-- Source/Engine/Level/Actors/Spline.h | 50 +++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index aaccd13fe..a3757253f 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -106,16 +106,29 @@ Vector3 Spline::GetSplineLocalPoint(int32 index) const Transform Spline::GetSplineTransform(int32 index) const { - CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity) return _transform.LocalToWorld(Curve[index].Value); } Transform Spline::GetSplineLocalTransform(int32 index) const { - CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity) return Curve[index].Value; } +Transform Spline::GetSplineTangent(int32 index, bool isIn) +{ + return _transform.LocalToWorld(GetSplineLocalTangent(index, isIn)); +} + +Transform Spline::GetSplineLocalTangent(int32 index, bool isIn) +{ + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity) + const auto& k = Curve[index]; + const auto& tangent = isIn ? k.TangentIn : k.TangentOut; + return tangent + k.Value; +} + int32 Spline::GetSplinePointsCount() const { return Curve.GetKeyframes().Count(); @@ -263,6 +276,29 @@ void Spline::SetSplineLocalTransform(int32 index, const Transform& point, bool u UpdateSpline(); } +void Spline::SetSplineTangent(int32 index, const Transform& point, bool isIn, bool updateSpline) +{ + SetSplineLocalTangent(index, _transform.WorldToLocal(point), isIn, updateSpline); +} + +void Spline::SetSplineLocalTangent(int32 index, const Transform& point, bool isIn, bool updateSpline) +{ + CHECK(index >= 0 && index < GetSplinePointsCount()); + auto& k = Curve[index]; + auto& tangent = isIn ? k.TangentIn : k.TangentOut; + tangent = point - k.Value; + if (updateSpline) + UpdateSpline(); +} + +void Spline::SetSplinePointTime(int32 index, float time, bool updateSpline) +{ + CHECK(index >= 0 && index < GetSplinePointsCount()); + Curve[index].Time = time; + if (updateSpline) + UpdateSpline(); +} + void Spline::AddSplinePoint(const Vector3& point, bool updateSpline) { const Keyframe k(Curve.IsEmpty() ? 0.0f : Curve.GetKeyframes().Last().Time + 1.0f, Transform(_transform.WorldToLocal(point))); diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index 76f1eddab..772aac821 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -138,6 +138,24 @@ public: /// The curve point transformation (local-space). API_FUNCTION() Transform GetSplineLocalTransform(int32 index) const; + /// + /// Gets the spline curve point tangent at the given index (world-space). + /// + /// Tangents are stored relative to the curve point but this methods converts them to be in world-space. + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// True if get arrive tangent, otherwise gets leave tangent (in or out). + /// The transformation of the tangent to set (world-space). + API_FUNCTION() Transform GetSplineTangent(int32 index, bool isIn); + + /// + /// Gets the spline curve point tangent at the given index (local-space). + /// + /// Tangents are stored relative to the curve point but this methods converts them to be in local-space of the actor. + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// True if get arrive tangent, otherwise gets leave tangent (in or out). + /// The transformation of the tangent to set (world-space). + API_FUNCTION() Transform GetSplineLocalTangent(int32 index, bool isIn); + /// /// Gets the amount of points in the spline. /// @@ -218,7 +236,7 @@ public: /// Sets the spline curve at the given index (world-space). /// /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). - /// The location of the point to set (world-space). + /// The transformation of the point to set (world-space). /// True if update spline after adding the point, otherwise false. API_FUNCTION() void SetSplineTransform(int32 index, const Transform& point, bool updateSpline = true); @@ -226,10 +244,38 @@ public: /// Sets the spline curve at the given index (local-space). /// /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). - /// The location of the point to set (local-space). + /// The transformation of the point to set (local-space). /// True if update spline after adding the point, otherwise false. API_FUNCTION() void SetSplineLocalTransform(int32 index, const Transform& point, bool updateSpline = true); + /// + /// Sets the spline curve point tangent at the given index (world-space). + /// + /// Tangents are stored relative to the curve point but this methods converts them to be in world-space. + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The transformation of the tangent to set (world-space). + /// True if set arrive tangent, otherwise sets leave tangent (in or out). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void SetSplineTangent(int32 index, const Transform& point, bool isIn, bool updateSpline = true); + + /// + /// Sets the spline curve point tangent at the given index (local-space). + /// + /// Tangents are stored relative to the curve point but this methods converts them to be in local-space of the actor. + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The transformation of the tangent to set (local-space). + /// True if set arrive tangent, otherwise sets leave tangent (in or out). + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void SetSplineLocalTangent(int32 index, const Transform& point, bool isIn, bool updateSpline = true); + + /// + /// Sets the spline curve point time at the given index (world-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The time to set. + /// True if update spline after adding the point, otherwise false. + API_FUNCTION() void SetSplinePointTime(int32 index, float time, bool updateSpline = true); + /// /// Adds the point to the spline curve (at the end). /// From bd93e46624b478fac32b7249b89cb749172afdb6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:29:14 +0100 Subject: [PATCH 245/419] Tweaks for ActorChildNode --- Source/Editor/SceneGraph/ActorChildNode.cs | 18 +++++++++++++----- .../Editor/SceneGraph/Actors/BoxVolumeNode.cs | 4 ++-- Source/Editor/SceneGraph/Actors/NavLinkNode.cs | 4 ++-- Source/Editor/SceneGraph/Actors/SplineNode.cs | 17 ++++++++++------- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Source/Editor/SceneGraph/ActorChildNode.cs b/Source/Editor/SceneGraph/ActorChildNode.cs index 328ca277a..436984bc4 100644 --- a/Source/Editor/SceneGraph/ActorChildNode.cs +++ b/Source/Editor/SceneGraph/ActorChildNode.cs @@ -94,20 +94,28 @@ namespace FlaxEditor.SceneGraph public abstract class ActorChildNode : ActorChildNode where T : ActorNode { /// - /// The actor. + /// The actor node. /// - protected readonly T _actor; + protected T _node; /// /// Initializes a new instance of the class. /// - /// The parent actor. + /// The parent actor node. /// The child id. /// The child index. - protected ActorChildNode(T actor, Guid id, int index) + protected ActorChildNode(T node, Guid id, int index) : base(id, index) { - _actor = actor; + _node = node; + } + + /// + public override void OnDispose() + { + _node = null; + + base.OnDispose(); } } } diff --git a/Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs b/Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs index 24b035bc8..ec62ebd47 100644 --- a/Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs +++ b/Source/Editor/SceneGraph/Actors/BoxVolumeNode.cs @@ -78,14 +78,14 @@ namespace FlaxEditor.SceneGraph.Actors { get { - var actor = (BoxVolume)_actor.Actor; + var actor = (BoxVolume)_node.Actor; var localOffset = _offset * actor.Size; Transform localTrans = new Transform(localOffset); return actor.Transform.LocalToWorld(localTrans); } set { - var actor = (BoxVolume)_actor.Actor; + var actor = (BoxVolume)_node.Actor; Transform localTrans = actor.Transform.WorldToLocal(value); var prevLocalOffset = _offset * actor.Size; var localOffset = Vector3.Abs(_offset) * 2.0f * localTrans.Translation; diff --git a/Source/Editor/SceneGraph/Actors/NavLinkNode.cs b/Source/Editor/SceneGraph/Actors/NavLinkNode.cs index 941e39267..62bc0a2e7 100644 --- a/Source/Editor/SceneGraph/Actors/NavLinkNode.cs +++ b/Source/Editor/SceneGraph/Actors/NavLinkNode.cs @@ -37,13 +37,13 @@ namespace FlaxEditor.SceneGraph.Actors { get { - var actor = (NavLink)_actor.Actor; + var actor = (NavLink)_node.Actor; Transform localTrans = new Transform(_isStart ? actor.Start : actor.End); return actor.Transform.LocalToWorld(localTrans); } set { - var actor = (NavLink)_actor.Actor; + var actor = (NavLink)_node.Actor; Transform localTrans = actor.Transform.WorldToLocal(value); if (_isStart) actor.Start = localTrans.Translation; diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index aa7fff53a..879e7e241 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -14,8 +14,8 @@ namespace FlaxEditor.SceneGraph.Actors { private sealed class SplinePointNode : ActorChildNode { - public SplinePointNode(SplineNode actor, Guid id, int index) - : base(actor, id, index) + public unsafe SplinePointNode(SplineNode node, Guid id, int index) + : base(node, id, index) { } @@ -25,19 +25,19 @@ namespace FlaxEditor.SceneGraph.Actors { get { - var actor = (Spline)_actor.Actor; + var actor = (Spline)_node.Actor; return actor.GetSplineTransform(Index); } set { - var actor = (Spline)_actor.Actor; + var actor = (Spline)_node.Actor; actor.SetSplineTransform(Index, value); } } public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal) { - var actor = (Spline)_actor.Actor; + var actor = (Spline)_node.Actor; var pos = actor.GetSplinePoint(Index); normal = -ray.Ray.Direction; return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance); @@ -45,10 +45,13 @@ namespace FlaxEditor.SceneGraph.Actors public override void OnDebugDraw(ViewportDebugDrawData data) { + var actor = (Spline)_node.Actor; + var pos = actor.GetSplinePoint(Index); + + // Draw spline path ParentNode.OnDebugDraw(data); - var actor = (Spline)_actor.Actor; - var pos = actor.GetSplinePoint(Index); + // Draw selected point highlight DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.Yellow, 0, false); } } From 9cdbbf590aad96427f87603f5b551b5fcf64a032 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:29:33 +0100 Subject: [PATCH 246/419] Add spline points tangents gizmos and debug drawing --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 78 ++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 879e7e241..6589fa848 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -17,6 +17,11 @@ namespace FlaxEditor.SceneGraph.Actors public unsafe SplinePointNode(SplineNode node, Guid id, int index) : base(node, id, index) { + var g = (JsonSerializer.GuidInterop*)&id; + g->D++; + AddChild(new SplinePointTangentNode(node, id, index, true)); + g->D++; + AddChild(new SplinePointTangentNode(node, id, index, false)); } public override bool CanBeSelectedDirectly => true; @@ -47,12 +52,81 @@ namespace FlaxEditor.SceneGraph.Actors { var actor = (Spline)_node.Actor; var pos = actor.GetSplinePoint(Index); + var tangentIn = actor.GetSplineTangent(Index, true).Translation; + var tangentOut = actor.GetSplineTangent(Index, false).Translation; // Draw spline path ParentNode.OnDebugDraw(data); // Draw selected point highlight DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.Yellow, 0, false); + + // Draw tangent points + if (tangentIn != pos) + { + DebugDraw.DrawLine(pos, tangentIn, Color.White.AlphaMultiplied(0.6f), 0, false); + DebugDraw.DrawWireSphere(new BoundingSphere(tangentIn, 4.0f), Color.White, 0, false); + } + if (tangentIn != pos) + { + DebugDraw.DrawLine(pos, tangentOut, Color.White.AlphaMultiplied(0.6f), 0, false); + DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, 4.0f), Color.White, 0, false); + } + } + } + + private sealed class SplinePointTangentNode : ActorChildNode + { + private SplineNode _node; + private int _index; + private bool _isIn; + + public SplinePointTangentNode(SplineNode node, Guid id, int index, bool isIn) + : base(id, isIn ? 0 : 1) + { + _node = node; + _index = index; + _isIn = isIn; + } + + public override Transform Transform + { + get + { + var actor = (Spline)_node.Actor; + return actor.GetSplineTangent(_index, _isIn); + } + set + { + var actor = (Spline)_node.Actor; + actor.SetSplineTangent(_index, value, _isIn); + } + } + + public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal) + { + var actor = (Spline)_node.Actor; + var pos = actor.GetSplineTangent(_index, _isIn).Translation; + normal = -ray.Ray.Direction; + return new BoundingSphere(pos, 7.0f).Intersects(ref ray.Ray, out distance); + } + + public override void OnDebugDraw(ViewportDebugDrawData data) + { + // Draw spline and spline point + ParentNode.OnDebugDraw(data); + + // Draw selected tangent highlight + var actor = (Spline)_node.Actor; + var pos = actor.GetSplineTangent(_index, _isIn).Translation; + DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.YellowGreen, 0, false); + } + + public override void OnDispose() + { + _node = null; + + base.OnDispose(); } } @@ -81,10 +155,10 @@ namespace FlaxEditor.SceneGraph.Actors // Add new points var id = ID; var g = (JsonSerializer.GuidInterop*)&id; - g->D += (uint)srcCount; + g->D += (uint)srcCount * 3; while (srcCount < dstCount) { - g->D += 1; + g->D += 3; AddChildNode(new SplinePointNode(this, id, srcCount++)); } } From 7c85ac61b80d90e4c107475f5a0da2247a347ebb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:39:38 +0100 Subject: [PATCH 247/419] Add new Spline actor initialization when spawning in editor --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 6589fa848..48030dbd4 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -164,6 +164,17 @@ namespace FlaxEditor.SceneGraph.Actors } } + /// + public override void PostSpawn() + { + base.PostSpawn(); + + // Setup for an initial spline + var spline = (Spline)Actor; + spline.AddSplineLocalPoint(Vector3.Zero, false); + spline.AddSplineLocalPoint(new Vector3(0, 0, 100.0f)); + } + /// public override void OnDispose() { From 18e3d5191464325466d5f5cb6df7af466494cd7d Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 27 Jan 2021 18:08:38 +0100 Subject: [PATCH 248/419] Check LFS using file size --- Development/Scripts/Linux/CallBuildTool.sh | 6 ++++++ Development/Scripts/Windows/CallBuildTool.bat | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/Development/Scripts/Linux/CallBuildTool.sh b/Development/Scripts/Linux/CallBuildTool.sh index 3b4568918..1a8affe20 100755 --- a/Development/Scripts/Linux/CallBuildTool.sh +++ b/Development/Scripts/Linux/CallBuildTool.sh @@ -3,6 +3,12 @@ set -e +testfilesize=$(wc -c < 'Source/Logo.png') +if [ $testfilesize -le 1000 ]; then + echo "CallBuildTool ERROR: Repository was not cloned using Git LFS" 1>&2 + exit 1 +fi + # Compile the build tool. xbuild /nologo /verbosity:quiet "Source/Tools/Flax.Build/Flax.Build.csproj" /property:Configuration=Release /property:Platform=AnyCPU /target:Build diff --git a/Development/Scripts/Windows/CallBuildTool.bat b/Development/Scripts/Windows/CallBuildTool.bat index 4d9124776..c9729cf55 100644 --- a/Development/Scripts/Windows/CallBuildTool.bat +++ b/Development/Scripts/Windows/CallBuildTool.bat @@ -4,6 +4,10 @@ rem Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. if not exist "Development\Scripts\Windows\GetMSBuildPath.bat" goto Error_InvalidLocation +for %%I in (Source\Logo.png) do if %%~zI LSS 2000 ( + goto Error_MissingLFS +) + call "Development\Scripts\Windows\GetMSBuildPath.bat" if errorlevel 1 goto Error_NoVisualStudioEnvironment @@ -33,6 +37,9 @@ Binaries\Tools\Flax.Build.exe %* if errorlevel 1 goto Error_FlaxBuildFailed exit /B 0 +:Error_MissingLFS +echo CallBuildTool ERROR: Repository was not cloned using Git LFS +goto Exit :Error_InvalidLocation echo CallBuildTool ERROR: The script is in invalid directory. goto Exit From f00ae7fb2199a46082be0a993caad0e35142a731 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 27 Jan 2021 21:30:22 +0100 Subject: [PATCH 249/419] Disable CTRL+W during the game Fix #96 --- Source/Editor/Windows/GameWindow.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 35bc91bbe..0a259566c 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -387,6 +387,12 @@ namespace FlaxEditor.Windows } } + // Prevent closing the game window tab during a play session + if (Editor.StateMachine.IsPlayMode && Editor.Options.Options.Input.CloseTab.Process(Root)) + { + return true; + } + return base.OnKeyDown(key); } From c820e87d4aef0f563a0ea3a336bf50f601124974 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 27 Jan 2021 22:08:17 +0100 Subject: [PATCH 250/419] Slightly tweak the key processing code --- Source/Editor/Options/InputBinding.cs | 37 +++++++++++++++++++-------- Source/Editor/Windows/GameWindow.cs | 3 ++- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Options/InputBinding.cs b/Source/Editor/Options/InputBinding.cs index e28e8fa0c..df3d2dae6 100644 --- a/Source/Editor/Options/InputBinding.cs +++ b/Source/Editor/Options/InputBinding.cs @@ -143,7 +143,30 @@ namespace FlaxEditor.Options { var root = control.Root; - if (root.GetKeyDown(Key)) + if (root.GetKey(Key)) + { + if (Modifier1 == KeyboardKeys.None || root.GetKey(Modifier1)) + { + if (Modifier2 == KeyboardKeys.None || root.GetKey(Modifier2)) + { + return true; + } + } + } + + return false; + } + + /// + /// Processes this input binding to check if state matches. + /// + /// The input providing control. + /// True if input has been processed, otherwise false. + public bool Process(Control control, KeyboardKeys key) + { + var root = control.Root; + + if (key == Key) { if (Modifier1 == KeyboardKeys.None || root.GetKey(Modifier1)) { @@ -435,16 +458,10 @@ namespace FlaxEditor.Options for (int i = 0; i < _bindings.Count; i++) { var binding = _bindings[i].Binder(options); - if (binding.Key == key) + if (binding.Process(control, key)) { - if (binding.Modifier1 == KeyboardKeys.None || root.GetKey(binding.Modifier1)) - { - if (binding.Modifier2 == KeyboardKeys.None || root.GetKey(binding.Modifier2)) - { - _bindings[i].Callback(); - return true; - } - } + _bindings[i].Callback(); + return true; } } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 0a259566c..c6a3fd24c 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -387,8 +387,9 @@ namespace FlaxEditor.Windows } } + // Prevent closing the game window tab during a play session - if (Editor.StateMachine.IsPlayMode && Editor.Options.Options.Input.CloseTab.Process(Root)) + if (Editor.StateMachine.IsPlayMode && Editor.Options.Options.Input.CloseTab.Process(this, key)) { return true; } From 36bd398136f9d7199bf3f6dac42ff0102bc60839 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:12:29 +0100 Subject: [PATCH 251/419] Fix error when animated model skeleton has duplicated node name --- Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs b/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs index 0d8d676c7..d73590ac7 100644 --- a/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs +++ b/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs @@ -92,7 +92,7 @@ namespace FlaxEditor.Surface.Elements { sb.Clear(); var node = nodes[nodeIndex]; - _nodeNameToIndex.Add(node.Name, nodeIndex); + _nodeNameToIndex[node.Name] = nodeIndex; int parent = node.ParentIndex; while (parent != -1) { From 3c3b47f5b4d1f7054f16dd598823831f7bf35ce8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 15:12:02 +0100 Subject: [PATCH 252/419] Fix Get Node Transform ndoe in anim graph --- Source/Editor/Surface/Archetypes/Animation.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index e067dceb4..15096e696 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -882,8 +882,7 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 160, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), - NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0), - NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1), + NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), } }, new NodeArchetype @@ -924,8 +923,7 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), NodeElementArchetype.Factory.SkeletonNodeNameSelect(40, Surface.Constants.LayoutOffsetY * 1, 120, 0), NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 1, "Node:"), - NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 0), - NodeElementArchetype.Factory.Output(1, "Transform", typeof(Transform), 1), + NodeElementArchetype.Factory.Output(0, "Transform", typeof(Transform), 1), } }, new NodeArchetype From 876a728b4105bfcff3beba44064eb88fc0fe7818 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 23:27:19 +0100 Subject: [PATCH 253/419] Fix scene animation rendering issue with game window is not selected tab Fixes #170 --- .../Windows/Assets/AssetEditorWindow.cs | 2 +- .../Windows/Assets/SceneAnimationWindow.cs | 28 +++++++++++++++---- Source/Editor/Windows/GameWindow.cs | 1 - 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Windows/Assets/AssetEditorWindow.cs b/Source/Editor/Windows/Assets/AssetEditorWindow.cs index 92c697b88..8ad655d01 100644 --- a/Source/Editor/Windows/Assets/AssetEditorWindow.cs +++ b/Source/Editor/Windows/Assets/AssetEditorWindow.cs @@ -430,7 +430,7 @@ namespace FlaxEditor.Windows.Assets /// /// Gets the original asset. Note: is the cloned asset for local editing. Use to apply changes to the original asset. /// - public T OriginalAsset => (T)FlaxEngine.Content.GetAsset(_item.ID); + public T OriginalAsset => (T)FlaxEngine.Content.Load(_item.ID); /// protected ClonedAssetEditorWindowBase(Editor editor, AssetItem item) diff --git a/Source/Editor/Windows/Assets/SceneAnimationWindow.cs b/Source/Editor/Windows/Assets/SceneAnimationWindow.cs index 95421585d..c18edc716 100644 --- a/Source/Editor/Windows/Assets/SceneAnimationWindow.cs +++ b/Source/Editor/Windows/Assets/SceneAnimationWindow.cs @@ -277,6 +277,7 @@ namespace FlaxEditor.Windows.Assets private readonly StagingTexture[] _stagingTextures = new StagingTexture[FrameLatency + 1]; private RenderProgress _progress; private RenderEditorState _editorState; + private GameWindow _gameWindow; public RenderOptions Options => _options; @@ -373,6 +374,9 @@ namespace FlaxEditor.Windows.Assets gameWin.Viewport.Task.PostRender += OnPostRender; if (!gameWin.Visible) gameWin.Show(); + else if (!gameWin.IsFocused) + gameWin.Focus(); + _gameWindow = gameWin; _warmUpTimeLeft = _options.WarmUpTime; _animationFrame = 0; var stagingTextureDesc = GPUTextureDescription.New2D(resolution.X, resolution.Y, gameWin.Viewport.Task.Output.Format, GPUTextureFlags.None); @@ -385,6 +389,12 @@ namespace FlaxEditor.Windows.Assets _stagingTextures[i].TaskFrame = -1; } _player.Play(); + if (!_player.IsPlaying) + { + Editor.LogError("Scene Animation Player failed to start playing."); + CancelRendering(); + return; + } if (_warmUpTimeLeft > 0.0f) { // Start warmup time @@ -438,8 +448,11 @@ namespace FlaxEditor.Windows.Assets _stagingTextures[i].Texture.ReleaseGPU(); Object.Destroy(ref _stagingTextures[i].Texture); } - _progress.End(); - editor.ProgressReporting.UnregisterHandler(_progress); + if (_progress != null) + { + _progress.End(); + editor.ProgressReporting.UnregisterHandler(_progress); + } if (_editorState != null) { editor.StateMachine.GoToState(editor.StateMachine.EditingSceneState); @@ -452,6 +465,7 @@ namespace FlaxEditor.Windows.Assets gameWin.Viewport.BackgroundColor = Color.Transparent; gameWin.Viewport.KeepAspectRatio = false; gameWin.Viewport.Task.PostRender -= OnPostRender; + _gameWindow = null; _isRendering = false; _presenter.Panel.Enabled = true; _presenter.BuildLayoutOnUpdate(); @@ -467,6 +481,11 @@ namespace FlaxEditor.Windows.Assets { // Render first frame _player.Play(); + if (!_player.IsPlaying) + { + Editor.LogError("Scene Animation Player failed to start playing."); + CancelRendering(); + } _warmUpTimeLeft = -1; _state = States.Render; } @@ -503,8 +522,7 @@ namespace FlaxEditor.Windows.Assets private void OnPostRender(GPUContext context, RenderContext renderContext) { - var gameWin = Editor.Instance.Windows.GameWin; - var task = gameWin.Viewport.Task; + var task = _gameWindow.Viewport.Task; var taskFrame = task.FrameCount; // Check all staging textures for finished GPU to CPU transfers @@ -540,7 +558,7 @@ namespace FlaxEditor.Windows.Assets ref var stagingTexture = ref _stagingTextures[textureIdx]; stagingTexture.AnimationFrame = _animationFrame; stagingTexture.TaskFrame = taskFrame; - _options.VideoOutputFormat.RenderFrame(context, ref stagingTexture, _options, gameWin.Viewport.Task.Output); + _options.VideoOutputFormat.RenderFrame(context, ref stagingTexture, _options, _gameWindow.Viewport.Task.Output); // Now wait for the next animation frame to be updated _state = States.Update; diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index c6a3fd24c..2f9be0254 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -387,7 +387,6 @@ namespace FlaxEditor.Windows } } - // Prevent closing the game window tab during a play session if (Editor.StateMachine.IsPlayMode && Editor.Options.Options.Input.CloseTab.Process(this, key)) { From 4b1172522618150f9a51f26c6fe23d784cf52779 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Jan 2021 23:59:24 +0100 Subject: [PATCH 254/419] Fix crash when creating Empty particle emitter Fixes #174 --- Source/Editor/Surface/Archetypes/Particles.cs | 2 +- Source/Engine/Serialization/Stream.cpp | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index 4d72bfa7b..1f3ee8ce6 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -338,7 +338,7 @@ namespace FlaxEditor.Surface.Archetypes Size = new Vector2(300, 600), DefaultValues = new object[] { - 500, // Capacity + 1000, // Capacity (int)ParticlesSimulationMode.Default, // Simulation Mode (int)ParticlesSimulationSpace.Local, // Simulation Space true, // Enable Pooling diff --git a/Source/Engine/Serialization/Stream.cpp b/Source/Engine/Serialization/Stream.cpp index 2d3ef8b30..0c82a379d 100644 --- a/Source/Engine/Serialization/Stream.cpp +++ b/Source/Engine/Serialization/Stream.cpp @@ -602,13 +602,21 @@ void WriteStream::WriteVariant(const Variant& data) break; case VariantType::Structure: case VariantType::Blob: - case VariantType::BoundingBox: - case VariantType::Transform: - case VariantType::Ray: - case VariantType::Matrix: WriteInt32(data.AsBlob.Length); WriteBytes(data.AsBlob.Data, data.AsBlob.Length); break; + case VariantType::BoundingBox: + WriteBytes(data.AsBlob.Data, sizeof(BoundingBox)); + break; + case VariantType::Transform: + WriteBytes(data.AsBlob.Data, sizeof(Transform)); + break; + case VariantType::Ray: + WriteBytes(data.AsBlob.Data, sizeof(Ray)); + break; + case VariantType::Matrix: + WriteBytes(data.AsBlob.Data, sizeof(Matrix)); + break; case VariantType::Asset: id = data.AsAsset ? data.AsAsset->GetID() : Guid::Empty; Write(&id); From 0de380ad3a45b5da6afc45a012aee6e60e60fe9f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Jan 2021 00:02:37 +0100 Subject: [PATCH 255/419] Optimize Variant typename loading if length is zero --- Source/Engine/Serialization/Stream.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Serialization/Stream.cpp b/Source/Engine/Serialization/Stream.cpp index 0c82a379d..269d49562 100644 --- a/Source/Engine/Serialization/Stream.cpp +++ b/Source/Engine/Serialization/Stream.cpp @@ -228,6 +228,8 @@ void ReadStream::ReadVariantType(VariantType* data) if (typeNameLength == MAX_int32) { ReadInt32(&typeNameLength); + if (typeNameLength == 0) + return; data->TypeName = static_cast(Allocator::Allocate(typeNameLength + 1)); char* ptr = data->TypeName; Read(ptr, typeNameLength); From bb84ad0f796f84755df87e63ce9e78aab289accb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Jan 2021 00:10:41 +0100 Subject: [PATCH 256/419] Add missing doc comment --- Source/Editor/Options/InputBinding.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Options/InputBinding.cs b/Source/Editor/Options/InputBinding.cs index df3d2dae6..eddd76882 100644 --- a/Source/Editor/Options/InputBinding.cs +++ b/Source/Editor/Options/InputBinding.cs @@ -161,6 +161,7 @@ namespace FlaxEditor.Options /// Processes this input binding to check if state matches. /// /// The input providing control. + /// The input key. /// True if input has been processed, otherwise false. public bool Process(Control control, KeyboardKeys key) { From a1cee0f0bf570a38d9722d3255dd3250fab0f008 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Jan 2021 00:38:08 +0100 Subject: [PATCH 257/419] Fix crash when applying changes to prefab --- Source/Engine/Level/Prefabs/Prefab.Apply.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 4305046e0..c0029aa93 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -850,7 +850,11 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr } // Keep root unlinked - root->_parent = nullptr; + if (root->_parent) + { + root->_parent->Children.Remove(root); + root->_parent = nullptr; + } } // Link objects hierarchy From a226ff87623533989cab90d60efd91f66ca15408 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Jan 2021 00:52:06 +0100 Subject: [PATCH 258/419] Fix plane model incorrect rotation in material preview (#165) --- Source/Editor/Viewport/Previews/MaterialPreview.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index a03598193..6ff695b69 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -23,6 +23,15 @@ namespace FlaxEditor.Viewport.Previews "Cone" }; + private static readonly Transform[] Transforms = + { + new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)), + new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)), + new Transform(Vector3.Zero, Quaternion.Identity, new Vector3(0.45f)), + new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)), + new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)), + }; + private StaticModel _previewModel; private Decal _decal; private Terrain _terrain; @@ -65,6 +74,7 @@ namespace FlaxEditor.Viewport.Previews _selectedModelIndex = value; _previewModel.Model = FlaxEngine.Content.LoadAsyncInternal("Editor/Primitives/" + Models[value]); + _previewModel.Transform = Transforms[value]; } } @@ -77,7 +87,6 @@ namespace FlaxEditor.Viewport.Previews { // Setup preview scene _previewModel = new StaticModel(); - _previewModel.Transform = new Transform(Vector3.Zero, Quaternion.RotationY(Mathf.Pi), new Vector3(0.45f)); SelectedModelIndex = 0; // Link actors for rendering From aedc6190096af2d008ff447c5b7b767e984b4d44 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Jan 2021 01:11:02 +0100 Subject: [PATCH 259/419] Fix crash when opening prefab that fails to load root ObjectsLookupIdMapping Fixes #126 --- Source/Engine/Level/Prefabs/PrefabManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Engine/Level/Prefabs/PrefabManager.cpp b/Source/Engine/Level/Prefabs/PrefabManager.cpp index f1fbf4766..44be0181b 100644 --- a/Source/Engine/Level/Prefabs/PrefabManager.cpp +++ b/Source/Engine/Level/Prefabs/PrefabManager.cpp @@ -173,6 +173,11 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, DictionaryAt(0); + if (!root) + { + LOG(Warning, "Failed to load prefab root object."); + return nullptr; + } // Prepare parent linkage for prefab root actor root->_parent = parent; From bd18286fbd781879c796b45ac890829fae404dca Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Thu, 28 Jan 2021 01:26:57 +0100 Subject: [PATCH 260/419] Refactoring and Node implementation Refactored all recently added nodes to maintain consistency. --- Source/Editor/Surface/Archetypes/Material.cs | 62 +++++++++++++++++- .../MaterialGenerator.Material.cpp | 63 ++++++++++++++----- 2 files changed, 106 insertions(+), 19 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 6e828fca4..f8fc025a8 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -725,7 +725,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 30, Title = "DDX", - Description = "Returns the partial derivative of the specified value with respect to the screen-space x-coordinate.", + Description = "Returns the partial derivative of the specified value with respect to the screen-space x-coordinate", Flags = NodeFlags.MaterialGraph, Size = new Vector2(90, 25), ConnectionsHints = ConnectionsHint.Numeric, @@ -741,7 +741,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 31, Title = "DDY", - Description = "Returns the partial derivative of the specified value with respect to the screen-space y-coordinate.", + Description = "Returns the partial derivative of the specified value with respect to the screen-space y-coordinate", Flags = NodeFlags.MaterialGraph, Size = new Vector2(90, 25), ConnectionsHints = ConnectionsHint.Numeric, @@ -753,6 +753,64 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), } }, + new NodeArchetype + { + TypeID = 32, + Title = "Sign", + Description = "Returns -1 if value is less than zero; 0 if value equals zero; and 1 if value is greater than zero", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(float), 1), + } + }, + new NodeArchetype + { + TypeID = 33, + Title = "Any", + Description = "True if any components of value are non-zero; otherwise, false", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 1), + } + }, + new NodeArchetype + { + TypeID = 34, + Title = "All", + Description = "Determines if all components of the specified value are non-zero", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(90, 25), + ConnectionsHints = ConnectionsHint.Numeric, + IndependentBoxes = new[] { 0 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Value", true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(bool), 1), + } + }, + new NodeArchetype + { + TypeID = 35, + Title = "Black Body", + Description = "Simulates black body radiation via a given temperature in kelvin", + Flags = NodeFlags.MaterialGraph, + Size = new Vector2(120, 25), + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Temp", true, typeof(float), 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector3), 1), + } + }, }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 0d8a0e226..d851e4b86 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -351,15 +351,15 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Rotator case 27: { - auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2(); - auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2(); - auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat(); + const auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2(); + const auto center = tryGetValue(node->GetBox(1), Value::Zero).AsVector2(); + const auto rotationAngle = tryGetValue(node->GetBox(2), Value::Zero).AsFloat(); - const auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, uv.Value), node); - const auto raCosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node); + auto x1 = writeLocal(ValueType::Vector2, String::Format(TEXT("({0} * -1) + {1}"), center.Value, uv.Value), node); + auto raCosSin = writeLocal(ValueType::Vector2, String::Format(TEXT("float2(cos({0}), sin({0}))"), rotationAngle.Value), node); - const auto dotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), raCosSin.Value), node); - const auto dotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), raCosSin.Value), node); + auto dotB1 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.x, {0}.y * -1)"), raCosSin.Value), node); + auto dotB2 = writeLocal(ValueType::Vector2, String::Format(TEXT("float2({0}.y, {0}.x)"), raCosSin.Value), node); value = writeLocal(ValueType::Vector2, String::Format(TEXT("{3} + float2(dot({0},{1}), dot({0},{2}))"), x1.Value, dotB1.Value, dotB2.Value, center.Value), node); break; @@ -367,11 +367,11 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Sphere Mask case 28: { - Value a = tryGetValue(node->GetBox(0), 0, Value::Zero); - Value b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type); - Value radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat(); - Value hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat(); - Value invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool(); + const auto a = tryGetValue(node->GetBox(0), 0, Value::Zero); + const auto b = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(a.Type); + const auto radius = tryGetValue(node->GetBox(2), node->Values[0]).AsFloat(); + const auto hardness = tryGetValue(node->GetBox(3), node->Values[1]).AsFloat(); + const auto invert = tryGetValue(node->GetBox(4), node->Values[2]).AsBool(); // Get distance and apply radius auto x1 = writeLocal(ValueType::Float, String::Format(TEXT("distance({0},{1}) * (1 / {2})"), a.Value, b.Value, radius.Value), node); @@ -385,9 +385,9 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Tiling & Offset case 29: { - auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2(); - auto tiling = tryGetValue(node->GetBox(1), node->Values[0]).AsVector2(); - auto offset = tryGetValue(node->GetBox(2), node->Values[1]).AsVector2(); + const auto uv = tryGetValue(node->GetBox(0), getUVs).AsVector2(); + const auto tiling = tryGetValue(node->GetBox(1), node->Values[0]).AsVector2(); + const auto offset = tryGetValue(node->GetBox(2), node->Values[1]).AsVector2(); value = writeLocal(ValueType::Vector2, String::Format(TEXT("{0} * {1} + {2}"), uv.Value, tiling.Value, offset.Value), node); break; @@ -395,7 +395,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // DDX case 30: { - auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); value = writeLocal(inValue.Type, String::Format(TEXT("ddx({0})"), inValue.Value), node); break; @@ -403,10 +403,39 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // DDY case 31: { - auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); value = writeLocal(inValue.Type, String::Format(TEXT("ddy({0})"), inValue.Value), node); break; + } + // Sign + case 32: + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(ValueType::Float, String::Format(TEXT("sign({0})"), inValue.Value), node); + break; + } + // Any + case 33: + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(ValueType::Bool, String::Format(TEXT("any({0})"), inValue.Value), node); + break; + } + // All + case 34: + { + const auto inValue = tryGetValue(node->GetBox(0), 0, Value::Zero); + + value = writeLocal(ValueType::Bool, String::Format(TEXT("all({0})"), inValue.Value), node); + break; + } + // Blackbody + case 35: + { + break; } default: break; From 50f6a01f83a36824d6bc011f1d186d6bda08621c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Jan 2021 01:28:13 +0100 Subject: [PATCH 261/419] Fix plane model normals --- Content/Editor/Primitives/Plane.flax | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content/Editor/Primitives/Plane.flax b/Content/Editor/Primitives/Plane.flax index bf2052f02..1e5eaf76b 100644 --- a/Content/Editor/Primitives/Plane.flax +++ b/Content/Editor/Primitives/Plane.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ab6d87b9f36f4ad231f38f415223130eea7b411be7ff8fd174b6ff7790258fb -size 2232 +oid sha256:5683a761f198d01d7189490d526b55e393eb01fffbd6761fb969d11067b3db73 +size 2277 From a250a232597aaa20f1897dddbe1cb13fb8668243 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Thu, 28 Jan 2021 16:13:45 +0100 Subject: [PATCH 262/419] Fix TexturePreview zooming Fix #164 --- Source/Editor/Viewport/Previews/TexturePreview.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Viewport/Previews/TexturePreview.cs b/Source/Editor/Viewport/Previews/TexturePreview.cs index d1904027a..5e1024609 100644 --- a/Source/Editor/Viewport/Previews/TexturePreview.cs +++ b/Source/Editor/Viewport/Previews/TexturePreview.cs @@ -159,13 +159,14 @@ namespace FlaxEditor.Viewport.Previews float prevScale = _viewScale; _viewScale = Mathf.Clamp(_viewScale + delta * 0.24f, 0.001f, 20.0f); - // Move view to make use of the control much more soother - //float coeff = (prevScale + (_viewScale - prevScale)) / prevScale; - //_viewPos += (location * coeff - location) / _viewScale; - //_viewPos += location / _viewScale; - Vector2 sizeDelta = (_viewScale - prevScale) * _textureRect.Size; + // Compensate for the Rectangle.MakeScaled + Vector2 sizeDelta = (_viewScale - prevScale) * _textureRect.Size * 0.5f; _viewPos += sizeDelta * 0.5f; + // Move to zoom position + Vector2 locationOnTexture = (location - _textureRect.Location) / _textureRect.Size; + _viewPos -= sizeDelta * locationOnTexture; + return true; } From 164da0f87a23d617643a02ea80395718ad1bf693 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Thu, 28 Jan 2021 16:24:36 +0100 Subject: [PATCH 263/419] Fix spelling error --- Source/Editor/Viewport/Cameras/ViewportCamera.cs | 14 +++++++------- Source/Editor/Viewport/Previews/AssetPreview.cs | 2 +- Source/Editor/Windows/Assets/ModelWindow.cs | 2 +- Source/Editor/Windows/Assets/SkinnedModelWindow.cs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Viewport/Cameras/ViewportCamera.cs b/Source/Editor/Viewport/Cameras/ViewportCamera.cs index a01000035..85b7a917c 100644 --- a/Source/Editor/Viewport/Cameras/ViewportCamera.cs +++ b/Source/Editor/Viewport/Cameras/ViewportCamera.cs @@ -32,9 +32,9 @@ namespace FlaxEditor.Viewport.Cameras /// /// The target object bounds. /// The margin distance scale of the orbit radius. - public void SerArcBallView(BoundingBox objectBounds, float marginDistanceScale = 2.0f) + public void SetArcBallView(BoundingBox objectBounds, float marginDistanceScale = 2.0f) { - SerArcBallView(BoundingSphere.FromBox(objectBounds), marginDistanceScale); + SetArcBallView(BoundingSphere.FromBox(objectBounds), marginDistanceScale); } /// @@ -42,18 +42,18 @@ namespace FlaxEditor.Viewport.Cameras /// /// The target object bounds. /// The margin distance scale of the orbit radius. - public void SerArcBallView(BoundingSphere objectBounds, float marginDistanceScale = 2.0f) + public void SetArcBallView(BoundingSphere objectBounds, float marginDistanceScale = 2.0f) { - SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), objectBounds.Center, objectBounds.Radius * marginDistanceScale); + SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), objectBounds.Center, objectBounds.Radius * marginDistanceScale); } /// /// Sets view orientation and position to match the arc ball camera style view for the given orbit radius. /// /// The orbit radius. - public void SerArcBallView(float orbitRadius) + public void SetArcBallView(float orbitRadius) { - SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); + SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); } /// @@ -62,7 +62,7 @@ namespace FlaxEditor.Viewport.Cameras /// The view rotation. /// The orbit center location. /// The orbit radius. - public void SerArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius) + public void SetArcBallView(Quaternion orientation, Vector3 orbitCenter, float orbitRadius) { // Rotate Viewport.ViewOrientation = orientation; diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index 012837f87..9bf3573a1 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -85,7 +85,7 @@ namespace FlaxEditor.Viewport.Previews var orbitRadius = 200.0f; if (camera is ArcBallCamera arcBallCamera) orbitRadius = arcBallCamera.OrbitRadius; - camera.SerArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); + camera.SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); if (useWidgets) { diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index 9910c6e7a..533c9694c 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -1004,7 +1004,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnAssetLoaded() { _refreshOnLODsLoaded = true; - _preview.ViewportCamera.SerArcBallView(Asset.GetBox()); + _preview.ViewportCamera.SetArcBallView(Asset.GetBox()); UpdateEffectsOnAsset(); // TODO: disable streaming for this model diff --git a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs index 2f6c952ba..c599e7e2a 100644 --- a/Source/Editor/Windows/Assets/SkinnedModelWindow.cs +++ b/Source/Editor/Windows/Assets/SkinnedModelWindow.cs @@ -1097,7 +1097,7 @@ namespace FlaxEditor.Windows.Assets protected override void OnAssetLoaded() { _refreshOnLODsLoaded = true; - _preview.ViewportCamera.SerArcBallView(Asset.GetBox()); + _preview.ViewportCamera.SetArcBallView(Asset.GetBox()); UpdateEffectsOnAsset(); // TODO: disable streaming for this model From 06f07c2903d1d10b82071d9519c7d87a02831a3c Mon Sep 17 00:00:00 2001 From: stefnotch Date: Thu, 28 Jan 2021 17:17:17 +0100 Subject: [PATCH 264/419] Fix EditorViewport zooming Fix #162 Also improves cases like LeftMouseDown + Scroll --- Source/Editor/Viewport/EditorViewport.cs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index a9a10c2d7..a19589c05 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -138,9 +138,7 @@ namespace FlaxEditor.Viewport private bool _isControllingMouse; private int _deltaFilteringStep; - private Vector2 _startPosMiddle; - private Vector2 _startPosRight; - private Vector2 _startPosLeft; + private Vector2 _startPos; private Vector2 _mouseDeltaRightLast; private Vector2[] _deltaFilteringBuffer = new Vector2[FpsCameraFilteringFrames]; @@ -850,7 +848,7 @@ namespace FlaxEditor.Viewport /// protected virtual void OnLeftMouseButtonDown() { - _startPosLeft = _viewMousePos; + _startPos = _viewMousePos; } /// @@ -865,7 +863,7 @@ namespace FlaxEditor.Viewport /// protected virtual void OnRightMouseButtonDown() { - _startPosRight = _viewMousePos; + _startPos = _viewMousePos; } /// @@ -880,7 +878,7 @@ namespace FlaxEditor.Viewport /// protected virtual void OnMiddleMouseButtonDown() { - _startPosMiddle = _viewMousePos; + _startPos = _viewMousePos; } /// @@ -1051,7 +1049,13 @@ namespace FlaxEditor.Viewport moveDelta *= 0.3f; // Calculate smooth mouse delta not dependant on viewport size - Vector2 offset = _viewMousePos - (_input.IsMouseMiddleDown ? _startPosMiddle : _startPosRight); + + Vector2 offset = _viewMousePos - _startPos; + if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown) + { + offset = Vector2.Zero; + } + offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X); offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y); _mouseDeltaRight = offset / size; @@ -1093,7 +1097,7 @@ namespace FlaxEditor.Viewport // Move mouse back to the root position if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown)) { - Vector2 center = PointToWindow(_input.IsMouseMiddleDown ? _startPosMiddle : _startPosRight); + Vector2 center = PointToWindow(_startPos); win.MousePosition = center; } } @@ -1139,11 +1143,11 @@ namespace FlaxEditor.Viewport if (_input.IsMouseLeftDown) { // Calculate smooth mouse delta not dependant on viewport size - Vector2 offset = _viewMousePos - _startPosLeft; + Vector2 offset = _viewMousePos - _startPos; offset.X = offset.X > 0 ? Mathf.Floor(offset.X) : Mathf.Ceil(offset.X); offset.Y = offset.Y > 0 ? Mathf.Floor(offset.Y) : Mathf.Ceil(offset.Y); _mouseDeltaLeft = offset / size; - _startPosLeft = _viewMousePos; + _startPos = _viewMousePos; } else { From beecbffdc34369fa098391bcb1ef9d95c2c9e693 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Jan 2021 21:59:30 +0100 Subject: [PATCH 265/419] Fix error when opening Anim Graph with node name not existing in skeleton anymore Fixes #176 --- .../Surface/Elements/SkeletonNodeNameSelectElement.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs b/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs index d73590ac7..4321eabd0 100644 --- a/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs +++ b/Source/Editor/Surface/Elements/SkeletonNodeNameSelectElement.cs @@ -32,8 +32,8 @@ namespace FlaxEditor.Surface.Elements } set { - if (!string.IsNullOrEmpty(value)) - SelectedIndex = _nodeNameToIndex[value]; + if (!string.IsNullOrEmpty(value) && _nodeNameToIndex.TryGetValue(value, out var index)) + SelectedIndex = index; else SelectedIndex = -1; } @@ -60,8 +60,8 @@ namespace FlaxEditor.Surface.Elements { _selectedIndices.Clear(); var selectedNode = (string)ParentNode.Values[Archetype.ValueIndex]; - if (!string.IsNullOrEmpty(selectedNode)) - _selectedIndices.Add(_nodeNameToIndex[selectedNode]); + if (!string.IsNullOrEmpty(selectedNode) && _nodeNameToIndex.TryGetValue(selectedNode, out var index)) + _selectedIndices.Add(index); OnSelectedIndexChanged(); } From f15f7ff59fc87c4562a2b50b3f1a5df093efddc5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Jan 2021 00:36:15 +0100 Subject: [PATCH 266/419] Bump up version --- Content/Shaders/BitonicSort.flax | 2 +- Flax.flaxproj | 2 +- Source/FlaxEngine.Gen.cs | 4 ++-- Source/FlaxEngine.Gen.h | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Content/Shaders/BitonicSort.flax b/Content/Shaders/BitonicSort.flax index d5964fca9..1d5b8a581 100644 --- a/Content/Shaders/BitonicSort.flax +++ b/Content/Shaders/BitonicSort.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df704c63770ee2b641f000726a3ec93c189685ffa149e484e961c9dba522baaf +oid sha256:f46a61cf8d5183230176e661a51208bfeece16cc7238655f406288ff448af64e size 6721 diff --git a/Flax.flaxproj b/Flax.flaxproj index 26fb340ba..0c2683afb 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,7 @@ "Version": { "Major": 1, "Minor": 0, - "Build": 6215 + "Build": 6216 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.", diff --git a/Source/FlaxEngine.Gen.cs b/Source/FlaxEngine.Gen.cs index ceab5ff53..04aef0556 100644 --- a/Source/FlaxEngine.Gen.cs +++ b/Source/FlaxEngine.Gen.cs @@ -13,5 +13,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("095aaaed-cc57-6182-57cc-82617b3c2889")] -[assembly: AssemblyVersion("1.0.6215")] -[assembly: AssemblyFileVersion("1.0.6215")] +[assembly: AssemblyVersion("1.0.6216")] +[assembly: AssemblyFileVersion("1.0.6216")] diff --git a/Source/FlaxEngine.Gen.h b/Source/FlaxEngine.Gen.h index 4ceda354d..53a18990c 100644 --- a/Source/FlaxEngine.Gen.h +++ b/Source/FlaxEngine.Gen.h @@ -5,11 +5,11 @@ #include "Engine/Core/Compiler.h" #define FLAXENGINE_NAME "FlaxEngine" -#define FLAXENGINE_VERSION Version(1, 0, 6215) -#define FLAXENGINE_VERSION_TEXT "1.0.6215" +#define FLAXENGINE_VERSION Version(1, 0, 6216) +#define FLAXENGINE_VERSION_TEXT "1.0.6216" #define FLAXENGINE_VERSION_MAJOR 1 #define FLAXENGINE_VERSION_MINOR 0 -#define FLAXENGINE_VERSION_BUILD 6215 +#define FLAXENGINE_VERSION_BUILD 6216 #define FLAXENGINE_COMPANY "Flax" #define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved." From 33ce17c3fa9041997b81c8e67562791e57b23f64 Mon Sep 17 00:00:00 2001 From: VNC <52937757+VNNCC@users.noreply.github.com> Date: Fri, 29 Jan 2021 16:18:00 +0100 Subject: [PATCH 267/419] Set minimum width and height for texture description in motion blur The calculation `motionVectorsWidth / tileSize` can return a result of 0. Set minimum width and height to 1. --- Source/Engine/Renderer/MotionBlurPass.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index 46b2ccd8f..065a9c53c 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -339,6 +339,13 @@ void MotionBlurPass::Render(RenderContext& renderContext, GPUTexture*& input, GP // Downscale motion vectors texture down to tileSize/tileSize (with max velocity calculation NxN kernel) rtDesc.Width = motionVectorsWidth / tileSize; rtDesc.Height = motionVectorsHeight / tileSize; + + if (rtDesc.Width < 1) + rtDesc.Width = 1; + + if (rtDesc.Height < 1) + rtDesc.Height = 1; + auto vMaxBuffer = RenderTargetPool::Get(rtDesc); context->ResetRenderTarget(); context->SetRenderTarget(vMaxBuffer->View()); From a5d6db6a1816c520b6282df78e966527ba1b383c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 30 Jan 2021 19:58:45 +0100 Subject: [PATCH 268/419] Fix InputText & Typo. --- Source/Engine/Input/Keyboard.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Input/Keyboard.h b/Source/Engine/Input/Keyboard.h index ae958bd86..08f80f5c5 100644 --- a/Source/Engine/Input/Keyboard.h +++ b/Source/Engine/Input/Keyboard.h @@ -13,7 +13,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Keyboard); public: /// - /// The mouse state. + /// The keyboard state. /// struct State { @@ -157,6 +157,7 @@ public: if (UpdateState()) return true; + _state.InputTextLength = 0; // Handle events for (int32 i = 0; i < _queue.Count(); i++) { From e75ca148c0aaa5e5cd9f68b6ed96ecbb104d7806 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sat, 30 Jan 2021 23:21:41 +0100 Subject: [PATCH 269/419] Added blackbody --- Source/Editor/Surface/Archetypes/Material.cs | 6 +++++- .../MaterialGenerator.Material.cpp | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index f8fc025a8..ceb9921cb 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -805,9 +805,13 @@ namespace FlaxEditor.Surface.Archetypes Description = "Simulates black body radiation via a given temperature in kelvin", Flags = NodeFlags.MaterialGraph, Size = new Vector2(120, 25), + DefaultValues = new object[] + { + 0.0f, + }, Elements = new[] { - NodeElementArchetype.Factory.Input(0, "Temp", true, typeof(float), 0), + NodeElementArchetype.Factory.Input(0, "Temp", true, typeof(float), 0, 0), NodeElementArchetype.Factory.Output(0, string.Empty, typeof(Vector3), 1), } }, diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index d851e4b86..34ed16d9a 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -435,6 +435,23 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Blackbody case 35: { + const auto temperature = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); + + // Value X + auto x = writeLocal(ValueType::Float, String::Format(TEXT("56100000.0f * pow({0}, (-3.0f / 2.0f)) + 148.0f"), temperature.Value), node); + + // Value Y + auto y = writeLocal(ValueType::Float, String::Format(TEXT("{0} > 6500.0f ? 35200000.0f * pow({0}, (-3.0f / 2.0f)) + 184.0f : 100.04f * log({0}) - 623.6f"), temperature.Value), node); + + // Value Z + auto z = writeLocal(ValueType::Float, String::Format(TEXT("194.18f * log({0}) - 1448.6f"), temperature.Value), node); + + // Final color + auto color = writeLocal(ValueType::Vector3, String::Format(TEXT("float3({0}, {1}, {2})"), x.Value, y.Value, z.Value), node); + color = writeLocal(ValueType::Vector3, String::Format(TEXT("clamp({0}, 0.0f, 255.0f) / 255.0f"), color.Value), node); + color = writeLocal(ValueType::Vector3, String::Format(TEXT("{1} < 1000.0f ? {0} * {1}/1000.0f : {0}"), color.Value, temperature.Value), node); + + value = color; break; } default: From bc6de6932b9f2b4fc7c23ebc688ac8717035a01b Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sat, 30 Jan 2021 23:28:53 +0100 Subject: [PATCH 270/419] Update MaterialGenerator.Material.cpp Already evaluate calculation instead --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 34ed16d9a..31d2ba36c 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -438,10 +438,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) const auto temperature = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); // Value X - auto x = writeLocal(ValueType::Float, String::Format(TEXT("56100000.0f * pow({0}, (-3.0f / 2.0f)) + 148.0f"), temperature.Value), node); + auto x = writeLocal(ValueType::Float, String::Format(TEXT("56100000.0f * pow({0}, -1) + 148.0f"), temperature.Value), node); // Value Y - auto y = writeLocal(ValueType::Float, String::Format(TEXT("{0} > 6500.0f ? 35200000.0f * pow({0}, (-3.0f / 2.0f)) + 184.0f : 100.04f * log({0}) - 623.6f"), temperature.Value), node); + auto y = writeLocal(ValueType::Float, String::Format(TEXT("{0} > 6500.0f ? 35200000.0f * pow({0}, -1) + 184.0f : 100.04f * log({0}) - 623.6f"), temperature.Value), node); // Value Z auto z = writeLocal(ValueType::Float, String::Format(TEXT("194.18f * log({0}) - 1448.6f"), temperature.Value), node); From 28d16fd620aec1864ad1208a053b02cda2b18cc7 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sat, 30 Jan 2021 23:32:35 +0100 Subject: [PATCH 271/419] Add notice --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 31d2ba36c..83251089f 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -435,6 +435,8 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Blackbody case 35: { + // Based on unity's implementation by using data gathered by Mitchell Charity. + const auto temperature = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); // Value X From 011b5f3e51e8cbf2a4b8a11a25de434d53d55399 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 11:14:59 +0100 Subject: [PATCH 272/419] Add SceneRenderTask.PreRender --- Source/Engine/Graphics/RenderTask.cpp | 5 +++++ Source/Engine/Graphics/RenderTask.h | 12 ++++++++++++ Source/Engine/Renderer/Renderer.cpp | 4 +--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index e3e261c96..d30e8e37c 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -260,6 +260,11 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext) CollectDrawCalls(renderContext); } +void SceneRenderTask::OnPreRender(GPUContext* context, RenderContext& renderContext) +{ + PreRender(context, renderContext); +} + void SceneRenderTask::OnPostRender(GPUContext* context, RenderContext& renderContext) { PostRender(context, renderContext); diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index b00d80a5f..265905e2a 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -313,6 +313,18 @@ public: /// The rendering context. virtual void OnCollectDrawCalls(RenderContext& renderContext); + /// + /// The action called after scene rendering. Can be used to perform custom pre-rendering or to modify the render view. + /// + API_EVENT() Delegate PreRender; + + /// + /// Called before scene rendering. Can be used to perform custom pre-rendering or to modify the render view. + /// + /// The GPU commands context. + /// The rendering context. + virtual void OnPreRender(GPUContext* context, RenderContext& renderContext); + /// /// The action called after scene rendering. Can be used to render additional visual elements to the output. /// diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index c2a18f519..4d3241375 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -199,9 +199,8 @@ void Renderer::Render(SceneRenderTask* task) #endif // Perform the actual rendering + task->OnPreRender(context, renderContext); RenderInner(task, renderContext); - - // Custom additional rendering task->OnPostRender(context, renderContext); #if USE_EDITOR @@ -306,7 +305,6 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext) #endif renderContext.List->Settings.AntiAliasing.Mode = aaMode; - // Prepare renderContext.View.Prepare(renderContext); renderContext.Buffers->Prepare(); From fecf7d7804202bcb79035893f258a9787bfd03e0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 11:18:46 +0100 Subject: [PATCH 273/419] Add virtual to Camera GetMatrices for custom cameras --- Source/Engine/Level/Actors/Camera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h index 8076e6a60..edf94ad22 100644 --- a/Source/Engine/Level/Actors/Camera.h +++ b/Source/Engine/Level/Actors/Camera.h @@ -211,7 +211,7 @@ public: /// The result camera view matrix. /// The result camera projection matrix. /// The custom output viewport. Use null to skip it. - API_FUNCTION() void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport) const; + API_FUNCTION() virtual void GetMatrices(API_PARAM(Out) Matrix& view, API_PARAM(Out) Matrix& projection, API_PARAM(Ref) const Viewport& viewport) const; #if USE_EDITOR // Intersection check for editor picking the camera From 283714a39f4582c3d5d7cc849791be22ce95d330 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 11:40:50 +0100 Subject: [PATCH 274/419] Fix PhysX header usage in public API --- Source/Engine/Physics/Actors/IPhysicsActor.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Physics/Actors/IPhysicsActor.h b/Source/Engine/Physics/Actors/IPhysicsActor.h index 8aebe3aeb..28f7fddf4 100644 --- a/Source/Engine/Physics/Actors/IPhysicsActor.h +++ b/Source/Engine/Physics/Actors/IPhysicsActor.h @@ -2,7 +2,11 @@ #pragma once -#include +namespace physx +{ + class PxRigidActor; + class PxTransform; +} /// /// A base interface for all physical actors types/owners that can responds on transformation changed event. @@ -20,7 +24,7 @@ public: /// Gets the rigid actor (PhysX object) may be null. /// /// PhysX rigid actor or null if not using - virtual PxRigidActor* GetRigidActor() = 0; + virtual physx::PxRigidActor* GetRigidActor() = 0; /// /// Called when actor's active transformation gets changed after the physics simulation step. From bf80827bfdad603fbee11db93910b00b7eb73c5a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 13:50:56 +0100 Subject: [PATCH 275/419] Add more Spline API for curve editing --- Source/Engine/Level/Actors/Spline.cpp | 24 ++++++++++++++++ Source/Engine/Level/Actors/Spline.h | 41 +++++++++++++++++++++------ Source/Engine/Level/Spline.cs | 22 ++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index a3757253f..f522a8770 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -244,6 +244,14 @@ void Spline::ClearSpline() UpdateSpline(); } +void Spline::RemoveSplinePoint(int32 index, bool updateSpline) +{ + CHECK(index >= 0 && index < GetSplinePointsCount()); + Curve.GetKeyframes().RemoveAtKeepOrder(index); + if (updateSpline) + UpdateSpline(); +} + void Spline::SetSplinePoint(int32 index, const Vector3& point, bool updateSpline) { CHECK(index >= 0 && index < GetSplinePointsCount()); @@ -331,6 +339,22 @@ void Spline::AddSplineLocalPoint(const Transform& point, bool updateSpline) UpdateSpline(); } +void Spline::InsertSplinePoint(int32 index, float time, const Transform& point, bool updateSpline) +{ + const Keyframe k(time, _transform.WorldToLocal(point)); + Curve.GetKeyframes().Insert(index, k); + if (updateSpline) + UpdateSpline(); +} + +void Spline::InsertSplineLocalPoint(int32 index, float time, const Transform& point, bool updateSpline) +{ + const Keyframe k(time, point); + Curve.GetKeyframes().Insert(index, k); + if (updateSpline) + UpdateSpline(); +} + void Spline::SetTangentsLinear() { const int32 count = Curve.GetKeyframes().Count(); diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index 772aac821..7f8076eac 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -216,12 +216,19 @@ public: /// API_FUNCTION() void ClearSpline(); + /// + /// Removes the spline curve point at the given index. + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// True if update spline after removing the point, otherwise false. + API_FUNCTION() void RemoveSplinePoint(int32 index, bool updateSpline = true); + /// /// Sets the spline curve at the given index (world-space). /// /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). /// The location of the point to set (world-space). - /// True if update spline after adding the point, otherwise false. + /// True if update spline after editing the point, otherwise false. API_FUNCTION() void SetSplinePoint(int32 index, const Vector3& point, bool updateSpline = true); /// @@ -229,7 +236,7 @@ public: /// /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). /// The location of the point to set (local-space). - /// True if update spline after adding the point, otherwise false. + /// True if update spline after editing the point, otherwise false. API_FUNCTION() void SetSplineLocalPoint(int32 index, const Vector3& point, bool updateSpline = true); /// @@ -237,7 +244,7 @@ public: /// /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). /// The transformation of the point to set (world-space). - /// True if update spline after adding the point, otherwise false. + /// True if update spline after editing the point, otherwise false. API_FUNCTION() void SetSplineTransform(int32 index, const Transform& point, bool updateSpline = true); /// @@ -245,7 +252,7 @@ public: /// /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). /// The transformation of the point to set (local-space). - /// True if update spline after adding the point, otherwise false. + /// True if update spline after editing the point, otherwise false. API_FUNCTION() void SetSplineLocalTransform(int32 index, const Transform& point, bool updateSpline = true); /// @@ -255,7 +262,7 @@ public: /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). /// The transformation of the tangent to set (world-space). /// True if set arrive tangent, otherwise sets leave tangent (in or out). - /// True if update spline after adding the point, otherwise false. + /// True if update spline after editing the point, otherwise false. API_FUNCTION() void SetSplineTangent(int32 index, const Transform& point, bool isIn, bool updateSpline = true); /// @@ -265,7 +272,7 @@ public: /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). /// The transformation of the tangent to set (local-space). /// True if set arrive tangent, otherwise sets leave tangent (in or out). - /// True if update spline after adding the point, otherwise false. + /// True if update spline after editing the point, otherwise false. API_FUNCTION() void SetSplineLocalTangent(int32 index, const Transform& point, bool isIn, bool updateSpline = true); /// @@ -273,14 +280,14 @@ public: /// /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). /// The time to set. - /// True if update spline after adding the point, otherwise false. + /// True if update spline after editing the point, otherwise false. API_FUNCTION() void SetSplinePointTime(int32 index, float time, bool updateSpline = true); /// /// Adds the point to the spline curve (at the end). /// /// The location of the point to add to the curve (world-space). - /// True if update spline after adding the point, otherwise false. + /// True if update spline after editing the point, otherwise false. API_FUNCTION() void AddSplinePoint(const Vector3& point, bool updateSpline = true); /// @@ -304,6 +311,24 @@ public: /// True if update spline after adding the point, otherwise false. API_FUNCTION() void AddSplineLocalPoint(const Transform& point, bool updateSpline = true); + /// + /// Inserts the spline curve point at the given index (world-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The time value. + /// The location of the point to add to the curve (world-space). + /// True if update spline after removing the point, otherwise false. + API_FUNCTION() void InsertSplinePoint(int32 index, float time, const Transform& point, bool updateSpline = true); + + /// + /// Inserts the spline curve point at the given index (local-space). + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The time value. + /// The location of the point to add to the curve (local-space). + /// True if update spline after removing the point, otherwise false. + API_FUNCTION() void InsertSplineLocalPoint(int32 index, float time, const Transform& point, bool updateSpline = true); + /// /// Updates the curve tangent points to make curve linear. /// diff --git a/Source/Engine/Level/Spline.cs b/Source/Engine/Level/Spline.cs index 46a05cd5d..29c65763a 100644 --- a/Source/Engine/Level/Spline.cs +++ b/Source/Engine/Level/Spline.cs @@ -37,5 +37,27 @@ namespace FlaxEngine Internal_SetKeyframes(__unmanagedPtr, value); } } + + /// + /// Gets the spline keyframe. + /// + /// The spline point index. + /// The keyframe. + public BezierCurve.Keyframe GetSplineKeyframe(int index) + { + return SplineKeyframes[index]; + } + + /// + /// Sets the spline keyframe. + /// + /// The spline point index. + /// The keyframe. + public void SetSplineKeyframe(int index, BezierCurve.Keyframe keyframe) + { + SetSplineLocalTransform(index, keyframe.Value, false); + SetSplineLocalTangent(index, keyframe.Value + keyframe.TangentIn, true, false); + SetSplineLocalTangent(index, keyframe.Value + keyframe.TangentOut, false); + } } } From bab236c081d76ad96bc3762e3f97422fdf8b78e5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 13:51:32 +0100 Subject: [PATCH 276/419] Add support for custom SceneGraphNode to handle delete with undo --- Source/Editor/SceneGraph/SceneGraphNode.cs | 40 ++++++ .../Editor/Undo/Actions/DeleteActorsAction.cs | 119 ++++++++++++++---- .../Windows/Assets/PrefabWindow.Actions.cs | 7 +- 3 files changed, 136 insertions(+), 30 deletions(-) diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs index 755aea139..c06c0d1c4 100644 --- a/Source/Editor/SceneGraph/SceneGraphNode.cs +++ b/Source/Editor/SceneGraph/SceneGraphNode.cs @@ -312,6 +312,46 @@ namespace FlaxEditor.SceneGraph { } + /// + /// The scene graph node state container. Used for Editor undo actions (eg. restoring deleted node). + /// + public struct StateData + { + /// + /// The name of the scene graph node type (full). + /// + public string TypeName; + + /// + /// The name of the method (in ) that takes this state as a parameter and returns the created scene graph node. Used by the undo actions to restore deleted objects. + /// + public string CreateMethodName; + + /// + /// The custom state data (as string). + /// + public string State; + + /// + /// The custom state data (as raw bytes). + /// + public byte[] StateRaw; + } + + /// + /// Gets a value indicating whether this node can use property for editor undo operations. + /// + public virtual bool CanUseState => false; + + /// + /// Gets or sets the node state. + /// + public virtual StateData State + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + /// /// Deletes object represented by this node eg. actor. /// diff --git a/Source/Editor/Undo/Actions/DeleteActorsAction.cs b/Source/Editor/Undo/Actions/DeleteActorsAction.cs index fa319e6f3..41dac3ccc 100644 --- a/Source/Editor/Undo/Actions/DeleteActorsAction.cs +++ b/Source/Editor/Undo/Actions/DeleteActorsAction.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using FlaxEditor.SceneGraph; +using FlaxEditor.Scripting; using FlaxEngine; namespace FlaxEditor.Actions @@ -15,7 +16,13 @@ namespace FlaxEditor.Actions class DeleteActorsAction : IUndoAction { [Serialize] - private byte[] _data; + private byte[] _actorsData; + + [Serialize] + private List _nodesData; + + [Serialize] + private Guid[] _nodeParentsIDs; [Serialize] private Guid[] _prefabIds; @@ -30,31 +37,51 @@ namespace FlaxEditor.Actions /// The node parents. /// [Serialize] - protected List _nodeParents; + protected List _nodeParents; /// /// Initializes a new instance of the class. /// - /// The objects. + /// The objects. /// If set to true action will be inverted - instead of delete it will be create actors. - internal DeleteActorsAction(List objects, bool isInverted = false) + internal DeleteActorsAction(List nodes, bool isInverted = false) { _isInverted = isInverted; - _nodeParents = new List(objects.Count); - var actorNodes = new List(objects.Count); - var actors = new List(objects.Count); - for (int i = 0; i < objects.Count; i++) + + // Collect nodes to delete + var deleteNodes = new List(nodes.Count); + var actors = new List(nodes.Count); + for (int i = 0; i < nodes.Count; i++) { - if (objects[i] is ActorNode node) + var node = nodes[i]; + if (node is ActorNode actorNode) { - actorNodes.Add(node); - actors.Add(node.Actor); + deleteNodes.Add(actorNode); + actors.Add(actorNode.Actor); + } + else + { + deleteNodes.Add(node); + if (node.CanUseState) + { + if (_nodesData == null) + _nodesData = new List(); + _nodesData.Add(node.State); + } } } - actorNodes.BuildNodesParents(_nodeParents); - _data = Actor.ToBytes(actors.ToArray()); + // Collect parent nodes to delete + _nodeParents = new List(nodes.Count); + deleteNodes.BuildNodesParents(_nodeParents); + _nodeParentsIDs = new Guid[_nodeParents.Count]; + for (int i = 0; i < _nodeParentsIDs.Length; i++) + _nodeParentsIDs[i] = _nodeParents[i].ID; + // Serialize actors + _actorsData = Actor.ToBytes(actors.ToArray()); + + // Cache actors linkage to prefab objects _prefabIds = new Guid[actors.Count]; _prefabObjectIds = new Guid[actors.Count]; for (int i = 0; i < actors.Count; i++) @@ -88,9 +115,11 @@ namespace FlaxEditor.Actions /// public void Dispose() { - _data = null; + _actorsData = null; + _nodeParentsIDs = null; _prefabIds = null; _prefabObjectIds = null; + _nodeParents.Clear(); } /// @@ -123,28 +152,64 @@ namespace FlaxEditor.Actions /// protected virtual void Create() { - // Restore objects - var actors = Actor.FromBytes(_data); - if (actors == null) - return; - for (int i = 0; i < actors.Length; i++) + var nodes = new List(); + + // Restore actors + var actors = Actor.FromBytes(_actorsData); + if (actors != null) { - Guid prefabId = _prefabIds[i]; - if (prefabId != Guid.Empty) + nodes.Capacity = Math.Max(nodes.Capacity, actors.Length); + + // Preserve prefab objects linkage + for (int i = 0; i < actors.Length; i++) { - Actor.Internal_LinkPrefab(FlaxEngine.Object.GetUnmanagedPtr(actors[i]), ref prefabId, ref _prefabObjectIds[i]); + Guid prefabId = _prefabIds[i]; + if (prefabId != Guid.Empty) + { + Actor.Internal_LinkPrefab(FlaxEngine.Object.GetUnmanagedPtr(actors[i]), ref prefabId, ref _prefabObjectIds[i]); + } } } - var actorNodes = new List(actors.Length); - for (int i = 0; i < actors.Length; i++) + + // Restore nodes state + if (_nodesData != null) { - var foundNode = GetNode(actors[i].ID); + for (int i = 0; i < _nodesData.Count; i++) + { + var state = _nodesData[i]; + var type = TypeUtils.GetManagedType(state.TypeName); + if (type == null) + { + Editor.LogError($"Missing type {state.TypeName} for scene graph node undo state restore."); + continue; + } + var method = type.GetMethod(state.CreateMethodName); + if (method == null) + { + Editor.LogError($"Missing method {state.CreateMethodName} from type {state.TypeName} for scene graph node undo state restore."); + continue; + } + var node = method.Invoke(null, new object[] { state }); + if (node == null) + { + Editor.LogError($"Failed to restore scene graph node state via method {state.CreateMethodName} from type {state.TypeName}."); + continue; + } + } + } + + // Cache parent nodes ids + for (int i = 0; i < _nodeParentsIDs.Length; i++) + { + var foundNode = GetNode(_nodeParentsIDs[i]); if (foundNode is ActorNode node) { - actorNodes.Add(node); + nodes.Add(node); } } - actorNodes.BuildNodesParents(_nodeParents); + nodes.BuildNodesParents(_nodeParents); + + // Mark scenes as modified for (int i = 0; i < _nodeParents.Count; i++) { Editor.Instance.Scene.MarkSceneEdited(_nodeParents[i].ParentScene); diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs index 00a96910f..fca4909f0 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs @@ -194,8 +194,8 @@ namespace FlaxEditor.Windows.Assets private class CustomDeleteActorsAction : DeleteActorsAction { - public CustomDeleteActorsAction(List objects, bool isInverted = false) - : base(objects, isInverted) + public CustomDeleteActorsAction(List nodes, bool isInverted = false) + : base(nodes, isInverted) { } @@ -207,7 +207,8 @@ namespace FlaxEditor.Windows.Assets // Unlink nodes from parents (actors spawned for prefab editing are not in a gameplay and may not send some important events) for (int i = 0; i < nodes.Length; i++) { - nodes[i].Actor.Parent = null; + if (nodes[i] is ActorNode actorNode) + actorNode.Actor.Parent = null; } base.Delete(); From 69790f3576e2676fdc3cc6e8442f104d78525510 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 13:51:37 +0100 Subject: [PATCH 277/419] Add support for deleting selected spline points (with undo) --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 48030dbd4..7b7e98239 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -3,6 +3,7 @@ using System; using FlaxEngine; using FlaxEngine.Json; +using Object = FlaxEngine.Object; namespace FlaxEditor.SceneGraph.Actors { @@ -26,6 +27,10 @@ namespace FlaxEditor.SceneGraph.Actors public override bool CanBeSelectedDirectly => true; + public override bool CanDelete => true; + + public override bool CanUseState => true; + public override Transform Transform { get @@ -40,6 +45,39 @@ namespace FlaxEditor.SceneGraph.Actors } } + private struct Data + { + public Guid Spline; + public int Index; + public BezierCurve.Keyframe Keyframe; + } + + public override StateData State + { + get + { + var actor = (Spline)_node.Actor; + return new StateData + { + TypeName = typeof(SplinePointNode).FullName, + CreateMethodName = nameof(Create), + State = JsonSerializer.Serialize(new Data + { + Spline = actor.ID, + Index = Index, + Keyframe = actor.GetSplineKeyframe(Index), + }), + }; + } + set => throw new NotImplementedException(); + } + + public override void Delete() + { + var actor = (Spline)_node.Actor; + actor.RemoveSplinePoint(Index); + } + public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal) { var actor = (Spline)_node.Actor; @@ -73,6 +111,19 @@ namespace FlaxEditor.SceneGraph.Actors DebugDraw.DrawWireSphere(new BoundingSphere(tangentOut, 4.0f), Color.White, 0, false); } } + + public static SceneGraphNode Create(StateData state) + { + var data = JsonSerializer.Deserialize(state.State); + var spline = Object.Find(ref data.Spline); + spline.InsertSplineLocalPoint(data.Index, data.Keyframe.Time, data.Keyframe.Value, false); + spline.SetSplineKeyframe(data.Index, data.Keyframe); + var splineNode = (SplineNode)SceneGraphFactory.FindNode(data.Spline); + if (splineNode == null) + return null; + splineNode.OnUpdate(); + return splineNode.ActorChildNodes[data.Index]; + } } private sealed class SplinePointTangentNode : ActorChildNode From d69b00579253a5bba8c2bd7d96ecf147e9ee3d3f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 14:16:32 +0100 Subject: [PATCH 278/419] Move Curve data serialization to binary format into CurveSerialization.h --- Source/Engine/Animations/Curve.h | 65 ++----------------- Source/Engine/Animations/CurveSerialization.h | 49 +++++++++++++- Source/Engine/Content/Assets/Animation.cpp | 13 ++-- Source/Engine/Graphics/Models/ModelData.cpp | 9 +-- 4 files changed, 66 insertions(+), 70 deletions(-) diff --git a/Source/Engine/Animations/Curve.h b/Source/Engine/Animations/Curve.h index 24dcd87a8..be5c268ef 100644 --- a/Source/Engine/Animations/Curve.h +++ b/Source/Engine/Animations/Curve.h @@ -4,9 +4,7 @@ #include "AnimationUtils.h" #include "Engine/Core/Collections/Array.h" -#include "Engine/Core/Types/DataContainer.h" -#include "Engine/Serialization/ReadStream.h" -#include "Engine/Serialization/WriteStream.h" +#include "Engine/Core/Types/Span.h" // @formatter:off @@ -289,7 +287,7 @@ class CurveBase { public: - typedef DataContainer KeyFrameData; + typedef Span KeyFrameData; protected: @@ -641,7 +639,7 @@ public: /// If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped. void Evaluate(T& result, float time, bool loop = true) const { - typename Base::KeyFrameData data(_keyframes); + typename Base::KeyFrameData data(_keyframes.Get(), _keyframes.Count()); Base::Evaluate(data, result, time, loop); } @@ -653,7 +651,7 @@ public: /// If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped. void EvaluateFirstDerivative(T& result, float time, bool loop = true) const { - typename Base::KeyFrameData data(_keyframes); + typename Base::KeyFrameData data(_keyframes.Get(), _keyframes.Count()); Base::EvaluateFirstDerivative(data, result, time, loop); } @@ -665,7 +663,7 @@ public: /// If true the curve will loop when it goes past the end or beginning. Otherwise the curve value will be clamped. void EvaluateKey(KeyFrame& result, float time, bool loop = true) const { - typename Base::KeyFrameData data(_keyframes); + typename Base::KeyFrameData data(_keyframes.Get(), _keyframes.Count()); Base::EvaluateKey(data, result, time, loop); } @@ -686,7 +684,7 @@ public: return; } - typename Base::KeyFrameData data(_keyframes); + typename Base::KeyFrameData data(_keyframes.Get(), _keyframes.Count()); KeyFrame startValue, endValue; Base::EvaluateKey(data, startValue, start, false); Base::EvaluateKey(data, endValue, end, false); @@ -749,57 +747,6 @@ public: _keyframes[i].Time = _keyframes[i].Time * timeScale + timeOffset;; } -public: - - /// - /// Serializes the curve data to the stream. - /// - /// The output stream. - void Serialize(WriteStream& stream) const - { - // Version - if (_keyframes.IsEmpty()) - { - stream.WriteInt32(0); - return; - } - stream.WriteInt32(1); - - // TODO: support compression (serialize compression mode) - - // Raw keyframes data - stream.WriteInt32(_keyframes.Count()); - stream.WriteBytes(_keyframes.Get(), _keyframes.Count() * sizeof(KeyFrame)); - } - - /// - /// Deserializes the curve data from the stream. - /// - /// The input stream. - bool Deserialize(ReadStream& stream) - { - // Cleanup - _keyframes.Resize(0); - - // Version - int32 version; - stream.ReadInt32(&version); - if (version == 0) - return false; - if (version != 1) - { - return true; - } - - // Raw keyframes data - int32 keyframesCount; - stream.ReadInt32(&keyframesCount); - _keyframes.Resize(keyframesCount, false); - stream.ReadBytes(_keyframes.Get(), _keyframes.Count() * sizeof(KeyFrame)); - - return false; - } - public: FORCE_INLINE KeyFrame& operator[](int32 index) diff --git a/Source/Engine/Animations/CurveSerialization.h b/Source/Engine/Animations/CurveSerialization.h index 06f7788ea..4af89ceca 100644 --- a/Source/Engine/Animations/CurveSerialization.h +++ b/Source/Engine/Animations/CurveSerialization.h @@ -3,7 +3,9 @@ #pragma once #include "Curve.h" -#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Types/DataContainer.h" +#include "Engine/Serialization/ReadStream.h" +#include "Engine/Serialization/WriteStream.h" #include "Engine/Serialization/Serialization.h" // @formatter:off @@ -166,6 +168,51 @@ namespace Serialization Deserialize(keyframesArray[i], keyframes[i], modifier); } } + + template + inline void Serialize(WriteStream& stream, const Curve& v) + { + auto& keyframes = v.GetKeyframes(); + + // Version + if (keyframes.IsEmpty()) + { + stream.WriteInt32(0); + return; + } + stream.WriteInt32(1); + + // TODO: support compression (serialize compression mode) + + // Raw keyframes data + stream.WriteInt32(keyframes.Count()); + stream.WriteBytes(keyframes.Get(), keyframes.Count() * sizeof(KeyFrame)); + } + + template + inline bool Deserialize(ReadStream& stream, Curve& v) + { + auto& keyframes = v.GetKeyframes(); + keyframes.Resize(0); + + // Version + int32 version; + stream.ReadInt32(&version); + if (version == 0) + return false; + if (version != 1) + { + return true; + } + + // Raw keyframes data + int32 keyframesCount; + stream.ReadInt32(&keyframesCount); + keyframes.Resize(keyframesCount, false); + stream.ReadBytes(keyframes.Get(), keyframes.Count() * sizeof(KeyFrame)); + + return false; + } } // @formatter:on diff --git a/Source/Engine/Content/Assets/Animation.cpp b/Source/Engine/Content/Assets/Animation.cpp index c6927530e..963ddaceb 100644 --- a/Source/Engine/Content/Assets/Animation.cpp +++ b/Source/Engine/Content/Assets/Animation.cpp @@ -5,6 +5,7 @@ #include "Engine/Core/Log.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" +#include "Engine/Animations/CurveSerialization.h" #include "Engine/Serialization/MemoryReadStream.h" #if USE_EDITOR #include "Engine/Serialization/MemoryWriteStream.h" @@ -358,9 +359,9 @@ bool Animation::Save(const StringView& path) { auto& anim = Data.Channels[i]; stream.WriteString(anim.NodeName, 172); - anim.Position.Serialize(stream); - anim.Rotation.Serialize(stream); - anim.Scale.Serialize(stream); + Serialization::Serialize(stream, anim.Position); + Serialization::Serialize(stream, anim.Rotation); + Serialization::Serialize(stream, anim.Scale); } // Set data to the chunk asset @@ -442,9 +443,9 @@ Asset::LoadResult Animation::load() auto& anim = Data.Channels[i]; stream.ReadString(&anim.NodeName, 172); - bool failed = anim.Position.Deserialize(stream); - failed |= anim.Rotation.Deserialize(stream); - failed |= anim.Scale.Deserialize(stream); + bool failed = Serialization::Deserialize(stream, anim.Position); + failed |= Serialization::Deserialize(stream, anim.Rotation); + failed |= Serialization::Deserialize(stream, anim.Scale); if (failed) { diff --git a/Source/Engine/Graphics/Models/ModelData.cpp b/Source/Engine/Graphics/Models/ModelData.cpp index 56981e65d..71b511b4f 100644 --- a/Source/Engine/Graphics/Models/ModelData.cpp +++ b/Source/Engine/Graphics/Models/ModelData.cpp @@ -2,6 +2,7 @@ #include "ModelData.h" #include "Engine/Core/Log.h" +#include "Engine/Animations/CurveSerialization.h" #include "Engine/Serialization/WriteStream.h" #include "Engine/Debug/Exceptions/ArgumentNullException.h" #include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h" @@ -753,7 +754,7 @@ bool ModelData::Pack2SkinnedModelHeader(WriteStream* stream) const stream->Write(&sphere); // TODO: calculate Sphere and Box at once - make it faster using SSE - + // Blend Shapes const int32 blendShapes = mesh.BlendShapes.Count(); stream->WriteUint16(blendShapes); @@ -830,9 +831,9 @@ bool ModelData::Pack2AnimationHeader(WriteStream* stream) const auto& anim = Animation.Channels[i]; stream->WriteString(anim.NodeName, 172); - anim.Position.Serialize(*stream); - anim.Rotation.Serialize(*stream); - anim.Scale.Serialize(*stream); + Serialization::Serialize(*stream, anim.Position); + Serialization::Serialize(*stream, anim.Rotation); + Serialization::Serialize(*stream, anim.Scale); } return false; From b22805f8662d6a2918701fa7fe3b1d642d459891 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 16:03:33 +0100 Subject: [PATCH 279/419] Add support for custom duplicate of SceneGraphNode --- Source/Editor/Modules/SceneEditingModule.cs | 91 +++++++++++++++---- Source/Editor/SceneGraph/ActorChildNode.cs | 3 + Source/Editor/SceneGraph/ActorNode.cs | 3 + Source/Editor/SceneGraph/Actors/SceneNode.cs | 3 + Source/Editor/SceneGraph/RootNode.cs | 3 + Source/Editor/SceneGraph/SceneGraphNode.cs | 16 ++++ .../Windows/Assets/PrefabWindow.Actions.cs | 2 +- 7 files changed, 103 insertions(+), 18 deletions(-) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 78ffd26b0..96aefe804 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -358,7 +358,15 @@ namespace FlaxEditor.Modules var pasteAction = PasteActorsAction.Paste(data, pasteTargetActor?.ID ?? Guid.Empty); if (pasteAction != null) { - OnPasteAction(pasteAction); + pasteAction.Do(out _, out var nodeParents); + + // Select spawned objects (parents only) + var selectAction = new SelectionChangeAction(Selection.ToArray(), nodeParents.Cast().ToArray(), OnSelectionUndo); + selectAction.Do(); + + // Build single compound undo action that pastes the actors and selects the created objects (parents only) + Undo.AddAction(new MultiUndoAction(pasteAction, selectAction)); + OnSelectionChanged(); } } @@ -377,12 +385,57 @@ namespace FlaxEditor.Modules public void Duplicate() { // Peek things that can be copied (copy all actors) - var objects = Selection.Where(x => x.CanCopyPaste).ToList().BuildAllNodes().Where(x => x.CanCopyPaste && x is ActorNode).ToList(); - if (objects.Count == 0) + var nodes = Selection.Where(x => x.CanDuplicate).ToList().BuildAllNodes(); + if (nodes.Count == 0) return; + var actors = new List(); + var newSelection = new List(); + List customUndoActions = null; + foreach (var node in nodes) + { + if (node.CanDuplicate) + { + if (node is ActorNode actorNode) + { + actors.Add(actorNode.Actor); + } + else + { + var customDuplicatedObject = node.Duplicate(out var customUndoAction); + if (customDuplicatedObject != null) + newSelection.Add(customDuplicatedObject); + if (customUndoAction != null) + { + if (customUndoActions == null) + customUndoActions = new List(); + customUndoActions.Add(customUndoAction); + } + } + } + } + if (actors.Count == 0) + { + // Duplicate custom scene graph nodes only without actors + if (newSelection.Count != 0) + { + // Select spawned objects (parents only) + var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo); + selectAction.Do(); + + // Build a single compound undo action that pastes the actors, pastes custom stuff (scene graph extension) and selects the created objects (parents only) + var customUndoActionsCount = customUndoActions?.Count ?? 0; + var undoActions = new IUndoAction[1 + customUndoActionsCount]; + for (int i = 0; i < customUndoActionsCount; i++) + undoActions[i] = customUndoActions[i]; + undoActions[undoActions.Length - 1] = selectAction; + + Undo.AddAction(new MultiUndoAction(undoActions)); + OnSelectionChanged(); + } + return; + } // Serialize actors - var actors = objects.ConvertAll(x => ((ActorNode)x).Actor); var data = Actor.ToBytes(actors.ToArray()); if (data == null) { @@ -394,22 +447,26 @@ namespace FlaxEditor.Modules var pasteAction = PasteActorsAction.Duplicate(data, Guid.Empty); if (pasteAction != null) { - OnPasteAction(pasteAction); + pasteAction.Do(out _, out var nodeParents); + + // Select spawned objects (parents only) + newSelection.AddRange(nodeParents); + var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo); + selectAction.Do(); + + // Build a single compound undo action that pastes the actors, pastes custom stuff (scene graph extension) and selects the created objects (parents only) + var customUndoActionsCount = customUndoActions?.Count ?? 0; + var undoActions = new IUndoAction[2 + customUndoActionsCount]; + undoActions[0] = pasteAction; + for (int i = 0; i < customUndoActionsCount; i++) + undoActions[i + 1] = customUndoActions[i]; + undoActions[undoActions.Length - 1] = selectAction; + + Undo.AddAction(new MultiUndoAction(undoActions)); + OnSelectionChanged(); } } - private void OnPasteAction(PasteActorsAction pasteAction) - { - pasteAction.Do(out _, out var nodeParents); - - // Select spawned objects - var selectAction = new SelectionChangeAction(Selection.ToArray(), nodeParents.Cast().ToArray(), OnSelectionUndo); - selectAction.Do(); - - Undo.AddAction(new MultiUndoAction(pasteAction, selectAction)); - OnSelectionChanged(); - } - /// /// Called when selection gets changed. Invokes the other events and updates editor. Call it when you manually modify selected objects collection. /// diff --git a/Source/Editor/SceneGraph/ActorChildNode.cs b/Source/Editor/SceneGraph/ActorChildNode.cs index 436984bc4..d514534ca 100644 --- a/Source/Editor/SceneGraph/ActorChildNode.cs +++ b/Source/Editor/SceneGraph/ActorChildNode.cs @@ -63,6 +63,9 @@ namespace FlaxEditor.SceneGraph /// public override bool CanCopyPaste => false; + /// + public override bool CanDuplicate => false; + /// public override bool CanDrag => false; diff --git a/Source/Editor/SceneGraph/ActorNode.cs b/Source/Editor/SceneGraph/ActorNode.cs index 5cc5f5337..07ff6a3b5 100644 --- a/Source/Editor/SceneGraph/ActorNode.cs +++ b/Source/Editor/SceneGraph/ActorNode.cs @@ -182,6 +182,9 @@ namespace FlaxEditor.SceneGraph /// public override bool CanCopyPaste => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0; + /// + public override bool CanDuplicate => (_actor.HideFlags & HideFlags.HideInHierarchy) == 0; + /// public override bool IsActive => _actor.IsActive; diff --git a/Source/Editor/SceneGraph/Actors/SceneNode.cs b/Source/Editor/SceneGraph/Actors/SceneNode.cs index 359d3d207..35c71bf02 100644 --- a/Source/Editor/SceneGraph/Actors/SceneNode.cs +++ b/Source/Editor/SceneGraph/Actors/SceneNode.cs @@ -57,6 +57,9 @@ namespace FlaxEditor.SceneGraph.Actors /// public override bool CanCopyPaste => false; + /// + public override bool CanDuplicate => false; + /// public override bool CanDelete => false; diff --git a/Source/Editor/SceneGraph/RootNode.cs b/Source/Editor/SceneGraph/RootNode.cs index fec8f68ad..10af88489 100644 --- a/Source/Editor/SceneGraph/RootNode.cs +++ b/Source/Editor/SceneGraph/RootNode.cs @@ -56,6 +56,9 @@ namespace FlaxEditor.SceneGraph /// public override bool CanCopyPaste => false; + /// + public override bool CanDuplicate => false; + /// public override bool CanDelete => false; diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs index c06c0d1c4..7ddf2d876 100644 --- a/Source/Editor/SceneGraph/SceneGraphNode.cs +++ b/Source/Editor/SceneGraph/SceneGraphNode.cs @@ -65,6 +65,11 @@ namespace FlaxEditor.SceneGraph /// public virtual bool CanCopyPaste => true; + /// + /// Gets a value indicating whether this instance can be duplicated by the user. + /// + public virtual bool CanDuplicate => true; + /// /// Gets a value indicating whether this node can be deleted by the user. /// @@ -359,6 +364,17 @@ namespace FlaxEditor.SceneGraph { } + /// + /// Duplicates this object. Valid only if returns true. + /// + /// The undo action that duplicated the object (already performed), null if skip it. + /// The duplicated object node. + public virtual SceneGraphNode Duplicate(out IUndoAction undoAction) + { + undoAction = null; + return null; + } + /// /// Releases the node and the child tree. Disposed all GUI parts and used resources. /// diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs index fca4909f0..af568cd3f 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs @@ -159,7 +159,7 @@ namespace FlaxEditor.Windows.Assets public void Duplicate() { // Peek things that can be copied (copy all actors) - var objects = Selection.Where(x => x.CanCopyPaste && x != Graph.Main).ToList().BuildAllNodes().Where(x => x.CanCopyPaste && x is ActorNode).ToList(); + var objects = Selection.Where(x => x.CanDuplicate && x != Graph.Main).ToList().BuildAllNodes().Where(x => x.CanDuplicate && x is ActorNode).ToList(); if (objects.Count == 0) return; From c3d9c52f1f1489de6d616ac2f8befaa8e47fd4bc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 16:04:20 +0100 Subject: [PATCH 280/419] Add support for spline point duplicate via Transform gizmo --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 68 ++++++++++++++++++- Source/Engine/Level/Actors/Spline.cpp | 6 ++ Source/Engine/Level/Actors/Spline.h | 7 ++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 7b7e98239..179f3267e 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.Json; using Object = FlaxEngine.Object; @@ -27,6 +28,8 @@ namespace FlaxEditor.SceneGraph.Actors public override bool CanBeSelectedDirectly => true; + public override bool CanDuplicate => true; + public override bool CanDelete => true; public override bool CanUseState => true; @@ -45,7 +48,7 @@ namespace FlaxEditor.SceneGraph.Actors } } - private struct Data + struct Data { public Guid Spline; public int Index; @@ -78,6 +81,69 @@ namespace FlaxEditor.SceneGraph.Actors actor.RemoveSplinePoint(Index); } + class DuplicateUndoAction : IUndoAction, ISceneEditAction + { + public Guid SplineId; + public int Index; + public float Time; + public Transform Value; + + public string ActionString => "Duplicate spline point"; + + public void Dispose() + { + } + + public void Do() + { + var spline = Object.Find(ref SplineId); + spline.InsertSplineLocalPoint(Index, Time, Value); + } + + public void Undo() + { + var spline = Object.Find(ref SplineId); + spline.RemoveSplinePoint(Index); + } + + public void MarkSceneEdited(SceneModule sceneModule) + { + var spline = Object.Find(ref SplineId); + sceneModule.MarkSceneEdited(spline.Scene); + } + } + + public override SceneGraphNode Duplicate(out IUndoAction undoAction) + { + var actor = (Spline)_node.Actor; + int newIndex; + float newTime; + if (Index == actor.SplinePointsCount - 1) + { + // Append to the end + newIndex = actor.SplinePointsCount; + newTime = actor.GetSplineTime(newIndex - 1) + 1.0f; + } + else + { + // Insert between this and next point + newIndex = Index + 1; + newTime = (actor.GetSplineTime(Index) + actor.GetSplineTime(Index + 1)) * 0.5f; + } + var action = new DuplicateUndoAction + { + SplineId = actor.ID, + Index = newIndex, + Time = newTime, + Value = actor.GetSplineLocalTransform(Index), + }; + actor.InsertSplineLocalPoint(newIndex, newTime, action.Value); + undoAction = action; + var splineNode = (SplineNode)SceneGraphFactory.FindNode(action.SplineId); + splineNode.OnUpdate(); + return splineNode.ActorChildNodes[newIndex]; + } + public override bool RayCastSelf(ref RayCastData ray, out float distance, out Vector3 normal) { var actor = (Spline)_node.Actor; diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index f522a8770..e16ff915e 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -169,6 +169,12 @@ float Spline::GetSplineLength() const return Math::Sqrt(sum); } +float Spline::GetSplineTime(int32 index) const +{ + CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), 0.0f) + return Curve[index].Time; +} + namespace { void FindTimeClosestToPoint(const Vector3& point, const Spline::Keyframe& start, const Spline::Keyframe& end, float& bestDistanceSquared, float& bestTime) diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index 7f8076eac..c712695d3 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -171,6 +171,13 @@ public: /// API_PROPERTY() float GetSplineLength() const; + /// + /// Gets the time of the spline keyframe. + /// + /// The curve keyframe index. Zero-based, smaller than GetSplinePointsCount(). + /// The spline time. + API_FUNCTION() float GetSplineTime(int32 index) const; + /// /// Calculates the closest point to the given location and returns the spline time at that point. /// From 1ae898918c31a65e9761f2d154b1afa159c300a8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 16:21:05 +0100 Subject: [PATCH 281/419] Add auto-select for spawned actors in the level --- Source/Editor/Modules/SceneEditingModule.cs | 13 +++++++++++-- Source/Editor/Tools/Foliage/FoliageTab.cs | 5 +---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Modules/SceneEditingModule.cs b/Source/Editor/Modules/SceneEditingModule.cs index 96aefe804..8c0914d58 100644 --- a/Source/Editor/Modules/SceneEditingModule.cs +++ b/Source/Editor/Modules/SceneEditingModule.cs @@ -200,7 +200,8 @@ namespace FlaxEditor.Modules /// /// The actor. /// The parent actor. Set null as default. - public void Spawn(Actor actor, Actor parent = null) + /// True if automatically select the spawned actor, otherwise false. + public void Spawn(Actor actor, Actor parent = null, bool autoSelect = true) { bool isPlayMode = Editor.StateMachine.IsPlayMode; @@ -225,7 +226,15 @@ namespace FlaxEditor.Modules actorNode.PostSpawn(); // Create undo action - var action = new DeleteActorsAction(new List(1) { actorNode }, true); + IUndoAction action = new DeleteActorsAction(new List(1) { actorNode }, true); + if (autoSelect) + { + var before = Selection.ToArray(); + Selection.Clear(); + Selection.Add(actorNode); + OnSelectionChanged(); + action = new MultiUndoAction(action, new SelectionChangeAction(before, Selection.ToArray(), OnSelectionUndo)); + } Undo.AddAction(action); // Mark scene as dirty diff --git a/Source/Editor/Tools/Foliage/FoliageTab.cs b/Source/Editor/Tools/Foliage/FoliageTab.cs index fe4120035..faf71bf1f 100644 --- a/Source/Editor/Tools/Foliage/FoliageTab.cs +++ b/Source/Editor/Tools/Foliage/FoliageTab.cs @@ -183,11 +183,8 @@ namespace FlaxEditor.Tools.Foliage actor.StaticFlags = StaticFlags.FullyStatic; actor.Name = "Foliage"; - // Spawn + // Spawn and select Editor.SceneEditing.Spawn(actor); - - // Select - Editor.SceneEditing.Select(actor); } private void OnSelectionChanged() From efea84248687b7eca9f3774e5151a452606a6d80 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 16:23:57 +0100 Subject: [PATCH 282/419] Fix warning --- Source/Engine/Scripting/BinaryModule.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 689b552f8..d5df8947e 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -606,6 +606,8 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly) for (int32 typeIndex = 0; typeIndex < Types.Count(); typeIndex++) { ScriptingType& type = Types[typeIndex]; + if (type.Type == ScriptingTypes::Interface) + continue; // TODO: generate C# class for interfaces in API ASSERT(type.ManagedClass == nullptr); // Cache class From e2cefc7407cb1f74c49748c4c3726eab3fb9765a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 16:58:01 +0100 Subject: [PATCH 283/419] Fix Quaternion comparison epsilon to reduce error rate --- Source/Engine/Core/Math/Quaternion.cs | 8 ++++---- Source/Engine/Core/Math/Quaternion.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Core/Math/Quaternion.cs b/Source/Engine/Core/Math/Quaternion.cs index 0e809f271..938bf48b1 100644 --- a/Source/Engine/Core/Math/Quaternion.cs +++ b/Source/Engine/Core/Math/Quaternion.cs @@ -635,7 +635,7 @@ namespace FlaxEngine public static float AngleBetween(Quaternion a, Quaternion b) { float num = Dot(a, b); - return num > 0.99999999f ? 0 : Mathf.Acos(Mathf.Min(Mathf.Abs(num), 1f)) * 2f * 57.29578f; + return num > 0.9999999f ? 0 : Mathf.Acos(Mathf.Min(Mathf.Abs(num), 1f)) * 2f * 57.29578f; } /// @@ -1478,7 +1478,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Quaternion left, Quaternion right) { - return Dot(ref left, ref right) > 0.99999999f; + return Dot(ref left, ref right) > 0.9999999f; } /// @@ -1493,7 +1493,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Quaternion left, Quaternion right) { - return Dot(ref left, ref right) <= 0.99999999f; + return Dot(ref left, ref right) <= 0.9999999f; } /// @@ -1605,7 +1605,7 @@ namespace FlaxEngine [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(ref Quaternion other) { - //return Dot(ref this, ref other) > 0.99999999f; + //return Dot(ref this, ref other) > 0.9999999f; return Mathf.NearEqual(other.X, X) && Mathf.NearEqual(other.Y, Y) && Mathf.NearEqual(other.Z, Z) && Mathf.NearEqual(other.W, W); } diff --git a/Source/Engine/Core/Math/Quaternion.h b/Source/Engine/Core/Math/Quaternion.h index 54ea49540..d3eeb4f32 100644 --- a/Source/Engine/Core/Math/Quaternion.h +++ b/Source/Engine/Core/Math/Quaternion.h @@ -391,7 +391,7 @@ public: /// true if the specified is equal to this instance; otherwise, false. FORCE_INLINE bool operator==(const Quaternion& other) const { - return Dot(*this, other) > 0.99999999f; + return Dot(*this, other) > 0.9999999f; } /// @@ -414,7 +414,7 @@ public: /// true if the specified structures are equal; otherwise, false. static bool NearEqual(const Quaternion& a, const Quaternion& b) { - return Dot(a, b) > 0.99999999f; + return Dot(a, b) > 0.9999999f; } /// @@ -474,7 +474,7 @@ public: static float AngleBetween(const Quaternion& a, const Quaternion& b) { const float dot = Dot(a, b); - return dot > 0.99999999f ? 0 : Math::Acos(Math::Min(Math::Abs(dot), 1.0f)) * 2.0f * 57.29578f; + return dot > 0.9999999f ? 0 : Math::Acos(Math::Min(Math::Abs(dot), 1.0f)) * 2.0f * 57.29578f; } // Adds two quaternions From 62f2ccb9426fa1e394d3cd1395e31cf172c1e1eb Mon Sep 17 00:00:00 2001 From: stefnotch Date: Mon, 1 Feb 2021 22:40:19 +0100 Subject: [PATCH 284/419] Fix conversion to degrees in Vector3.Angle --- Source/Engine/Core/Math/Vector3.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Math/Vector3.cs b/Source/Engine/Core/Math/Vector3.cs index db27cf8ed..a57fcb224 100644 --- a/Source/Engine/Core/Math/Vector3.cs +++ b/Source/Engine/Core/Math/Vector3.cs @@ -1221,7 +1221,7 @@ namespace FlaxEngine float dot = Mathf.Clamp(Dot(from.Normalized, to.Normalized), -1F, 1F); if (Mathf.Abs(dot) > (1F - Mathf.Epsilon)) return dot > 0F ? 0F : 180F; - return Mathf.Acos(dot) * Mathf.DegreesToRadians; + return Mathf.Acos(dot) * Mathf.RadiansToDegrees; } /// From 1bd639c86df48f5dc56124e2f59e2c12eee01eaa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 23:38:16 +0100 Subject: [PATCH 285/419] Fix including CharacterController.h in game code --- Source/Engine/Physics/Colliders/CharacterController.cpp | 5 +++-- Source/Engine/Physics/Colliders/CharacterController.h | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 73b653ee4..e0630573e 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -29,6 +29,7 @@ CharacterController::CharacterController(const SpawnParams& params) , _nonWalkableMode(CharacterController::NonWalkableModes::PreventClimbing) , _lastFlags(CollisionFlags::None) { + static_assert(sizeof(_filterData) == sizeof(PxFilterData), "Invalid filter data size."); } void CharacterController::SetRadius(const float value) @@ -112,7 +113,7 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis { const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds(); PxControllerFilters filters; - filters.mFilterData = &_filterData; + filters.mFilterData = (PxFilterData*)&_filterData; filters.mFilterCallback = Physics::GetCharacterQueryFilterCallback(); filters.mFilterFlags = PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC | PxQueryFlag::ePREFILTER; @@ -281,7 +282,7 @@ void CharacterController::UpdateLayerBits() Collider::UpdateLayerBits(); // Cache filter data - _filterData = _shape->getSimulationFilterData(); + *(PxFilterData*)&_filterData = _shape->getSimulationFilterData(); } void CharacterController::BeginPlay(SceneBeginData* data) diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index e79cea5e3..3959e8ad8 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -4,7 +4,6 @@ #include "Collider.h" #include "Engine/Physics/Actors/IPhysicsActor.h" -#include /// /// Physical objects that allows to easily do player movement constrained by collisions without having to deal with a rigidbody. @@ -68,7 +67,7 @@ private: bool _isUpdatingTransform; NonWalkableModes _nonWalkableMode; CollisionFlags _lastFlags; - PxFilterData _filterData; + uint32 _filterData[4]; public: From 2fe6129fcd268ac02ad68cbaf0f4827915bc4df5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 23:41:23 +0100 Subject: [PATCH 286/419] Simplify code --- Source/Engine/Renderer/MotionBlurPass.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index 065a9c53c..f10ef442b 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -337,15 +337,8 @@ void MotionBlurPass::Render(RenderContext& renderContext, GPUTexture*& input, GP RenderTargetPool::Release(vMaxBuffer4); // Downscale motion vectors texture down to tileSize/tileSize (with max velocity calculation NxN kernel) - rtDesc.Width = motionVectorsWidth / tileSize; - rtDesc.Height = motionVectorsHeight / tileSize; - - if (rtDesc.Width < 1) - rtDesc.Width = 1; - - if (rtDesc.Height < 1) - rtDesc.Height = 1; - + rtDesc.Width = Math::Max(motionVectorsWidth / tileSize, 1); + rtDesc.Height = Math::Max(motionVectorsHeight / tileSize, 1); auto vMaxBuffer = RenderTargetPool::Get(rtDesc); context->ResetRenderTarget(); context->SetRenderTarget(vMaxBuffer->View()); From 5d535fad6ab65c25b619a58a363033f2ebdf1c06 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Feb 2021 23:53:32 +0100 Subject: [PATCH 287/419] Move Mouse and Keyboard impl from headers to Input.cpp --- Source/Engine/Input/Input.cpp | 195 +++++++++++++++++++++++++++++++++ Source/Engine/Input/Keyboard.h | 80 ++------------ Source/Engine/Input/Mouse.h | 119 ++------------------ 3 files changed, 211 insertions(+), 183 deletions(-) diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 1dfa0e5fe..72cc6bcb9 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -98,6 +98,201 @@ Delegate Input::ActionTriggered; Array Input::ActionMappings; Array Input::AxisMappings; +void Mouse::OnMouseMoved(const Vector2& newPosition) +{ + _prevState.MousePosition = newPosition; + _state.MousePosition = newPosition; +} + +void Mouse::OnMouseDown(const Vector2& position, const MouseButton button, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseDown; + e.Target = target; + e.MouseData.Button = button; + e.MouseData.Position = position; +} + +void Mouse::OnMouseUp(const Vector2& position, const MouseButton button, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseUp; + e.Target = target; + e.MouseData.Button = button; + e.MouseData.Position = position; +} + +void Mouse::OnMouseDoubleClick(const Vector2& position, const MouseButton button, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseDoubleClick; + e.Target = target; + e.MouseData.Button = button; + e.MouseData.Position = position; +} + +void Mouse::OnMouseMove(const Vector2& position, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseMove; + e.Target = target; + e.MouseData.Position = position; +} + +void Mouse::OnMouseLeave(Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseLeave; + e.Target = target; +} + +void Mouse::OnMouseWheel(const Vector2& position, float delta, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::MouseWheel; + e.Target = target; + e.MouseWheelData.WheelDelta = delta; + e.MouseWheelData.Position = position; +} + +void Mouse::ResetState() +{ + InputDevice::ResetState(); + + _prevState.Clear(); + _state.Clear(); +} + +bool Mouse::Update(EventQueue& queue) +{ + // Move the current state to the previous + Platform::MemoryCopy(&_prevState, &_state, sizeof(State)); + + // Gather new events + if (UpdateState()) + return true; + + // Handle events + _state.MouseWheelDelta = 0; + for (int32 i = 0; i < _queue.Count(); i++) + { + const Event& e = _queue[i]; + switch (e.Type) + { + case EventType::MouseDown: + { + _state.MouseButtons[static_cast(e.MouseData.Button)] = true; + break; + } + case EventType::MouseUp: + { + _state.MouseButtons[static_cast(e.MouseData.Button)] = false; + break; + } + case EventType::MouseDoubleClick: + { + break; + } + case EventType::MouseWheel: + { + _state.MouseWheelDelta += e.MouseWheelData.WheelDelta; + break; + } + case EventType::MouseMove: + { + _state.MousePosition = e.MouseData.Position; + break; + } + case EventType::MouseLeave: + { + break; + } + } + } + + // Send events further + queue.Add(_queue); + _queue.Clear(); + return false; +} + +void Keyboard::OnCharInput(Char c, Window* target) +{ + // Skip control characters + if (c < 32) + return; + + Event& e = _queue.AddOne(); + e.Type = EventType::Char; + e.Target = target; + e.CharData.Char = c; +} + +void Keyboard::OnKeyUp(KeyboardKeys key, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::KeyUp; + e.Target = target; + e.KeyData.Key = key; +} + +void Keyboard::OnKeyDown(KeyboardKeys key, Window* target) +{ + Event& e = _queue.AddOne(); + e.Type = EventType::KeyDown; + e.Target = target; + e.KeyData.Key = key; +} + +void Keyboard::ResetState() +{ + InputDevice::ResetState(); + + _prevState.Clear(); + _state.Clear(); +} + +bool Keyboard::Update(EventQueue& queue) +{ + // Move the current state to the previous + Platform::MemoryCopy(&_prevState, &_state, sizeof(State)); + + // Gather new events + if (UpdateState()) + return true; + + // Handle events + _state.InputTextLength = 0; + for (int32 i = 0; i < _queue.Count(); i++) + { + const Event& e = _queue[i]; + switch (e.Type) + { + case EventType::Char: + { + if (_state.InputTextLength < ARRAY_COUNT(_state.InputText) - 1) + _state.InputText[_state.InputTextLength++] = e.CharData.Char; + break; + } + case EventType::KeyDown: + { + _state.Keys[static_cast(e.KeyData.Key)] = true; + break; + } + case EventType::KeyUp: + { + _state.Keys[static_cast(e.KeyData.Key)] = false; + break; + } + } + } + + // Send events further + queue.Add(_queue); + _queue.Clear(); + return false; +} + int32 Input::GetGamepadsCount() { return Gamepads.Count(); diff --git a/Source/Engine/Input/Keyboard.h b/Source/Engine/Input/Keyboard.h index 08f80f5c5..f182f9731 100644 --- a/Source/Engine/Input/Keyboard.h +++ b/Source/Engine/Input/Keyboard.h @@ -94,98 +94,32 @@ public: return !_state.Keys[static_cast(key)] && _prevState.Keys[static_cast(key)]; } +public: + /// /// Called when keyboard enters input character. /// /// The Unicode character entered by the user. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnCharInput(const Char c, Window* target = nullptr) - { - // Skip control characters - if (c < 32) - return; - - Event& e = _queue.AddOne(); - e.Type = EventType::Char; - e.Target = target; - e.CharData.Char = c; - } + void OnCharInput(Char c, Window* target = nullptr); /// /// Called when key goes up. /// /// The keyboard key. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnKeyUp(const KeyboardKeys key, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::KeyUp; - e.Target = target; - e.KeyData.Key = key; - } + void OnKeyUp(KeyboardKeys key, Window* target = nullptr); /// /// Called when key goes down. /// /// The keyboard key. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnKeyDown(const KeyboardKeys key, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::KeyDown; - e.Target = target; - e.KeyData.Key = key; - } + void OnKeyDown(KeyboardKeys key, Window* target = nullptr); public: // [InputDevice] - void ResetState() override - { - InputDevice::ResetState(); - - _prevState.Clear(); - _state.Clear(); - } - - bool Update(EventQueue& queue) final override - { - // Move the current state to the previous - Platform::MemoryCopy(&_prevState, &_state, sizeof(State)); - - // Gather new events - if (UpdateState()) - return true; - - _state.InputTextLength = 0; - // Handle events - for (int32 i = 0; i < _queue.Count(); i++) - { - const Event& e = _queue[i]; - switch (e.Type) - { - case EventType::Char: - { - if (_state.InputTextLength < ARRAY_COUNT(_state.InputText) - 1) - _state.InputText[_state.InputTextLength++] = e.CharData.Char; - break; - } - case EventType::KeyDown: - { - _state.Keys[static_cast(e.KeyData.Key)] = true; - break; - } - case EventType::KeyUp: - { - _state.Keys[static_cast(e.KeyData.Key)] = false; - break; - } - } - } - - // Send events further - queue.Add(_queue); - _queue.Clear(); - return false; - } + void ResetState() override;; + bool Update(EventQueue& queue) final override; }; diff --git a/Source/Engine/Input/Mouse.h b/Source/Engine/Input/Mouse.h index 94875ea3a..d58350956 100644 --- a/Source/Engine/Input/Mouse.h +++ b/Source/Engine/Input/Mouse.h @@ -127,11 +127,7 @@ public: /// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programmatically. /// /// The new mouse position. - void OnMouseMoved(const Vector2& newPosition) - { - _prevState.MousePosition = newPosition; - _state.MousePosition = newPosition; - } + void OnMouseMoved(const Vector2& newPosition); /// /// Called when mouse button goes down. @@ -139,14 +135,7 @@ public: /// The mouse position. /// The button. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseDown(const Vector2& position, const MouseButton button, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseDown; - e.Target = target; - e.MouseData.Button = button; - e.MouseData.Position = position; - } + void OnMouseDown(const Vector2& position, const MouseButton button, Window* target = nullptr); /// /// Called when mouse button goes up. @@ -154,14 +143,7 @@ public: /// The mouse position. /// The button. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseUp(const Vector2& position, const MouseButton button, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseUp; - e.Target = target; - e.MouseData.Button = button; - e.MouseData.Position = position; - } + void OnMouseUp(const Vector2& position, const MouseButton button, Window* target = nullptr); /// /// Called when mouse double clicks. @@ -169,38 +151,20 @@ public: /// The mouse position. /// The button. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseDoubleClick(const Vector2& position, const MouseButton button, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseDoubleClick; - e.Target = target; - e.MouseData.Button = button; - e.MouseData.Position = position; - } + void OnMouseDoubleClick(const Vector2& position, const MouseButton button, Window* target = nullptr); /// /// Called when mouse moves. /// /// The mouse position. /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseMove(const Vector2& position, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseMove; - e.Target = target; - e.MouseData.Position = position; - } + void OnMouseMove(const Vector2& position, Window* target = nullptr); /// /// Called when mouse leaves the input source area. /// /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseLeave(Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseLeave; - e.Target = target; - } + void OnMouseLeave(Window* target = nullptr); /// /// Called when mouse wheel moves. @@ -208,76 +172,11 @@ public: /// The mouse position. /// The normalized delta (range [-1;1]). /// The target window to receive this event, otherwise input system will pick the window automatically. - void OnMouseWheel(const Vector2& position, const float delta, Window* target = nullptr) - { - Event& e = _queue.AddOne(); - e.Type = EventType::MouseWheel; - e.Target = target; - e.MouseWheelData.WheelDelta = delta; - e.MouseWheelData.Position = position; - } + void OnMouseWheel(const Vector2& position, float delta, Window* target = nullptr); public: // [InputDevice] - void ResetState() override - { - InputDevice::ResetState(); - - _prevState.Clear(); - _state.Clear(); - } - - bool Update(EventQueue& queue) final override - { - // Move the current state to the previous - Platform::MemoryCopy(&_prevState, &_state, sizeof(State)); - - // Gather new events - if (UpdateState()) - return true; - - // Handle events - _state.MouseWheelDelta = 0; - for (int32 i = 0; i < _queue.Count(); i++) - { - const Event& e = _queue[i]; - switch (e.Type) - { - case EventType::MouseDown: - { - _state.MouseButtons[static_cast(e.MouseData.Button)] = true; - break; - } - case EventType::MouseUp: - { - _state.MouseButtons[static_cast(e.MouseData.Button)] = false; - break; - } - case EventType::MouseDoubleClick: - { - break; - } - case EventType::MouseWheel: - { - _state.MouseWheelDelta += e.MouseWheelData.WheelDelta; - break; - } - case EventType::MouseMove: - { - _state.MousePosition = e.MouseData.Position; - break; - } - case EventType::MouseLeave: - { - break; - } - } - } - - // Send events further - queue.Add(_queue); - _queue.Clear(); - return false; - } + void ResetState() override; + bool Update(EventQueue& queue) final override; }; From c00b32364ab97a6c8434d7fe02d17c0691e8eb7c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 2 Feb 2021 12:51:36 +0100 Subject: [PATCH 288/419] Add `SceneGraphNode.OnContextMenu` for customizations --- Source/Editor/SceneGraph/SceneGraphNode.cs | 7 +++++++ .../Windows/SceneTreeWindow.ContextMenu.cs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs index 7ddf2d876..cc47c20f2 100644 --- a/Source/Editor/SceneGraph/SceneGraphNode.cs +++ b/Source/Editor/SceneGraph/SceneGraphNode.cs @@ -317,6 +317,13 @@ namespace FlaxEditor.SceneGraph { } + /// + /// Called when scene tree window wants to show the context menu. Allows to add custom options. + /// + public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu) + { + } + /// /// The scene graph node state container. Used for Editor undo actions (eg. restoring deleted node). /// diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index b6756b438..f407017df 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using System.Collections.Generic; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.SceneGraph; using FlaxEngine; @@ -113,6 +114,24 @@ namespace FlaxEditor.Windows // Custom options + bool showCustomNodeOptions = Editor.SceneEditing.Selection.Count == 1; + if (!showCustomNodeOptions && Editor.SceneEditing.Selection.Count != 0) + { + showCustomNodeOptions = true; + for (int i = 1; i < Editor.SceneEditing.Selection.Count; i++) + { + if (Editor.SceneEditing.Selection[0].GetType() != Editor.SceneEditing.Selection[i].GetType()) + { + showCustomNodeOptions = false; + break; + } + } + } + if (showCustomNodeOptions) + { + Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu); + } + ContextMenuShow?.Invoke(contextMenu); return contextMenu; From d2dba5680bf3bd8e83aa62c74da8e5f36cee3ee6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 2 Feb 2021 12:52:21 +0100 Subject: [PATCH 289/419] Fix calling OnParentChanged in Actor load --- Source/Engine/Level/Actor.cpp | 22 +++++++++++++++++----- Source/Engine/Level/Actor.h | 4 +--- Source/Engine/Level/Actors/BoneSocket.cpp | 3 +++ Source/Engine/Level/Actors/BoxBrush.cpp | 3 +++ Source/Engine/Physics/Joints/Joint.cpp | 3 +++ Source/Engine/UI/UIControl.cpp | 3 +++ 6 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 30ca06daf..ce3bcdb65 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -948,11 +948,19 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) const auto parent = Scripting::FindObject(parentId); if (_parent != parent) { - if (_parent) - _parent->Children.RemoveKeepOrder(this); - _parent = parent; - if (_parent) - _parent->Children.Add(this); + if (IsDuringPlay()) + { + SetParent(parent, false, false); + } + else + { + if (_parent) + _parent->Children.RemoveKeepOrder(this); + _parent = parent; + if (_parent) + _parent->Children.Add(this); + OnParentChanged(); + } } else if (!parent && parentId.IsValid()) { @@ -1044,6 +1052,10 @@ void Actor::OnDisable() } } +void Actor::OnParentChanged() +{ +} + void Actor::OnTransformChanged() { ASSERT_LOW_LAYER(!_localTransform.IsNanOrInfinity()); diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index da08bc498..6416fd33a 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -918,9 +918,7 @@ public: /// /// Called when actor parent gets changed. /// - virtual void OnParentChanged() - { - } + virtual void OnParentChanged(); /// /// Called when actor transform gets changed. diff --git a/Source/Engine/Level/Actors/BoneSocket.cpp b/Source/Engine/Level/Actors/BoneSocket.cpp index 7673781ff..028b1c507 100644 --- a/Source/Engine/Level/Actors/BoneSocket.cpp +++ b/Source/Engine/Level/Actors/BoneSocket.cpp @@ -104,6 +104,9 @@ void BoneSocket::OnParentChanged() { // Base Actor::OnParentChanged(); + + if (!IsDuringPlay()) + return; _index = -1; UpdateTransformation(); diff --git a/Source/Engine/Level/Actors/BoxBrush.cpp b/Source/Engine/Level/Actors/BoxBrush.cpp index ca38c103d..f02dd5bd3 100644 --- a/Source/Engine/Level/Actors/BoxBrush.cpp +++ b/Source/Engine/Level/Actors/BoxBrush.cpp @@ -272,6 +272,9 @@ void BoxBrush::OnParentChanged() { // Base Actor::OnParentChanged(); + + if (!IsDuringPlay()) + return; // Fire event OnBrushModified(); diff --git a/Source/Engine/Physics/Joints/Joint.cpp b/Source/Engine/Physics/Joints/Joint.cpp index cda249d71..56c5c1b32 100644 --- a/Source/Engine/Physics/Joints/Joint.cpp +++ b/Source/Engine/Physics/Joints/Joint.cpp @@ -295,6 +295,9 @@ void Joint::OnParentChanged() // Base Actor::OnParentChanged(); + if (!IsDuringPlay()) + return; + // Check reparenting Joint case const auto parent = dynamic_cast(GetParent()); if (parent == nullptr) diff --git a/Source/Engine/UI/UIControl.cpp b/Source/Engine/UI/UIControl.cpp index 131c04298..33fe8a93b 100644 --- a/Source/Engine/UI/UIControl.cpp +++ b/Source/Engine/UI/UIControl.cpp @@ -156,6 +156,9 @@ void UIControl::OnParentChanged() // Base Actor::OnParentChanged(); + if (!IsDuringPlay()) + return; + UICONTROL_INVOKE(ParentChanged); } From 85a6f485f12804bac866fb15103feb754603db10 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 2 Feb 2021 12:52:37 +0100 Subject: [PATCH 290/419] Fixes and tweaks --- Source/Engine/Level/Actors/AnimatedModel.cpp | 16 ++++++++-------- Source/Engine/Level/Actors/AnimatedModel.h | 3 ++- Source/Engine/Level/Actors/ModelInstanceActor.h | 15 ++++++++------- Source/Engine/Level/Actors/StaticModel.h | 2 +- Source/Engine/Renderer/DrawCall.h | 2 +- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 3d6450716..85ee6d333 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -327,14 +327,6 @@ void AnimatedModel::SyncParameters() } } -void AnimatedModel::UpdateBounds() -{ - UpdateLocalBounds(); - - BoundingBox::Transform(_boxLocal, _world, _box); - BoundingSphere::FromBox(_box, _sphere); -} - void AnimatedModel::BeginPlay(SceneBeginData* data) { if (SkinnedModel && SkinnedModel->IsLoaded()) @@ -401,6 +393,14 @@ void AnimatedModel::UpdateLocalBounds() _boxLocal = box; } +void AnimatedModel::UpdateBounds() +{ + UpdateLocalBounds(); + + BoundingBox::Transform(_boxLocal, _world, _box); + BoundingSphere::FromBox(_box, _sphere); +} + void AnimatedModel::OnAnimUpdate() { UpdateBounds(); diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 641247273..d5d9b4424 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -290,6 +290,8 @@ private: /// void UpdateLocalBounds(); + void UpdateBounds(); + /// /// Called after animation graph update. /// @@ -317,7 +319,6 @@ public: void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; bool IntersectsEntry(int32 entryIndex, const Ray& ray, float& distance, Vector3& normal) override; bool IntersectsEntry(const Ray& ray, float& distance, Vector3& normal, int32& entryIndex) override; - void UpdateBounds() final override; protected: diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.h b/Source/Engine/Level/Actors/ModelInstanceActor.h index c431429fd..7b2a08fd9 100644 --- a/Source/Engine/Level/Actors/ModelInstanceActor.h +++ b/Source/Engine/Level/Actors/ModelInstanceActor.h @@ -19,11 +19,6 @@ public: /// ModelInstanceEntries Entries; - /// - /// Updates the bounds of the actor. - /// - virtual void UpdateBounds() = 0; - /// /// Gets the model entries collection. Each entry contains data how to render meshes using this entry (transformation, material, shadows casting, etc.). /// @@ -63,7 +58,10 @@ public: /// When the method completes and returns true, contains the distance of the intersection (if any valid). /// When the method completes, contains the intersection surface normal vector (if any valid). /// True if the actor is intersected by the ray, otherwise false. - API_FUNCTION() virtual bool IntersectsEntry(int32 entryIndex, API_PARAM(Ref) const Ray& ray, API_PARAM(Out) float& distance, API_PARAM(Out) Vector3& normal) = 0; + API_FUNCTION() virtual bool IntersectsEntry(int32 entryIndex, API_PARAM(Ref) const Ray& ray, API_PARAM(Out) float& distance, API_PARAM(Out) Vector3& normal) + { + return false; + } /// /// Determines if there is an intersection between the model actor mesh entry and a ray. @@ -76,7 +74,10 @@ public: /// When the method completes, contains the intersection surface normal vector (if any valid). /// When the method completes, contains the intersection entry index (if any valid). /// True if the actor is intersected by the ray, otherwise false. - API_FUNCTION() virtual bool IntersectsEntry(API_PARAM(Ref) const Ray& ray, API_PARAM(Out) float& distance, API_PARAM(Out) Vector3& normal, API_PARAM(Out) int32& entryIndex) = 0; + API_FUNCTION() virtual bool IntersectsEntry(API_PARAM(Ref) const Ray& ray, API_PARAM(Out) float& distance, API_PARAM(Out) Vector3& normal, API_PARAM(Out) int32& entryIndex) + { + return false; + } protected: diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h index 5ca6e05e4..a255ba4ab 100644 --- a/Source/Engine/Level/Actors/StaticModel.h +++ b/Source/Engine/Level/Actors/StaticModel.h @@ -181,6 +181,7 @@ private: void OnModelChanged(); void OnModelLoaded(); + void UpdateBounds(); public: @@ -193,7 +194,6 @@ public: void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; bool IntersectsEntry(int32 entryIndex, const Ray& ray, float& distance, Vector3& normal) override; bool IntersectsEntry(const Ray& ray, float& distance, Vector3& normal, int32& entryIndex) override; - void UpdateBounds() override; protected: diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index 7c4f4fe6d..cc35b2627 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -333,6 +333,6 @@ struct TIsPODType #define GEOMETRY_DRAW_STATE_EVENT_END(drawState, worldMatrix) \ if (drawState.PrevFrame != frame) \ { \ - drawState.PrevWorld = _world; \ + drawState.PrevWorld = worldMatrix; \ drawState.PrevFrame = frame; \ } From 00fab5cfd6ef06b66270163f8b44025ae7c51a8d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 2 Feb 2021 12:53:18 +0100 Subject: [PATCH 291/419] Add event to spline for updating custom data --- Source/Engine/Level/Actors/Spline.cpp | 2 ++ Source/Engine/Level/Actors/Spline.h | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index e16ff915e..08a06d433 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -416,6 +416,8 @@ void Spline::UpdateSpline() last.TangentIn = first.TangentIn; last.TangentOut = first.TangentOut; } + + SplineUpdated(); } void Spline::GetKeyframes(MonoArray* data) diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index c712695d3..7d7e8dcb1 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -348,6 +348,11 @@ public: public: + /// + /// Called when spline gets updated (eg. after curve modification). + /// + API_EVENT() Action SplineUpdated; + /// /// Updates the spline after it was modified. Recreates the collision and/or any cached state that depends on the spline type. /// From 81cc8cf69c36a611fa1d844379e60c92de4b4315 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 2 Feb 2021 15:44:14 +0100 Subject: [PATCH 292/419] Add Matrix3x4 --- Source/Engine/Core/Math/Matrix3x4.h | 55 +++++++++++++++++ .../Graphics/Models/SkinnedMeshDrawData.cpp | 61 +++---------------- 2 files changed, 62 insertions(+), 54 deletions(-) create mode 100644 Source/Engine/Core/Math/Matrix3x4.h diff --git a/Source/Engine/Core/Math/Matrix3x4.h b/Source/Engine/Core/Math/Matrix3x4.h new file mode 100644 index 000000000..49f6cc1ff --- /dev/null +++ b/Source/Engine/Core/Math/Matrix3x4.h @@ -0,0 +1,55 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Matrix.h" + +/// +/// Helper matrix for optimized float3x4 package of transformation matrices. +/// +struct FLAXENGINE_API Matrix3x4 +{ + float M[3][4]; + + void SetMatrix(const Matrix& m) + { + const float* src = m.Raw; + float* dst = &M[0][0]; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + dst[8] = src[8]; + dst[9] = src[9]; + dst[10] = src[10]; + dst[11] = src[11]; + } + + void SetMatrixTranspose(const Matrix& m) + { + const float* src = m.Raw; + float* dst = &M[0][0]; + dst[0] = src[0]; + dst[1] = src[4]; + dst[2] = src[8]; + dst[3] = src[12]; + dst[4] = src[1]; + dst[5] = src[5]; + dst[6] = src[9]; + dst[7] = src[13]; + dst[8] = src[2]; + dst[9] = src[6]; + dst[10] = src[10]; + dst[11] = src[14]; + } +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; diff --git a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp index 2094be3b2..25c29b11a 100644 --- a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp @@ -4,53 +4,7 @@ #include "Engine/Graphics/GPUDevice.h" #include "Engine/Animations/Config.h" #include "Engine/Core/Math/Matrix.h" - -struct SkinMatrix3x4 -{ - float M[3][4]; - - FORCE_INLINE void SetMatrix(const Matrix& mat) - { - const float* src = mat.Raw; - float* dest = &(M[0][0]); - - dest[0] = src[0]; // [0][0] - dest[1] = src[1]; // [0][1] - dest[2] = src[2]; // [0][2] - dest[3] = src[3]; // [0][3] - - dest[4] = src[4]; // [1][0] - dest[5] = src[5]; // [1][1] - dest[6] = src[6]; // [1][2] - dest[7] = src[7]; // [1][3] - - dest[8] = src[8]; // [2][0] - dest[9] = src[9]; // [2][1] - dest[10] = src[10]; // [2][2] - dest[11] = src[11]; // [2][3] - } - - FORCE_INLINE void SetMatrixTranspose(const Matrix& mat) - { - const float* src = mat.Raw; - float* dest = &(M[0][0]); - - dest[0] = src[0]; // [0][0] - dest[1] = src[4]; // [1][0] - dest[2] = src[8]; // [2][0] - dest[3] = src[12]; // [3][0] - - dest[4] = src[1]; // [0][1] - dest[5] = src[5]; // [1][1] - dest[6] = src[9]; // [2][1] - dest[7] = src[13]; // [3][1] - - dest[8] = src[2]; // [0][2] - dest[9] = src[6]; // [1][2] - dest[10] = src[10]; // [2][2] - dest[11] = src[14]; // [3][2] - } -}; +#include "Engine/Core/Math/Matrix3x4.h" SkinnedMeshDrawData::~SkinnedMeshDrawData() { @@ -99,7 +53,6 @@ void SkinnedMeshDrawData::SetData(const Matrix* bones, bool dropHistory) LOG(Fatal, "Failed to initialize the skinned mesh bones buffer"); } } - Swap(PrevBoneMatrices, BoneMatrices); } else @@ -109,15 +62,15 @@ void SkinnedMeshDrawData::SetData(const Matrix* bones, bool dropHistory) // Copy bones to the buffer const int32 count = BonesCount; - const int32 PreFetchStride = 2; + const int32 preFetchStride = 2; const Matrix* input = bones; - const auto output = (SkinMatrix3x4*)Data.Get(); - ASSERT(Data.Count() == count * sizeof(SkinMatrix3x4)); + const auto output = (Matrix3x4*)Data.Get(); + ASSERT(Data.Count() == count * sizeof(Matrix3x4)); for (int32 i = 0; i < count; i++) { - SkinMatrix3x4* bone = output + i; - Platform::Prefetch(bone + PreFetchStride); - Platform::Prefetch((byte*)(bone + PreFetchStride) + PLATFORM_CACHE_LINE_SIZE); + Matrix3x4* bone = output + i; + Platform::Prefetch(bone + preFetchStride); + Platform::Prefetch((byte*)(bone + preFetchStride) + PLATFORM_CACHE_LINE_SIZE); bone->SetMatrixTranspose(input[i]); } From fb74aef541c18a0a55deee1b761ed0506d3cba31 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 2 Feb 2021 23:14:21 +0100 Subject: [PATCH 293/419] Add check for C++ bindings generator for property getter and setter method value types to match --- .../Bindings/BindingsGenerator.Parsing.cs | 22 +++++++++++++++++++ Source/Tools/Flax.Build/Bindings/TypeInfo.cs | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index b39f2f066..42c0e73b2 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -691,6 +691,28 @@ namespace Flax.Build.Bindings else propertyInfo.Setter = functionInfo; + if (propertyInfo.Getter != null && propertyInfo.Setter != null) + { + // Check if getter and setter types are matching (const and ref specifiers are skipped) + var getterType = propertyInfo.Getter.ReturnType; + var setterType = propertyInfo.Setter.Parameters[0].Type; + if (!string.Equals(getterType.Type, setterType.Type) || + getterType.IsPtr != setterType.IsPtr || + getterType.IsArray != setterType.IsArray || + getterType.IsBitField != setterType.IsBitField || + getterType.ArraySize != setterType.ArraySize || + getterType.BitSize != setterType.BitSize || + !TypeInfo.Equals(getterType.GenericArgs, setterType.GenericArgs)) + { + // Skip compatible types + if (getterType.Type == "String" && setterType.Type == "StringView") + return propertyInfo; + if (getterType.Type == "Array" && setterType.Type == "Span" && getterType.GenericArgs?.Count == 1 && setterType.GenericArgs?.Count == 1 && getterType.GenericArgs[0].Equals(setterType.GenericArgs[0])) + return propertyInfo; + throw new Exception($"Property {propertyName} in class {classInfo.Name} (line {context.Tokenizer.CurrentLine}) has mismatching getter return type ({getterType}) and setter parameter type ({setterType}). Both getter and setter methods must use the same value type used for property."); + } + } + return propertyInfo; } diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index 8af061200..962aa9523 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -83,7 +83,7 @@ namespace Flax.Build.Bindings return sb.ToString(); } - private static bool Equals(List a, List b) + public static bool Equals(List a, List b) { if (a == null && b == null) return true; From 28f53339e77acaaed0c1a61c5c6a114a034fb2b2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 2 Feb 2021 23:33:42 +0100 Subject: [PATCH 294/419] Fix crash when loading string property in json that is not a string Fixes #178 --- Source/ThirdParty/rapidjson/document.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/ThirdParty/rapidjson/document.h b/Source/ThirdParty/rapidjson/document.h index d9472ec1e..2691a6386 100644 --- a/Source/ThirdParty/rapidjson/document.h +++ b/Source/ThirdParty/rapidjson/document.h @@ -1679,7 +1679,18 @@ public: //@{ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } - ::String GetText() const { RAPIDJSON_ASSERT(IsString()); return ::String(GetString(), GetStringLength()); } + ::String GetText() const + { + ::String result; + if (IsString()) + { + if (data_.f.flags & kInlineStrFlag) + result.Set(data_.ss.str, data_.ss.GetLength()); + else + result.Set(GetStringPointer(), data_.s.length); + } + return result; + } //! Get the length of string. /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). From a560b19cbceb5a015099d52662b8970885ddc5da Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 09:33:48 +0100 Subject: [PATCH 295/419] Refactor draw calls and instancing logic to be more modular --- Source/Engine/Content/Assets/Material.cpp | 4 +- Source/Engine/Content/Assets/Material.h | 2 +- .../Content/Assets/MaterialInstance.cpp | 4 +- .../Engine/Content/Assets/MaterialInstance.h | 2 +- .../Materials/DeferredMaterialShader.cpp | 28 ++--- .../Materials/DeferredMaterialShader.h | 2 +- .../Materials/ForwardMaterialShader.cpp | 18 +-- .../Materials/ForwardMaterialShader.h | 2 +- Source/Engine/Graphics/Materials/IMaterial.h | 32 ++---- .../Materials/ParticleMaterialShader.cpp | 54 ++++----- .../Materials/TerrainMaterialShader.cpp | 26 ++--- Source/Engine/Graphics/Models/Mesh.cpp | 24 ++-- Source/Engine/Graphics/Models/SkinnedMesh.cpp | 12 +- Source/Engine/Level/Actors/Skybox.cpp | 2 +- Source/Engine/Particles/ParticleManager.cpp | 11 +- Source/Engine/Renderer/DrawCall.h | 108 ++++++------------ .../Renderer/Editor/LightmapUVsDensity.cpp | 2 +- Source/Engine/Renderer/GBufferPass.cpp | 8 +- Source/Engine/Renderer/RenderList.cpp | 74 ++++++++---- Source/Engine/Renderer/RenderList.h | 33 ++++-- Source/Engine/Renderer/Renderer.cpp | 3 - Source/Engine/Terrain/TerrainChunk.cpp | 60 +++++----- Source/Engine/UI/TextRender.cpp | 12 +- 23 files changed, 255 insertions(+), 268 deletions(-) diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index b40e1e9c0..571e46243 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -61,9 +61,9 @@ bool Material::CanUseLightmap() const return _materialShader && _materialShader->CanUseLightmap(); } -bool Material::CanUseInstancing() const +bool Material::CanUseInstancing(InstancingHandler& handler) const { - return _materialShader && _materialShader->CanUseInstancing(); + return _materialShader && _materialShader->CanUseInstancing(handler); } void Material::Bind(BindParameters& params) diff --git a/Source/Engine/Content/Assets/Material.h b/Source/Engine/Content/Assets/Material.h index bdaba7abb..0a9ad1dbc 100644 --- a/Source/Engine/Content/Assets/Material.h +++ b/Source/Engine/Content/Assets/Material.h @@ -48,7 +48,7 @@ public: bool IsReady() const override; DrawPass GetDrawModes() const override; bool CanUseLightmap() const override; - bool CanUseInstancing() const override; + bool CanUseInstancing(InstancingHandler& handler) const override; void Bind(BindParameters& params) override; // [ShaderAssetBase] diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index ace2fcee9..0678f9992 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -167,9 +167,9 @@ bool MaterialInstance::CanUseLightmap() const return _baseMaterial && _baseMaterial->CanUseLightmap(); } -bool MaterialInstance::CanUseInstancing() const +bool MaterialInstance::CanUseInstancing(InstancingHandler& handler) const { - return _baseMaterial && _baseMaterial->CanUseInstancing(); + return _baseMaterial && _baseMaterial->CanUseInstancing(handler); } void MaterialInstance::Bind(BindParameters& params) diff --git a/Source/Engine/Content/Assets/MaterialInstance.h b/Source/Engine/Content/Assets/MaterialInstance.h index 9fe92e39c..3cf74db03 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.h +++ b/Source/Engine/Content/Assets/MaterialInstance.h @@ -64,7 +64,7 @@ public: bool IsReady() const override; DrawPass GetDrawModes() const override; bool CanUseLightmap() const override; - bool CanUseInstancing() const override; + bool CanUseInstancing(InstancingHandler& handler) const override; void Bind(BindParameters& params) override; protected: diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index 049484459..5a460079d 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -5,6 +5,7 @@ #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Renderer/DrawCall.h" +#include "Engine/Renderer/RenderList.h" #include "Engine/Level/Scene/Lightmap.h" #include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/Shaders/GPUConstantBuffer.h" @@ -48,8 +49,9 @@ bool DeferredMaterialShader::CanUseLightmap() const return true; } -bool DeferredMaterialShader::CanUseInstancing() const +bool DeferredMaterialShader::CanUseInstancing(InstancingHandler& handler) const { + handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, SurfaceDrawCallHandler::WriteDrawCall, }; return true; } @@ -79,7 +81,7 @@ void DeferredMaterialShader::Bind(BindParameters& params) Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); Matrix::Transpose(view.View, materialData->ViewMatrix); - Matrix::Transpose(drawCall.PrevWorld, materialData->PrevWorldMatrix); + Matrix::Transpose(drawCall.Surface.PrevWorld, materialData->PrevWorldMatrix); Matrix::Transpose(view.PrevViewProjection, materialData->PrevViewProjectionMatrix); materialData->ViewPos = view.Position; @@ -100,40 +102,40 @@ void DeferredMaterialShader::Bind(BindParameters& params) materialData->WorldInvScale = worldInvScale; materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; - materialData->LODDitherFactor = drawCall.LODDitherFactor; + materialData->LODDitherFactor = drawCall.Surface.LODDitherFactor; materialData->PerInstanceRandom = drawCall.PerInstanceRandom; materialData->TemporalAAJitter = view.TemporalAAJitter; - materialData->GeometrySize = drawCall.GeometrySize; + materialData->GeometrySize = drawCall.Surface.GeometrySize; } const bool useLightmap = view.Flags & ViewFlags::GI #if USE_EDITOR && EnableLightmapsUsage #endif - && drawCall.Lightmap != nullptr; + && drawCall.Surface.Lightmap != nullptr; if (useLightmap) { // Bind lightmap textures GPUTexture *lightmap0, *lightmap1, *lightmap2; - drawCall.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); + drawCall.Surface.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); context->BindSR(0, lightmap0); context->BindSR(1, lightmap1); context->BindSR(2, lightmap2); // Set lightmap data - materialData->LightmapArea = drawCall.LightmapUVsArea; + materialData->LightmapArea = drawCall.Surface.LightmapUVsArea; } // Check if is using mesh skinning - const bool useSkinning = drawCall.Skinning != nullptr; + const bool useSkinning = drawCall.Surface.Skinning != nullptr; bool perBoneMotionBlur = false; if (useSkinning) { // Bind skinning buffer - ASSERT(drawCall.Skinning->IsReady()); - context->BindSR(0, drawCall.Skinning->BoneMatrices->View()); - if (drawCall.Skinning->PrevBoneMatrices && drawCall.Skinning->PrevBoneMatrices->IsAllocated()) + ASSERT(drawCall.Surface.Skinning->IsReady()); + context->BindSR(0, drawCall.Surface.Skinning->BoneMatrices->View()); + if (drawCall.Surface.Skinning->PrevBoneMatrices && drawCall.Surface.Skinning->PrevBoneMatrices->IsAllocated()) { - context->BindSR(1, drawCall.Skinning->PrevBoneMatrices->View()); + context->BindSR(1, drawCall.Surface.Skinning->PrevBoneMatrices->View()); perBoneMotionBlur = true; } } @@ -152,7 +154,7 @@ void DeferredMaterialShader::Bind(BindParameters& params) if (IsRunningRadiancePass) cullMode = CullMode::TwoSided; #endif - if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale()) + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) { // Invert culling when scale is negative if (cullMode == CullMode::Normal) diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.h b/Source/Engine/Graphics/Materials/DeferredMaterialShader.h index f8df968c9..537547c6a 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.h +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.h @@ -66,7 +66,7 @@ public: // [MaterialShader] DrawPass GetDrawModes() const override; bool CanUseLightmap() const override; - bool CanUseInstancing() const override; + bool CanUseInstancing(InstancingHandler& handler) const override; void Bind(BindParameters& params) override; void Unload() override; diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index 863af34e1..39e39a7d4 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -10,6 +10,7 @@ #include "Engine/Renderer/DepthOfFieldPass.h" #include "Engine/Renderer/DrawCall.h" #include "Engine/Renderer/ShadowsPass.h" +#include "Engine/Renderer/RenderList.h" #if USE_EDITOR #include "Engine/Renderer/Lightmaps.h" #endif @@ -54,8 +55,9 @@ DrawPass ForwardMaterialShader::GetDrawModes() const return _drawModes; } -bool ForwardMaterialShader::CanUseInstancing() const +bool ForwardMaterialShader::CanUseInstancing(InstancingHandler& handler) const { + handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, SurfaceDrawCallHandler::WriteDrawCall, }; return true; } @@ -82,12 +84,12 @@ void ForwardMaterialShader::Bind(BindParameters& params) MaterialParams::Bind(params.ParamsLink, bindMeta); // Check if is using mesh skinning - const bool useSkinning = drawCall.Skinning != nullptr; + const bool useSkinning = drawCall.Surface.Skinning != nullptr; if (useSkinning) { // Bind skinning buffer - ASSERT(drawCall.Skinning->IsReady()); - context->BindSR(0, drawCall.Skinning->BoneMatrices->View()); + ASSERT(drawCall.Surface.Skinning->IsReady()); + context->BindSR(0, drawCall.Surface.Skinning->BoneMatrices->View()); } // Setup material constants data @@ -97,7 +99,7 @@ void ForwardMaterialShader::Bind(BindParameters& params) Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); Matrix::Transpose(view.View, materialData->ViewMatrix); - Matrix::Transpose(drawCall.PrevWorld, materialData->PrevWorldMatrix); + Matrix::Transpose(drawCall.Surface.PrevWorld, materialData->PrevWorldMatrix); Matrix::Transpose(view.PrevViewProjection, materialData->PrevViewProjectionMatrix); materialData->ViewPos = view.Position; @@ -118,9 +120,9 @@ void ForwardMaterialShader::Bind(BindParameters& params) materialData->WorldInvScale = worldInvScale; materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; - materialData->LODDitherFactor = drawCall.LODDitherFactor; + materialData->LODDitherFactor = drawCall.Surface.LODDitherFactor; materialData->PerInstanceRandom = drawCall.PerInstanceRandom; - materialData->GeometrySize = drawCall.GeometrySize; + materialData->GeometrySize = drawCall.Surface.GeometrySize; } // Setup lighting constants data @@ -249,7 +251,7 @@ void ForwardMaterialShader::Bind(BindParameters& params) if (IsRunningRadiancePass) cullMode = CullMode::TwoSided; #endif - if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale()) + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) { // Invert culling when scale is negative if (cullMode == CullMode::Normal) diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.h b/Source/Engine/Graphics/Materials/ForwardMaterialShader.h index d39fcea54..bcba48259 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.h +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.h @@ -67,7 +67,7 @@ public: // [MaterialShader] DrawPass GetDrawModes() const override; - bool CanUseInstancing() const override; + bool CanUseInstancing(InstancingHandler& handler) const override; void Bind(BindParameters& params) override; void Unload() override; diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index 09796c04b..f1e7856af 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -2,7 +2,6 @@ #pragma once -#include "Engine/Threading/Task.h" #include "MaterialInfo.h" struct MaterialParamsLink; @@ -81,24 +80,6 @@ public: return GetInfo().Domain == MaterialDomain::Particle; } - /// - /// Checks if material needs vertex color vertex buffer data for rendering. - /// - /// True if mesh renderer have to provide vertex color buffer to render that material - FORCE_INLINE bool RequireVertexColor() const - { - return (GetInfo().UsageFlags & MaterialUsageFlags::UseVertexColor) != 0; - } - - /// - /// Checks if material supports dithered LOD transitions. - /// - /// True if material supports dithered LOD transitions, otherwise false. - FORCE_INLINE bool IsDitheredLODTransition() const - { - return (GetInfo().FeaturesFlags & MaterialFeaturesFlags::DitheredLODTransition) != 0; - } - /// /// Returns true if material is ready for rendering. /// @@ -123,11 +104,22 @@ public: return false; } + /// + /// The instancing handling used to hash, batch and write draw calls. + /// + struct InstancingHandler + { + void (*GetHash)(const DrawCall& drawCall, int32& batchKey); + bool (*CanBatch)(const DrawCall& a, const DrawCall& b); + void (*WriteDrawCall)(struct InstanceData* instanceData, const DrawCall& drawCall); + }; + /// /// Returns true if material can use draw calls instancing. /// + /// The output data for the instancing handling used to hash, batch and write draw calls. Valid only when function returns true. /// True if can use instancing, otherwise false. - virtual bool CanUseInstancing() const + virtual bool CanUseInstancing(InstancingHandler& handler) const { return false; } diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index 8c1c065ab..210c81e7d 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -76,7 +76,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) const bool hasCb0 = cb0->GetSize() != 0; const auto cb1 = _shader->GetCB(1); const bool hasCb1 = cb1->GetSize() != 0; - const uint32 sortedIndicesOffset = drawCall.Module->SortedIndicesOffset; + const uint32 sortedIndicesOffset = drawCall.Particle.Module->SortedIndicesOffset; // Setup parameters MaterialParameter::BindMeta bindMeta; @@ -90,9 +90,9 @@ void ParticleMaterialShader::Bind(BindParameters& params) // Setup particles data and attributes binding info { - context->BindSR(0, drawCall.Particles->GPU.Buffer->View()); - if (drawCall.Particles->GPU.SortedIndices) - context->BindSR(1, drawCall.Particles->GPU.SortedIndices->View()); + context->BindSR(0, drawCall.Particle.Particles->GPU.Buffer->View()); + if (drawCall.Particle.Particles->GPU.SortedIndices) + context->BindSR(1, drawCall.Particle.Particles->GPU.SortedIndices->View()); if (hasCb0) { @@ -104,7 +104,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) { auto name = StringView(param.GetName().Get() + 9); - const int32 offset = drawCall.Particles->Layout->FindAttributeOffset(name); + const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name); *((int32*)(bindMeta.Buffer0 + param.GetBindOffset())) = offset; } } @@ -115,7 +115,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != 0 || view.Mode == ViewMode::Wireframe; CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode; PipelineStateCache* psCache = nullptr; - switch (drawCall.Module->TypeID) + switch (drawCall.Particle.Module->TypeID) { // Sprite Rendering case 400: @@ -142,20 +142,20 @@ void ParticleMaterialShader::Bind(BindParameters& params) { const auto materialData = reinterpret_cast(_cb0Data.Get()); - materialData->RibbonWidthOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1); - materialData->RibbonTwistOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1); - materialData->RibbonFacingVectorOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1); + materialData->RibbonWidthOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1); + materialData->RibbonTwistOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1); + materialData->RibbonFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1); - materialData->RibbonUVTilingDistance = drawCall.Ribbon.UVTilingDistance; - materialData->RibbonUVScale.X = drawCall.Ribbon.UVScaleX; - materialData->RibbonUVScale.Y = drawCall.Ribbon.UVScaleY; - materialData->RibbonUVOffset.X = drawCall.Ribbon.UVOffsetX; - materialData->RibbonUVOffset.Y = drawCall.Ribbon.UVOffsetY; - materialData->RibbonSegmentCount = drawCall.Ribbon.SegmentCount; + materialData->RibbonUVTilingDistance = drawCall.Particle.Ribbon.UVTilingDistance; + materialData->RibbonUVScale.X = drawCall.Particle.Ribbon.UVScaleX; + materialData->RibbonUVScale.Y = drawCall.Particle.Ribbon.UVScaleY; + materialData->RibbonUVOffset.X = drawCall.Particle.Ribbon.UVOffsetX; + materialData->RibbonUVOffset.Y = drawCall.Particle.Ribbon.UVOffsetY; + materialData->RibbonSegmentCount = drawCall.Particle.Ribbon.SegmentCount; } - if (drawCall.Ribbon.SegmentDistances) - context->BindSR(1, drawCall.Ribbon.SegmentDistances->View()); + if (drawCall.Particle.Ribbon.SegmentDistances) + context->BindSR(1, drawCall.Particle.Ribbon.SegmentDistances->View()); break; } @@ -186,17 +186,17 @@ void ParticleMaterialShader::Bind(BindParameters& params) materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds(); materialData->ViewInfo = view.ViewInfo; materialData->ScreenSize = view.ScreenSize; - materialData->SortedIndicesOffset = drawCall.Particles->GPU.SortedIndices && params.RenderContext.View.Pass != DrawPass::Depth ? sortedIndicesOffset : 0xFFFFFFFF; + materialData->SortedIndicesOffset = drawCall.Particle.Particles->GPU.SortedIndices && params.RenderContext.View.Pass != DrawPass::Depth ? sortedIndicesOffset : 0xFFFFFFFF; materialData->PerInstanceRandom = drawCall.PerInstanceRandom; - materialData->ParticleStride = drawCall.Particles->Stride; - materialData->PositionOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticlePosition, ParticleAttribute::ValueTypes::Vector3); - materialData->SpriteSizeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteSize, ParticleAttribute::ValueTypes::Vector2); - materialData->SpriteFacingModeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingMode, ParticleAttribute::ValueTypes::Int, -1); - materialData->SpriteFacingVectorOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingVector, ParticleAttribute::ValueTypes::Vector3); - materialData->VelocityOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleVelocityOffset, ParticleAttribute::ValueTypes::Vector3); - materialData->RotationOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRotationOffset, ParticleAttribute::ValueTypes::Vector3, -1); - materialData->ScaleOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleScaleOffset, ParticleAttribute::ValueTypes::Vector3, -1); - materialData->ModelFacingModeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleModelFacingModeOffset, ParticleAttribute::ValueTypes::Int, -1); + materialData->ParticleStride = drawCall.Particle.Particles->Stride; + materialData->PositionOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticlePosition, ParticleAttribute::ValueTypes::Vector3); + materialData->SpriteSizeOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleSpriteSize, ParticleAttribute::ValueTypes::Vector2); + materialData->SpriteFacingModeOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingMode, ParticleAttribute::ValueTypes::Int, -1); + materialData->SpriteFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingVector, ParticleAttribute::ValueTypes::Vector3); + materialData->VelocityOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleVelocityOffset, ParticleAttribute::ValueTypes::Vector3); + materialData->RotationOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRotationOffset, ParticleAttribute::ValueTypes::Vector3, -1); + materialData->ScaleOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleScaleOffset, ParticleAttribute::ValueTypes::Vector3, -1); + materialData->ModelFacingModeOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleModelFacingModeOffset, ParticleAttribute::ValueTypes::Int, -1); Matrix::Invert(drawCall.World, materialData->WorldMatrixInverseTransposed); } diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index 212e27d87..37f932a5a 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -93,36 +93,36 @@ void TerrainMaterialShader::Bind(BindParameters& params) data->WorldDeterminantSign = drawCall.WorldDeterminantSign; data->PerInstanceRandom = drawCall.PerInstanceRandom; - data->CurrentLOD = drawCall.TerrainData.CurrentLOD; - data->ChunkSizeNextLOD = drawCall.TerrainData.ChunkSizeNextLOD; - data->TerrainChunkSizeLOD0 = drawCall.TerrainData.TerrainChunkSizeLOD0; - data->HeightmapUVScaleBias = drawCall.TerrainData.HeightmapUVScaleBias; - data->NeighborLOD = drawCall.TerrainData.NeighborLOD; - data->OffsetUV = drawCall.TerrainData.OffsetUV; + data->CurrentLOD = drawCall.Terrain.CurrentLOD; + data->ChunkSizeNextLOD = drawCall.Terrain.ChunkSizeNextLOD; + data->TerrainChunkSizeLOD0 = drawCall.Terrain.TerrainChunkSizeLOD0; + data->HeightmapUVScaleBias = drawCall.Terrain.HeightmapUVScaleBias; + data->NeighborLOD = drawCall.Terrain.NeighborLOD; + data->OffsetUV = drawCall.Terrain.OffsetUV; } const bool useLightmap = view.Flags & ViewFlags::GI #if USE_EDITOR && EnableLightmapsUsage #endif && view.Pass == DrawPass::GBuffer - && drawCall.Lightmap != nullptr; + && drawCall.Terrain.Lightmap != nullptr; if (useLightmap) { // Bind lightmap textures GPUTexture *lightmap0, *lightmap1, *lightmap2; - drawCall.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); + drawCall.Terrain.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); context->BindSR(0, lightmap0); context->BindSR(1, lightmap1); context->BindSR(2, lightmap2); // Set lightmap data - data->LightmapArea = drawCall.LightmapUVsArea; + data->LightmapArea = drawCall.Terrain.LightmapUVsArea; } // Bind terrain textures - const auto heightmap = drawCall.TerrainData.Patch->Heightmap.Get()->GetTexture(); - const auto splatmap0 = drawCall.TerrainData.Patch->Splatmap[0] ? drawCall.TerrainData.Patch->Splatmap[0]->GetTexture() : nullptr; - const auto splatmap1 = drawCall.TerrainData.Patch->Splatmap[1] ? drawCall.TerrainData.Patch->Splatmap[1]->GetTexture() : nullptr; + const auto heightmap = drawCall.Terrain.Patch->Heightmap.Get()->GetTexture(); + const auto splatmap0 = drawCall.Terrain.Patch->Splatmap[0] ? drawCall.Terrain.Patch->Splatmap[0]->GetTexture() : nullptr; + const auto splatmap1 = drawCall.Terrain.Patch->Splatmap[1] ? drawCall.Terrain.Patch->Splatmap[1]->GetTexture() : nullptr; context->BindSR(3, heightmap); context->BindSR(4, splatmap0); context->BindSR(5, splatmap1); @@ -141,7 +141,7 @@ void TerrainMaterialShader::Bind(BindParameters& params) if (IsRunningRadiancePass) cullMode = CullMode::TwoSided; #endif - if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale()) + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) { // Invert culling when scale is negative if (cullMode == CullMode::Normal) diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 444d60540..99262cd51 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -393,15 +393,15 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons drawCall.IndirectArgsOffset = 0; drawCall.Material = material; drawCall.World = world; - drawCall.PrevWorld = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.GeometrySize = _box.GetSize(); - drawCall.Lightmap = nullptr; - drawCall.LightmapUVsArea = Rectangle::Empty; - drawCall.Skinning = nullptr; + drawCall.Surface.GeometrySize = _box.GetSize(); + drawCall.Surface.PrevWorld = world; + drawCall.Surface.Lightmap = nullptr; + drawCall.Surface.LightmapUVsArea = Rectangle::Empty; + drawCall.Surface.Skinning = nullptr; + drawCall.Surface.LODDitherFactor = 0.0f; drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = 0.0f; - drawCall.LODDitherFactor = 0.0f; renderContext.List->AddDrawCall(DrawPass::Default, flags, drawCall, receiveDecals); } @@ -456,15 +456,15 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float drawCall.IndirectArgsOffset = 0; drawCall.Material = material; drawCall.World = *info.World; - drawCall.PrevWorld = info.DrawState->PrevWorld; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.GeometrySize = _box.GetSize(); - drawCall.Lightmap = info.Flags & StaticFlags::Lightmap ? info.Lightmap : nullptr; - drawCall.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty; - drawCall.Skinning = nullptr; + drawCall.Surface.GeometrySize = _box.GetSize(); + drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; + drawCall.Surface.Lightmap = info.Flags & StaticFlags::Lightmap ? info.Lightmap : nullptr; + drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty; + drawCall.Surface.Skinning = nullptr; + drawCall.Surface.LODDitherFactor = lodDitherFactor; drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = info.PerInstanceRandom; - drawCall.LODDitherFactor = lodDitherFactor; renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals); } diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index 90aa03d86..e9e7f8438 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -193,15 +193,15 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, drawCall.IndirectArgsOffset = 0; drawCall.Material = material; drawCall.World = *info.World; - drawCall.PrevWorld = info.DrawState->PrevWorld; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.GeometrySize = _box.GetSize(); - drawCall.Lightmap = nullptr; - drawCall.LightmapUVsArea = Rectangle::Empty; - drawCall.Skinning = info.Skinning; + drawCall.Surface.GeometrySize = _box.GetSize(); + drawCall.Surface.PrevWorld = info.DrawState->PrevWorld; + drawCall.Surface.Lightmap = nullptr; + drawCall.Surface.LightmapUVsArea = Rectangle::Empty; + drawCall.Surface.Skinning = info.Skinning; + drawCall.Surface.LODDitherFactor = lodDitherFactor; drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = info.PerInstanceRandom; - drawCall.LODDitherFactor = lodDitherFactor; renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals); } diff --git a/Source/Engine/Level/Actors/Skybox.cpp b/Source/Engine/Level/Actors/Skybox.cpp index 48c19191f..e8ea08c89 100644 --- a/Source/Engine/Level/Actors/Skybox.cpp +++ b/Source/Engine/Level/Actors/Skybox.cpp @@ -93,7 +93,7 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M Platform::MemoryClear(&drawCall, sizeof(DrawCall)); drawCall.World = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.GeometrySize = _box.GetSize(); + drawCall.Surface.GeometrySize = _box.GetSize(); drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = GetPerInstanceRandom(); MaterialBase::BindParameters bindParams(context, renderContext, drawCall); diff --git a/Source/Engine/Particles/ParticleManager.cpp b/Source/Engine/Particles/ParticleManager.cpp index 40cebd892..c83aacbbf 100644 --- a/Source/Engine/Particles/ParticleManager.cpp +++ b/Source/Engine/Particles/ParticleManager.cpp @@ -423,7 +423,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa { const int32 moduleIndex = renderModulesIndices[index]; auto module = emitter->Graph.RenderModules[moduleIndex]; - drawCall.Module = module; + drawCall.Particle.Module = module; switch (module->TypeID) { @@ -486,7 +486,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa int32 count = buffer->CPU.Count; // Setup ribbon data - auto& ribbon = drawCall.Ribbon; + auto& ribbon = drawCall.Particle.Ribbon; ribbon.UVTilingDistance = uvTilingDistance; ribbon.SegmentCount = ribbonModulesSegmentCount[ribbonModuleIndex]; ribbon.UVScaleX = uvScale.X; @@ -788,7 +788,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa { int32 moduleIndex = renderModulesIndices[index]; auto module = emitter->Graph.RenderModules[moduleIndex]; - drawCall.Module = module; + drawCall.Particle.Module = module; switch (module->TypeID) { @@ -875,9 +875,7 @@ void ParticleManager::DrawParticles(RenderContext& renderContext, ParticleEffect // Setup a draw call common data DrawCall drawCall; - drawCall.LightmapUVsArea = Rectangle::Empty; drawCall.PerInstanceRandom = effect->GetPerInstanceRandom(); - drawCall.LODDitherFactor = 1.0f; drawCall.ObjectPosition = world.GetTranslation(); // Draw all emitters @@ -890,9 +888,8 @@ void ParticleManager::DrawParticles(RenderContext& renderContext, ParticleEffect auto emitter = buffer->Emitter; drawCall.World = emitter->SimulationSpace == ParticlesSimulationSpace::World ? Matrix::Identity : world; - drawCall.PrevWorld = drawCall.World; drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); - drawCall.Particles = buffer; + drawCall.Particle.Particles = buffer; // Check if need to render any module RenderModulesIndices renderModulesIndices; diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index cc35b2627..31656145b 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -113,6 +113,11 @@ public: /// struct DrawCall { + /// + /// The material to use for rendering. + /// + IMaterial* Material; + struct { /// @@ -156,53 +161,18 @@ struct DrawCall /// GPUBuffer* IndirectArgsBuffer; - /// - /// The target material to use. - /// - IMaterial* Material; - - // Particles don't use skinning nor lightmaps so pack those stuff together + // Per-material shader data packed into union union { struct { - /// - /// Pointer to lightmap for static object with prebaked lighting. - /// const Lightmap* Lightmap; - - /// - /// The skinning data. If set then material should use GPU skinning during rendering. - /// SkinnedMeshDrawData* Skinning; - }; - - struct - { - /// - /// The particles data. Used only by the particles shaders. - /// - class ParticleBuffer* Particles; - - /// - /// The particle module to draw. - /// - class ParticleEmitterGraphCPUNode* Module; - }; - }; - - /// - /// Object world transformation matrix. - /// - Matrix World; - - // Terrain and particles don't use previous world matrix so pack those stuff together - union - { - /// - /// Object world transformation matrix using during previous frame. - /// - Matrix PrevWorld; + Rectangle LightmapUVsArea; + Vector3 GeometrySize; // Object geometry size in the world (unscaled). + float LODDitherFactor; // The model LOD transition dither progress. + Matrix PrevWorld; + } Surface; struct { @@ -212,26 +182,39 @@ struct DrawCall float CurrentLOD; float ChunkSizeNextLOD; float TerrainChunkSizeLOD0; + Rectangle LightmapUVsArea; const class TerrainPatch* Patch; - } TerrainData; + const Lightmap* Lightmap; + } Terrain; struct { - int32 RibbonOrderOffset; - float UVTilingDistance; - float UVScaleX; - float UVScaleY; - float UVOffsetX; - float UVOffsetY; - uint32 SegmentCount; - GPUBuffer* SegmentDistances; - } Ribbon; + class ParticleBuffer* Particles; + class ParticleEmitterGraphCPUNode* Module; + + struct + { + int32 OrderOffset; + float UVTilingDistance; + float UVScaleX; + float UVScaleY; + float UVOffsetX; + float UVOffsetY; + uint32 SegmentCount; + GPUBuffer* SegmentDistances; + } Ribbon; + } Particle; + + struct + { + byte Raw[96]; + } Custom; }; /// - /// Lightmap UVs area that entry occupies. + /// Object world transformation matrix. /// - Rectangle LightmapUVsArea; + Matrix World; /// /// Object location in the world used for draw calls sorting. @@ -243,36 +226,17 @@ struct DrawCall /// float WorldDeterminantSign; - /// - /// Object geometry size in the world (unscaled). - /// - Vector3 GeometrySize; - /// /// The random per-instance value (normalized to range 0-1). /// float PerInstanceRandom; - /// - /// The model LOD transition dither factor. - /// - float LODDitherFactor; - /// /// Does nothing. /// DrawCall() { } - - /// - /// Determines whether world transform matrix is performing negative scale (then model culling should be inverted). - /// - /// true if world matrix contains negative scale; otherwise, false. - FORCE_INLINE bool IsNegativeScale() const - { - return WorldDeterminantSign < 0; - } }; template<> diff --git a/Source/Engine/Renderer/Editor/LightmapUVsDensity.cpp b/Source/Engine/Renderer/Editor/LightmapUVsDensity.cpp index 152b43d50..9ba9f1a0d 100644 --- a/Source/Engine/Renderer/Editor/LightmapUVsDensity.cpp +++ b/Source/Engine/Renderer/Editor/LightmapUVsDensity.cpp @@ -178,7 +178,7 @@ void LightmapUVsDensityMaterialShader::Bind(BindParameters& params) scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); data.LightmapTexelsPerWorldUnit = ShadowsOfMordor::LightmapTexelsPerWorldUnit; data.LightmapSize = 1024.0f; - data.LightmapArea = drawCall.LightmapUVsArea; + data.LightmapArea = drawCall.Surface.LightmapUVsArea; if (drawCallModel) { // Calculate current lightmap slot size for the object (matches the ShadowsOfMordor calculations when baking the lighting) diff --git a/Source/Engine/Renderer/GBufferPass.cpp b/Source/Engine/Renderer/GBufferPass.cpp index 5c6f76905..b9e91ee01 100644 --- a/Source/Engine/Renderer/GBufferPass.cpp +++ b/Source/Engine/Renderer/GBufferPass.cpp @@ -145,7 +145,8 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer drawCall.Material = _lightmapUVsDensityMaterialShader; } } - if (!_lightmapUVsDensityMaterialShader->CanUseInstancing()) + IMaterial::InstancingHandler handler; + if (!_lightmapUVsDensityMaterialShader->CanUseInstancing(handler)) { drawCallsList.CanUseInstancing = false; } @@ -166,7 +167,8 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer drawCall.Material = _vertexColorsMaterialShader; } } - if (!_vertexColorsMaterialShader->CanUseInstancing()) + IMaterial::InstancingHandler handler; + if (!_vertexColorsMaterialShader->CanUseInstancing(handler)) { drawCallsList.CanUseInstancing = false; } @@ -311,8 +313,6 @@ void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* light DrawCall drawCall; MaterialBase::BindParameters bindParams(gpuContext, renderContext, drawCall); drawCall.Material = nullptr; - drawCall.Lightmap = nullptr; - drawCall.Skinning = nullptr; drawCall.WorldDeterminantSign = 1.0f; // Draw all decals diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index c78931416..dd711a5e0 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -19,6 +19,11 @@ #define BATCH_KEY_BITS 32 #define BATCH_KEY_MASK ((1 << BATCH_KEY_BITS) - 1) +static_assert(sizeof(DrawCall) <= 280, "Too big draw call data size."); +static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Terrain), "Wrong draw call data size."); +static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Particle), "Wrong draw call data size."); +static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Custom), "Wrong draw call data size."); + namespace { // Cached data for the draw calls sorting @@ -495,24 +500,24 @@ end: } } -/// -/// Checks if this draw call be batched together with the other one. -/// -/// The first draw call. -/// The second draw call. -/// True if can merge them, otherwise false. -FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b) +namespace { - return Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 && - a.Material == b.Material && - a.Lightmap == b.Lightmap && - // TODO: add batch.CanBatch flag computed in AddDrawCall to remove those checks here for Skinning and IndirectDrawArgs - a.Skinning == nullptr && - b.Skinning == nullptr && - a.IndirectArgsBuffer == nullptr && - b.IndirectArgsBuffer == nullptr && - a.WorldDeterminantSign == b.WorldDeterminantSign && - a.Material->CanUseInstancing(); + /// + /// Checks if this draw call be batched together with the other one. + /// + /// The first draw call. + /// The second draw call. + /// True if can merge them, otherwise false. + FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b) + { + IMaterial::InstancingHandler handler; + return a.Material == b.Material && + a.Material->CanUseInstancing(handler) && + Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 && + a.IndirectArgsBuffer == nullptr && + b.IndirectArgsBuffer == nullptr && + a.WorldDeterminantSign == b.WorldDeterminantSign; + } } void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list) @@ -540,7 +545,9 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[1]); batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[2]); batchKey = (batchKey * 397) ^ GetHash(drawCall.Material); - batchKey = (batchKey * 397) ^ GetHash(drawCall.Lightmap); + IMaterial::InstancingHandler handler; + if (drawCall.Material->CanUseInstancing(handler)) + handler.GetHash(drawCall, batchKey); batchKey += (int32)(471 * drawCall.WorldDeterminantSign); #if USE_BATCH_KEY_MASK const uint32 batchHashKey = (uint32)batchKey & BATCH_KEY_MASK; @@ -635,14 +642,12 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL auto& batch = list.Batches[i]; if (batch.BatchSize > 1) { + IMaterial::InstancingHandler handler; + DrawCalls[list.Indices[batch.StartIndex]].Material->CanUseInstancing(handler); for (int32 j = 0; j < batch.BatchSize; j++) { auto& drawCall = DrawCalls[list.Indices[batch.StartIndex + j]]; - instanceData->InstanceOrigin = Vector4(drawCall.World.M41, drawCall.World.M42, drawCall.World.M43, drawCall.PerInstanceRandom); - instanceData->InstanceTransform1 = Vector4(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13, drawCall.LODDitherFactor); - instanceData->InstanceTransform2 = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23); - instanceData->InstanceTransform3 = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33); - instanceData->InstanceLightmapArea = Half4(drawCall.LightmapUVsArea); + handler.WriteDrawCall(instanceData, drawCall); instanceData++; } } @@ -742,3 +747,26 @@ DRAW: } } } + +void SurfaceDrawCallHandler::GetHash(const DrawCall& drawCall, int32& batchKey) +{ + batchKey = (batchKey * 397) ^ ::GetHash(drawCall.Surface.Lightmap); +} + +bool SurfaceDrawCallHandler::CanBatch(const DrawCall& a, const DrawCall& b) +{ + return a.Surface.Lightmap == b.Surface.Lightmap && + a.Surface.Skinning == nullptr && + b.Surface.Skinning == nullptr; +} + +void SurfaceDrawCallHandler::WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall) +{ + instanceData->InstanceOrigin = Vector3(drawCall.World.M41, drawCall.World.M42, drawCall.World.M43); + instanceData->PerInstanceRandom = drawCall.PerInstanceRandom; + instanceData->InstanceTransform1 = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13); + instanceData->LODDitherFactor = drawCall.Surface.LODDitherFactor; + instanceData->InstanceTransform2 = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23); + instanceData->InstanceTransform3 = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33); + instanceData->InstanceLightmapArea = Half4(drawCall.Surface.LightmapUVsArea); +} diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index dffbbd2d1..69ef0c33d 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -357,18 +357,6 @@ public: private: - /// - /// Represents data per instance element used for instanced rendering. - /// - struct InstanceData - { - Vector4 InstanceOrigin; // .w contains PerInstanceRandom - Vector4 InstanceTransform1; // .w contains LODDitherFactor - Vector3 InstanceTransform2; - Vector3 InstanceTransform3; - Half4 InstanceLightmapArea; - }; - DynamicVertexBuffer _instanceBuffer; public: @@ -475,3 +463,24 @@ public: /// The collected draw calls list. void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list); }; + +/// +/// Represents data per instance element used for instanced rendering. +/// +struct FLAXENGINE_API InstanceData +{ + Vector3 InstanceOrigin; + float PerInstanceRandom; + Vector3 InstanceTransform1; + float LODDitherFactor; + Vector3 InstanceTransform2; + Vector3 InstanceTransform3; + Half4 InstanceLightmapArea; +}; + +struct SurfaceDrawCallHandler +{ + static void GetHash(const DrawCall& drawCall, int32& batchKey); + static bool CanBatch(const DrawCall& a, const DrawCall& b); + static void WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall); +}; diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 4d3241375..89efef74c 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -31,9 +31,6 @@ #include "Editor/Editor.h" #endif -// It must use less or the same amount of memory -static_assert(sizeof(DrawCall::TerrainData) <= sizeof(DrawCall::PrevWorld), "Invalid size of the terrain data in the draw call."); - #if USE_EDITOR // Additional options used in editor for lightmaps baking bool IsRunningRadiancePass = false; diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index 1241935e5..6cff541f3 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -88,33 +88,31 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const drawCall.Material = _cachedDrawMaterial; drawCall.World = _world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.TerrainData.Patch = _patch; - drawCall.TerrainData.HeightmapUVScaleBias = _heightmapUVScaleBias; - drawCall.TerrainData.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z)); - drawCall.TerrainData.CurrentLOD = (float)lod; - drawCall.TerrainData.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1); - drawCall.TerrainData.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize; + drawCall.Terrain.Patch = _patch; + drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias; + drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z)); + drawCall.Terrain.CurrentLOD = (float)lod; + drawCall.Terrain.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1); + drawCall.Terrain.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize; // TODO: try using SIMD clamping for 4 chunks at once - drawCall.TerrainData.NeighborLOD.X = (float)Math::Clamp(_neighbors[0]->_cachedDrawLOD, lod, minLod); - drawCall.TerrainData.NeighborLOD.Y = (float)Math::Clamp(_neighbors[1]->_cachedDrawLOD, lod, minLod); - drawCall.TerrainData.NeighborLOD.Z = (float)Math::Clamp(_neighbors[2]->_cachedDrawLOD, lod, minLod); - drawCall.TerrainData.NeighborLOD.W = (float)Math::Clamp(_neighbors[3]->_cachedDrawLOD, lod, minLod); + drawCall.Terrain.NeighborLOD.X = (float)Math::Clamp(_neighbors[0]->_cachedDrawLOD, lod, minLod); + drawCall.Terrain.NeighborLOD.Y = (float)Math::Clamp(_neighbors[1]->_cachedDrawLOD, lod, minLod); + drawCall.Terrain.NeighborLOD.Z = (float)Math::Clamp(_neighbors[2]->_cachedDrawLOD, lod, minLod); + drawCall.Terrain.NeighborLOD.W = (float)Math::Clamp(_neighbors[3]->_cachedDrawLOD, lod, minLod); const auto scene = _patch->_terrain->GetScene(); const auto flags = _patch->_terrain->_staticFlags; if (flags & StaticFlags::Lightmap && scene) { - drawCall.Lightmap = scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); - drawCall.LightmapUVsArea = Lightmap.UVsArea; + drawCall.Terrain.Lightmap = scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); + drawCall.Terrain.LightmapUVsArea = Lightmap.UVsArea; } else { - drawCall.Lightmap = nullptr; - drawCall.LightmapUVsArea = Rectangle::Empty; + drawCall.Terrain.Lightmap = nullptr; + drawCall.Terrain.LightmapUVsArea = Rectangle::Empty; } - drawCall.Skinning = nullptr; drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = _perInstanceRandom; - drawCall.LODDitherFactor = 0.0f; // Add half-texel offset for heightmap sampling in vertex shader //const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod); @@ -147,32 +145,30 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi drawCall.Material = material; drawCall.World = _world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.TerrainData.Patch = _patch; - drawCall.TerrainData.HeightmapUVScaleBias = _heightmapUVScaleBias; - drawCall.TerrainData.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z)); - drawCall.TerrainData.CurrentLOD = (float)lod; - drawCall.TerrainData.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1); - drawCall.TerrainData.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize; - drawCall.TerrainData.NeighborLOD.X = (float)lod; - drawCall.TerrainData.NeighborLOD.Y = (float)lod; - drawCall.TerrainData.NeighborLOD.Z = (float)lod; - drawCall.TerrainData.NeighborLOD.W = (float)lod; + drawCall.Terrain.Patch = _patch; + drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias; + drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z)); + drawCall.Terrain.CurrentLOD = (float)lod; + drawCall.Terrain.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1); + drawCall.Terrain.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize; + drawCall.Terrain.NeighborLOD.X = (float)lod; + drawCall.Terrain.NeighborLOD.Y = (float)lod; + drawCall.Terrain.NeighborLOD.Z = (float)lod; + drawCall.Terrain.NeighborLOD.W = (float)lod; const auto scene = _patch->_terrain->GetScene(); const auto flags = _patch->_terrain->_staticFlags; if (flags & StaticFlags::Lightmap && scene) { - drawCall.Lightmap = scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); - drawCall.LightmapUVsArea = Lightmap.UVsArea; + drawCall.Terrain.Lightmap = scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); + drawCall.Terrain.LightmapUVsArea = Lightmap.UVsArea; } else { - drawCall.Lightmap = nullptr; - drawCall.LightmapUVsArea = Rectangle::Empty; + drawCall.Terrain.Lightmap = nullptr; + drawCall.Terrain.LightmapUVsArea = Rectangle::Empty; } - drawCall.Skinning = nullptr; drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = _perInstanceRandom; - drawCall.LODDitherFactor = 0.0f; // Add half-texel offset for heightmap sampling in vertex shader //const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod); diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index 958aced8b..5707b4461 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -319,15 +319,15 @@ void TextRender::Draw(RenderContext& renderContext) // Setup draw call DrawCall drawCall; drawCall.World = _world; - drawCall.PrevWorld = _drawState.PrevWorld; drawCall.ObjectPosition = drawCall.World.GetTranslation(); - drawCall.GeometrySize = _localBox.GetSize(); - drawCall.Lightmap = nullptr; - drawCall.LightmapUVsArea = Rectangle::Empty; - drawCall.Skinning = nullptr; + drawCall.Surface.GeometrySize = _localBox.GetSize(); + drawCall.Surface.PrevWorld = _drawState.PrevWorld; + drawCall.Surface.Lightmap = nullptr; + drawCall.Surface.LightmapUVsArea = Rectangle::Empty; + drawCall.Surface.Skinning = nullptr; + drawCall.Surface.LODDitherFactor = 0.0f; drawCall.WorldDeterminantSign = Math::FloatSelect(_world.RotDeterminant(), 1, -1); drawCall.PerInstanceRandom = GetPerInstanceRandom(); - drawCall.LODDitherFactor = 0.0f; drawCall.Geometry.IndexBuffer = _ib.GetBuffer(); drawCall.Geometry.VertexBuffers[0] = _vb0.GetBuffer(); drawCall.Geometry.VertexBuffers[1] = _vb1.GetBuffer(); From 8e4a0e9e97dacb964438392e44dc5c6f741cc60b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 19:09:41 +0100 Subject: [PATCH 296/419] Fix drawing UI Control outline in Game Window (#194) --- Source/Editor/Windows/GameWindow.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 2f9be0254..858f0e0ba 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -294,7 +294,9 @@ namespace FlaxEditor.Windows { if (Editor.Instance.SceneEditing.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null) { - Render2D.DrawRectangle(controlActor.Control.Bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize); + var control = controlActor.Control; + var bounds = Rectangle.FromPoints(control.PointToParent(_viewport, Vector2.Zero), control.PointToParent(_viewport, control.Size)); + Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize); } } From c3b36062edb2edeb3c34ccb3620be203fb42fc17 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 19:24:47 +0100 Subject: [PATCH 297/419] Fix error --- Source/Engine/UI/GUI/Control.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs index 1c4eda965..c2ba34f05 100644 --- a/Source/Engine/UI/GUI/Control.cs +++ b/Source/Engine/UI/GUI/Control.cs @@ -1006,10 +1006,9 @@ namespace FlaxEngine.GUI c = c.Parent; if (c == parent) - return location; + break; } - - throw new ArgumentException(); + return location; } /// From a572e581e5614da8a2a58a6c59ef2c8222b2daa9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 21:30:33 +0100 Subject: [PATCH 298/419] Fix HorizontalPanel children layout Fixes 191 --- Source/Engine/UI/GUI/Panels/HorizontalPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs index 8f8e7f994..53e435f91 100644 --- a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs +++ b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs @@ -45,7 +45,7 @@ namespace FlaxEngine.GUI if (c.Visible) { var w = c.Width; - c.Bounds = new Rectangle(x + _offset.X, _margin.Top + _offset.Y, h, w); + c.Bounds = new Rectangle(x + _offset.X, _margin.Top + _offset.Y, w, h); x = c.Right + _spacing; hasAnyItem = true; } From ba0f07b57ece91052e517d18ab920779f5cab897 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 21:52:29 +0100 Subject: [PATCH 299/419] Fix BlurPanel rendering Fixes #192 --- Source/Engine/Render2D/Render2D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 2413686aa..154dbe636 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -967,7 +967,7 @@ void DrawBatch(int32 startIndex, int32 count) // Downscale (or not) and extract the background image for the blurring Context->ResetRenderTarget(); Context->SetRenderTarget(blurA->View()); - Context->SetViewport((float)renderTargetWidth, (float)renderTargetHeight); + Context->SetViewportAndScissors((float)renderTargetWidth, (float)renderTargetHeight); Context->BindSR(0, Output); Context->SetState(CurrentPso->PS_Downscale); Context->DrawFullscreenTriangle(); From 6ffa497c0a0d31d34c4b46e91591ad1239cffe74 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 21:52:49 +0100 Subject: [PATCH 300/419] Fixes for Render2D --- Source/Engine/Render2D/Render2D.cpp | 74 ++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 154dbe636..c956566ca 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -182,41 +182,44 @@ struct ClipMask Render2D::RenderingFeatures Render2D::Features = RenderingFeatures::VertexSnapping; -// Private Stuff -GPUContext* Context = nullptr; -GPUTextureView* Output = nullptr; -GPUTextureView* DepthBuffer = nullptr; -Viewport View; -Matrix ViewProjection; +namespace +{ + // Private Stuff + GPUContext* Context = nullptr; + GPUTextureView* Output = nullptr; + GPUTextureView* DepthBuffer = nullptr; + Viewport View; + Matrix ViewProjection; -// Drawing -Array DrawCalls; -Array Lines; -Array Lines2; -bool IsScissorsRectEmpty; -bool IsScissorsRectEnabled; + // Drawing + Array DrawCalls; + Array Lines; + Array Lines2; + bool IsScissorsRectEmpty; + bool IsScissorsRectEnabled; -// Transform -// Note: we use Matrix3x3 instead of Matrix because we use only 2D transformations on CPU side -// Matrix layout: -// [ m1, m2, 0 ] -// [ m3, m4, 0 ] -// [ t1, t2, 1 ] -// where 'm' is 2D transformation (scale, shear and rotate), 't' is translation -Array> TransformLayersStack; -Matrix3x3 TransformCached; + // Transform + // Note: we use Matrix3x3 instead of Matrix because we use only 2D transformations on CPU side + // Matrix layout: + // [ m1, m2, 0 ] + // [ m3, m4, 0 ] + // [ t1, t2, 1 ] + // where 'm' is 2D transformation (scale, shear and rotate), 't' is translation + Array> TransformLayersStack; + Matrix3x3 TransformCached; -Array> ClipLayersStack; + Array> ClipLayersStack; -// Shader -AssetReference GUIShader; -CachedPSO PsoDepth; -CachedPSO PsoNoDepth; -CachedPSO* CurrentPso = nullptr; -DynamicVertexBuffer VB(RENDER2D_INITIAL_VB_CAPACITY, (uint32)sizeof(Render2DVertex), TEXT("Render2D.VB")); -DynamicIndexBuffer IB(RENDER2D_INITIAL_IB_CAPACITY, sizeof(uint32), TEXT("Render2D.IB")); -uint32 VBIndex = 0; -uint32 IBIndex = 0; + // Shader + AssetReference GUIShader; + CachedPSO PsoDepth; + CachedPSO PsoNoDepth; + CachedPSO* CurrentPso = nullptr; + DynamicVertexBuffer VB(RENDER2D_INITIAL_VB_CAPACITY, (uint32)sizeof(Render2DVertex), TEXT("Render2D.VB")); + DynamicIndexBuffer IB(RENDER2D_INITIAL_IB_CAPACITY, sizeof(uint32), TEXT("Render2D.IB")); + uint32 VBIndex = 0; + uint32 IBIndex = 0; +} #define RENDER2D_WRITE_IB_QUAD(indices) \ indices[0] = VBIndex + 0; \ @@ -957,8 +960,8 @@ void DrawBatch(int32 startIndex, int32 count) data.Bounds.Y = bounds.Y; data.Bounds.Z = bounds.Z - bounds.X; data.Bounds.W = bounds.W - bounds.Y; - data.InvBufferSize.X = 1.0f / renderTargetWidth; - data.InvBufferSize.Y = 1.0f / renderTargetHeight; + data.InvBufferSize.X = 1.0f / (float)renderTargetWidth; + data.InvBufferSize.Y = 1.0f / (float)renderTargetHeight; data.SampleCount = ComputeBlurWeights(kernelSize, blurStrength, data.WeightAndOffsets); const auto cb = GUIShader->GetShader()->GetCB(1); Context->UpdateCB(cb, &data); @@ -1003,11 +1006,8 @@ void DrawBatch(int32 startIndex, int32 count) break; } case DrawCallType::ClipScissors: - { - Rectangle* scissorsRect = (Rectangle*)&d.AsClipScissors.X; - Context->SetScissor(*scissorsRect); + Context->SetScissor(*(Rectangle*)&d.AsClipScissors.X); return; - } case DrawCallType::LineAA: Context->SetState(CurrentPso->PS_LineAA); break; From 85c219369d3194b10c4aa52524dcbb86990daeb2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 21:59:21 +0100 Subject: [PATCH 301/419] Fix updating UI layout when adding control Fixes #190 --- Source/Engine/UI/UIControl.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Engine/UI/UIControl.cs b/Source/Engine/UI/UIControl.cs index e0f15a106..e4a6b4e33 100644 --- a/Source/Engine/UI/UIControl.cs +++ b/Source/Engine/UI/UIControl.cs @@ -30,10 +30,11 @@ namespace FlaxEngine return; // Cleanup previous - if (_control != null) + var prevControl = _control; + if (prevControl != null) { - _control.LocationChanged -= OnControlLocationChanged; - _control.Dispose(); + prevControl.LocationChanged -= OnControlLocationChanged; + prevControl.Dispose(); } // Set value @@ -42,16 +43,17 @@ namespace FlaxEngine // Link the new one (events and parent) if (_control != null) { + // Setup control var containerControl = _control as ContainerControl; if (containerControl != null) containerControl.UnlockChildrenRecursive(); - _control.Parent = GetParent(); _control.IndexInParent = OrderInParent; _control.Location = new Vector2(LocalPosition); // TODO: sync control order in parent with actor order in parent (think about special cases like Panel with scroll bars used as internal controls) _control.LocationChanged += OnControlLocationChanged; + // Link children UI controls if (containerControl != null && IsActiveInHierarchy) { var children = ChildrenCount; @@ -64,6 +66,12 @@ namespace FlaxEngine } } } + + // Refresh + if (prevControl == null && _control.Parent != null) + _control.Parent.PerformLayout(); + else + _control.PerformLayout(); } } } From 9c348b284fe38c33942155afe871f8e235a6d5a4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 22:00:12 +0100 Subject: [PATCH 302/419] Fixes for UI control sync --- Source/Engine/UI/UIControl.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Source/Engine/UI/UIControl.cs b/Source/Engine/UI/UIControl.cs index e4a6b4e33..b31d9cab0 100644 --- a/Source/Engine/UI/UIControl.cs +++ b/Source/Engine/UI/UIControl.cs @@ -13,6 +13,7 @@ namespace FlaxEngine partial class UIControl { private Control _control; + private static bool _blockEvents; // Used to ignore internal events from C++ UIControl impl when performing state sync with C# UI /// /// Gets or sets the GUI control used by this actor. @@ -44,6 +45,7 @@ namespace FlaxEngine if (_control != null) { // Setup control + _blockEvents = true; var containerControl = _control as ContainerControl; if (containerControl != null) containerControl.UnlockChildrenRecursive(); @@ -68,6 +70,7 @@ namespace FlaxEngine } // Refresh + _blockEvents = false; if (prevControl == null && _control.Parent != null) _control.Parent.PerformLayout(); else @@ -178,7 +181,9 @@ namespace FlaxEngine private void OnControlLocationChanged(Control control) { + _blockEvents = true; LocalPosition = new Vector3(control.Location, LocalPosition.Z); + _blockEvents = false; } /// @@ -293,7 +298,7 @@ namespace FlaxEngine internal void ParentChanged() { - if (_control != null) + if (_control != null && !_blockEvents) { _control.Parent = GetParent(); _control.IndexInParent = OrderInParent; @@ -302,13 +307,15 @@ namespace FlaxEngine internal void TransformChanged() { - if (_control != null) + if (_control != null && !_blockEvents) + { _control.Location = new Vector2(LocalPosition); + } } internal void ActiveInTreeChanged() { - if (_control != null) + if (_control != null && !_blockEvents) { // Link or unlink control (won't modify Enable/Visible state) _control.Parent = GetParent(); @@ -318,8 +325,10 @@ namespace FlaxEngine internal void OrderInParentChanged() { - if (_control != null) + if (_control != null && !_blockEvents) + { _control.IndexInParent = OrderInParent; + } } internal void BeginPlay() From 4949ea4b3b48283c6585a3bb7194104f903256e9 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Wed, 3 Feb 2021 22:24:08 +0100 Subject: [PATCH 303/419] Applied review changes --- .../Tools/MaterialGenerator/MaterialGenerator.Material.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 83251089f..16d9623ec 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -435,7 +435,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Blackbody case 35: { - // Based on unity's implementation by using data gathered by Mitchell Charity. + // Reference: Mitchell Charity, http://www.vendian.org/mncharity/dir3/blackbody/ const auto temperature = tryGetValue(node->GetBox(0), node->Values[0]).AsFloat(); @@ -451,9 +451,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) // Final color auto color = writeLocal(ValueType::Vector3, String::Format(TEXT("float3({0}, {1}, {2})"), x.Value, y.Value, z.Value), node); color = writeLocal(ValueType::Vector3, String::Format(TEXT("clamp({0}, 0.0f, 255.0f) / 255.0f"), color.Value), node); - color = writeLocal(ValueType::Vector3, String::Format(TEXT("{1} < 1000.0f ? {0} * {1}/1000.0f : {0}"), color.Value, temperature.Value), node); - - value = color; + value = writeLocal(ValueType::Vector3, String::Format(TEXT("{1} < 1000.0f ? {0} * {1}/1000.0f : {0}"), color.Value, temperature.Value), node); break; } default: From e3142e640853b5c6cbd269c604946da000f45efb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 22:34:11 +0100 Subject: [PATCH 304/419] Fix updating input fields on editing end without changes Fixes #197 --- Source/Editor/GUI/Input/ValueBox.cs | 21 +++++++++++++++++++-- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index 8ee1c3887..d34acd17b 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -49,6 +49,11 @@ namespace FlaxEditor.GUI.Input /// protected T _startSlideValue; + /// + /// The text cached on editing start. Used to compare with the end result to detect changes. + /// + protected string _startEditText; + private Vector2 _startSlideLocation; /// @@ -257,11 +262,23 @@ namespace FlaxEditor.GUI.Input return base.OnMouseUp(location, button); } + /// + protected override void OnEditBegin() + { + base.OnEditBegin(); + + _startEditText = _text; + } + /// protected override void OnEditEnd() { - // Update value - TryGetValue(); + if (_startEditText != _text) + { + // Update value + TryGetValue(); + } + _startEditText = null; base.OnEditEnd(); } diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 107da6f40..ccf346ed6 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -942,6 +942,8 @@ namespace FlaxEngine.GUI { base.OnLostFocus(); + if (IsReadOnly) + return; OnEditEnd(); } From 880a8c9f58044f0a6cee99638ac6074e53c84da8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Feb 2021 23:10:36 +0100 Subject: [PATCH 305/419] Add support for parsing exponential (scientific) notation numbers in input fields --- Source/Editor/Utilities/ShuntingYardParser.cs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Utilities/ShuntingYardParser.cs b/Source/Editor/Utilities/ShuntingYardParser.cs index 100c5c3d7..a07026d1c 100644 --- a/Source/Editor/Utilities/ShuntingYardParser.cs +++ b/Source/Editor/Utilities/ShuntingYardParser.cs @@ -193,6 +193,7 @@ namespace FlaxEditor.Utilities { case 'x': case 'X': + { // Hexadecimal value i++; token.Clear(); @@ -200,22 +201,35 @@ namespace FlaxEditor.Utilities throw new ParsingException("invalid hexadecimal number"); while (i + 1 < text.Length && StringUtils.IsHexDigit(text[i + 1])) { - i++; - token.Append(text[i]); + token.Append(text[++i]); } var value = ulong.Parse(token.ToString(), NumberStyles.HexNumber); token.Clear(); token.Append(value.ToString()); break; + } default: + { // Decimal value while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Number) { - i++; - token.Append(text[i]); + token.Append(text[++i]); + } + + // Exponential notation + if (i + 2 < text.Length && (text[i + 1] == 'e' || text[i + 1] == 'E')) + { + token.Append(text[++i]); + if (text[i + 1] == '-' || text[i + 1] == '+') + token.Append(text[++i]); + while (i + 1 < text.Length && DetermineType(text[i + 1]) == TokenType.Number) + { + token.Append(text[++i]); + } } break; } + } } // Discard solo '-' From 2a3b6edf50978d282632055420e03115018dd825 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Feb 2021 10:43:04 +0100 Subject: [PATCH 306/419] Refactor material shaders generator to use modular features as extensions --- Content/Editor/MaterialTemplates/Decal.shader | 4 + .../MaterialTemplates/Features/Lightmap.hlsl | 53 +++ .../Features/Tessellation.hlsl | 294 ++++++++++++++++ Content/Editor/MaterialTemplates/GUI.shader | 4 + .../Editor/MaterialTemplates/Particle.shader | 4 + .../MaterialTemplates/PostProcess.shader | 4 + .../MaterialTemplates/SurfaceDeferred.shader | 330 +----------------- .../MaterialTemplates/SurfaceForward.shader | 258 +------------- .../Editor/MaterialTemplates/Terrain.shader | 22 +- Source/Engine/Content/Assets/Material.cpp | 1 - .../Materials/DecalMaterialShader.cpp | 2 +- .../Materials/DeferredMaterialShader.cpp | 32 +- .../Materials/ForwardMaterialShader.cpp | 14 +- .../Graphics/Materials/GUIMaterialShader.cpp | 2 +- Source/Engine/Graphics/Materials/IMaterial.h | 6 - .../Graphics/Materials/MaterialParams.cpp | 43 +-- .../Graphics/Materials/MaterialParams.h | 4 +- .../Materials/MaterialShaderFeatures.cpp | 172 +++++++++ .../Materials/MaterialShaderFeatures.h | 70 ++++ .../Materials/ParticleMaterialShader.cpp | 4 +- .../Materials/PostFxMaterialShader.cpp | 2 +- .../Materials/TerrainMaterialShader.cpp | 2 +- .../Particles/Graph/GPU/GPUParticles.cpp | 2 +- .../MaterialGenerator.Texture.cpp | 23 -- .../MaterialGenerator/MaterialGenerator.cpp | 190 +++++++++- .../MaterialGenerator/MaterialGenerator.h | 2 - Source/Engine/Utilities/TextWriter.h | 20 ++ .../Engine/Visject/ShaderGraphUtilities.cpp | 4 - Source/Shaders/MaterialCommon.hlsl | 20 -- 29 files changed, 877 insertions(+), 711 deletions(-) create mode 100644 Content/Editor/MaterialTemplates/Features/Lightmap.hlsl create mode 100644 Content/Editor/MaterialTemplates/Features/Tessellation.hlsl create mode 100644 Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp create mode 100644 Source/Engine/Graphics/Materials/MaterialShaderFeatures.h diff --git a/Content/Editor/MaterialTemplates/Decal.shader b/Content/Editor/MaterialTemplates/Decal.shader index e0253efd6..35446b7e2 100644 --- a/Content/Editor/MaterialTemplates/Decal.shader +++ b/Content/Editor/MaterialTemplates/Decal.shader @@ -108,6 +108,8 @@ float4 GetVertexColor(MaterialInput input) return 1; } +@8 + // Get material properties function (for pixel shader) Material GetMaterialPS(MaterialInput input) { @@ -211,3 +213,5 @@ void PS_Decal( #error "Invalid decal blending mode" #endif } + +@9 diff --git a/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl b/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl new file mode 100644 index 000000000..6f6a44d8b --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl @@ -0,0 +1,53 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +@0// Lightmap: Defines +@1// Lightmap: Includes +@2// Lightmap: Constants +float4 LightmapArea; +@3// Lightmap: Resources +#if USE_LIGHTMAP +// Irradiance and directionality prebaked lightmaps +Texture2D Lightmap0 : register(t__SRV__); +Texture2D Lightmap1 : register(t__SRV__); +Texture2D Lightmap2 : register(t__SRV__); +#endif +@4// Lightmap: Utilities +#if USE_LIGHTMAP + +// Evaluates the H-Basis coefficients in the tangent space normal direction +float3 GetHBasisIrradiance(float3 n, float3 h0, float3 h1, float3 h2, float3 h3) +{ + // Band 0 + float3 color = h0 * (1.0f / sqrt(2.0f * PI)); + + // Band 1 + color += h1 * -sqrt(1.5f / PI) * n.y; + color += h2 * sqrt(1.5f / PI) * (2 * n.z - 1.0f); + color += h3 * -sqrt(1.5f / PI) * n.x; + + return color; +} + +float3 SampleLightmap(Material material, MaterialInput materialInput) +{ + // Sample lightmaps + float4 lightmap0 = Lightmap0.Sample(SamplerLinearClamp, materialInput.LightmapUV); + float4 lightmap1 = Lightmap1.Sample(SamplerLinearClamp, materialInput.LightmapUV); + float4 lightmap2 = Lightmap2.Sample(SamplerLinearClamp, materialInput.LightmapUV); + + // Unpack H-basis + float3 h0 = float3(lightmap0.x, lightmap1.x, lightmap2.x); + float3 h1 = float3(lightmap0.y, lightmap1.y, lightmap2.y); + float3 h2 = float3(lightmap0.z, lightmap1.z, lightmap2.z); + float3 h3 = float3(lightmap0.w, lightmap1.w, lightmap2.w); + + // Sample baked diffuse irradiance from the H-basis coefficients + float3 normal = material.TangentNormal; +#if MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE + normal *= material.TangentNormal; +#endif + return GetHBasisIrradiance(normal, h0, h1, h2, h3) / PI; +} + +#endif +@5// Lightmap: Shaders \ No newline at end of file diff --git a/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl b/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl new file mode 100644 index 000000000..c5ebbba92 --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl @@ -0,0 +1,294 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +@0// Tessellation: Defines +@1// Tessellation: Includes +@2// Tessellation: Constants +@3// Tessellation: Resources +@4// Tessellation: Utilities +@5// Tessellation: Shaders +#if USE_TESSELLATION + +// Interpolants passed from the hull shader to the domain shader +struct TessalationHSToDS +{ + float4 Position : SV_Position; + float3 WorldPosition : TEXCOORD0; + float2 TexCoord : TEXCOORD1; + float2 LightmapUV : TEXCOORD2; +#if USE_VERTEX_COLOR + half4 VertexColor : COLOR; +#endif + float3 WorldNormal : TEXCOORD3; + float4 WorldTangent : TEXCOORD4; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; +#endif + float3 InstanceOrigin : TEXCOORD6; + float2 InstanceParams : TEXCOORD7; +#if IS_MOTION_VECTORS_PASS + float3 PrevWorldPosition : TEXCOORD8; +#endif + float TessellationMultiplier : TESS; +}; + +// Interpolants passed from the domain shader and to the pixel shader +struct TessalationDSToPS +{ + float4 Position : SV_Position; + float3 WorldPosition : TEXCOORD0; + float2 TexCoord : TEXCOORD1; + float2 LightmapUV : TEXCOORD2; +#if USE_VERTEX_COLOR + half4 VertexColor : COLOR; +#endif + float3 WorldNormal : TEXCOORD3; + float4 WorldTangent : TEXCOORD4; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; +#endif + float3 InstanceOrigin : TEXCOORD6; + float2 InstanceParams : TEXCOORD7; +#if IS_MOTION_VECTORS_PASS + float3 PrevWorldPosition : TEXCOORD8; +#endif +}; + +MaterialInput GetMaterialInput(TessalationDSToPS input) +{ + MaterialInput result = (MaterialInput)0; + result.WorldPosition = input.WorldPosition; + result.TexCoord = input.TexCoord; +#if USE_LIGHTMAP + result.LightmapUV = input.LightmapUV; +#endif +#if USE_VERTEX_COLOR + result.VertexColor = input.VertexColor; +#endif + result.TBN = CalcTangentBasis(input.WorldNormal, input.WorldTangent); + result.TwoSidedSign = WorldDeterminantSign; + result.InstanceOrigin = input.InstanceOrigin; + result.InstanceParams = input.InstanceParams; + result.SvPosition = input.Position; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + result.CustomVSToPS = input.CustomVSToPS; +#endif + return result; +} + +struct TessalationPatch +{ + float EdgeTessFactor[3] : SV_TessFactor; + float InsideTessFactor : SV_InsideTessFactor; +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN + float3 B210 : POSITION4; + float3 B120 : POSITION5; + float3 B021 : POSITION6; + float3 B012 : POSITION7; + float3 B102 : POSITION8; + float3 B201 : POSITION9; + float3 B111 : CENTER; +#endif +}; + +TessalationPatch HS_PatchConstant(InputPatch input) +{ + TessalationPatch output; + + // Average tess factors along edges, and pick an edge tess factor for the interior tessellation + float4 tessellationMultipliers; + tessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier); + tessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier); + tessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier); + tessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier); + tessellationMultipliers = clamp(tessellationMultipliers, 1, MAX_TESSELLATION_FACTOR); + + output.EdgeTessFactor[0] = tessellationMultipliers.x; // 1->2 edge + output.EdgeTessFactor[1] = tessellationMultipliers.y; // 2->0 edge + output.EdgeTessFactor[2] = tessellationMultipliers.z; // 0->1 edge + output.InsideTessFactor = tessellationMultipliers.w; + +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN + // Calculate PN Triangle control points + // Reference: [Vlachos 2001] + float3 p1 = input[0].WorldPosition; + float3 p2 = input[1].WorldPosition; + float3 p3 = input[2].WorldPosition; + float3 n1 = input[0].WorldNormal; + float3 n2 = input[1].WorldNormal; + float3 n3 = input[2].WorldNormal; + output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f; + output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f; + output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f; + output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f; + output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f; + output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f; + float3 e = (output.B210 + output.B120 + output.B021 + + output.B012 + output.B102 + output.B201) / 6.0f; + float3 v = (p1 + p2 + p3) / 3.0f; + output.B111 = e + ((e - v) / 2.0f); +#endif + + return output; +} + +META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5) +META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) +META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) +META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS) +[domain("tri")] +[partitioning("fractional_odd")] +[outputtopology("triangle_cw")] +[maxtessfactor(MAX_TESSELLATION_FACTOR)] +[outputcontrolpoints(3)] +[patchconstantfunc("HS_PatchConstant")] +TessalationHSToDS HS(InputPatch input, uint ControlPointID : SV_OutputControlPointID) +{ + TessalationHSToDS output; + + // Pass through shader +#define COPY(thing) output.thing = input[ControlPointID].thing; + COPY(Position); + COPY(WorldPosition); + COPY(TexCoord); + COPY(LightmapUV); +#if USE_VERTEX_COLOR + COPY(VertexColor); +#endif + COPY(WorldNormal); + COPY(WorldTangent); + COPY(InstanceOrigin); + COPY(InstanceParams); +#if IS_MOTION_VECTORS_PASS + COPY(PrevWorldPosition); +#endif + COPY(TessellationMultiplier); +#if USE_CUSTOM_VERTEX_INTERPOLATORS + COPY(CustomVSToPS); +#endif +#undef COPY + + return output; +} + +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG + +// Orthogonal projection on to plane +float3 ProjectOntoPlane(float3 planeNormal, float3 planePosition, float3 pointToProject) +{ + return pointToProject - dot(pointToProject - planePosition, planeNormal) * planeNormal; +} + +#endif + +META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5) +META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) +META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) +[domain("tri")] +TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch input) +{ + TessalationDSToPS output; + + // Get the barycentric coords + float U = barycentricCoords.x; + float V = barycentricCoords.y; + float W = barycentricCoords.z; + + // Interpolate patch attributes to generated vertices +#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing +#define COPY(thing) output.thing = input[0].thing + INTERPOLATE(Position); +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN + float UU = U * U; + float VV = V * V; + float WW = W * W; + float UU3 = UU * 3.0f; + float VV3 = VV * 3.0f; + float WW3 = WW * 3.0f; + + // Interpolate using barycentric coordinates and PN Triangle control points + output.WorldPosition = + input[0].WorldPosition * UU * U + + input[1].WorldPosition * VV * V + + input[2].WorldPosition * WW * W + + constantData.B210 * UU3 * V + + constantData.B120 * VV3 * U + + constantData.B021 * VV3 * W + + constantData.B012 * WW3 * V + + constantData.B102 * WW3 * U + + constantData.B201 * UU3 * W + + constantData.B111 * 6.0f * W * U * V; +#if IS_MOTION_VECTORS_PASS + output.PrevWorldPosition = + input[0].PrevWorldPosition * UU * U + + input[1].PrevWorldPosition * VV * V + + input[2].PrevWorldPosition * WW * W + + constantData.B210 * UU3 * V + + constantData.B120 * VV3 * U + + constantData.B021 * VV3 * W + + constantData.B012 * WW3 * V + + constantData.B102 * WW3 * U + + constantData.B201 * UU3 * W + + constantData.B111 * 6.0f * W * U * V; +#endif +#else + INTERPOLATE(WorldPosition); +#if IS_MOTION_VECTORS_PASS + INTERPOLATE(PrevWorldPosition); +#endif +#endif + INTERPOLATE(TexCoord); + INTERPOLATE(LightmapUV); +#if USE_VERTEX_COLOR + INTERPOLATE(VertexColor); +#endif + INTERPOLATE(WorldNormal); + INTERPOLATE(WorldTangent); + COPY(InstanceOrigin); + COPY(InstanceParams); +#if USE_CUSTOM_VERTEX_INTERPOLATORS + UNROLL + for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++) + { + INTERPOLATE(CustomVSToPS[i]); + } +#endif +#undef INTERPOLATE +#undef COPY + + // Interpolating tangents can unnormalize it, so normalize it + output.WorldNormal = normalize(output.WorldNormal); + output.WorldTangent.xyz = normalize(output.WorldTangent.xyz); + +#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG + // Orthogonal projection in the tangent planes + float3 posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].WorldPosition, output.WorldPosition); + float3 posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].WorldPosition, output.WorldPosition); + float3 posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].WorldPosition, output.WorldPosition); + + // Interpolate the projected points + output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; +#if IS_MOTION_VECTORS_PASS + posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].PrevWorldPosition, output.PrevWorldPosition); + posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].PrevWorldPosition, output.PrevWorldPosition); + posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].PrevWorldPosition, output.PrevWorldPosition); + output.PrevWorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; +#endif +#endif + + // Perform displacement mapping +#if USE_DISPLACEMENT + MaterialInput materialInput = GetMaterialInput(output); + Material material = GetMaterialDS(materialInput); + output.WorldPosition += material.WorldDisplacement; +#if IS_MOTION_VECTORS_PASS + output.PrevWorldPosition += material.WorldDisplacement; +#endif +#endif + + // Recalculate the clip space position + output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); + + return output; +} + +#endif diff --git a/Content/Editor/MaterialTemplates/GUI.shader b/Content/Editor/MaterialTemplates/GUI.shader index 1457a0bc3..08fddde79 100644 --- a/Content/Editor/MaterialTemplates/GUI.shader +++ b/Content/Editor/MaterialTemplates/GUI.shader @@ -185,6 +185,8 @@ float4 GetVertexColor(MaterialInput input) #endif } +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -257,3 +259,5 @@ float4 PS_GUI(PixelInput input) : SV_Target0 return float4(material.Emissive, material.Opacity); } + +@9 diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index 5fd161645..e8d00d1db 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -312,6 +312,8 @@ float3 TransformParticleVector(float3 input) return mul(float4(input, 0.0f), WorldMatrixInverseTransposed).xyz; } +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -865,3 +867,5 @@ void PS_Depth(PixelInput input OutColor = 0; #endif } + +@9 diff --git a/Content/Editor/MaterialTemplates/PostProcess.shader b/Content/Editor/MaterialTemplates/PostProcess.shader index dad9795af..a3e5e42c7 100644 --- a/Content/Editor/MaterialTemplates/PostProcess.shader +++ b/Content/Editor/MaterialTemplates/PostProcess.shader @@ -128,6 +128,8 @@ float4 GetVertexColor(MaterialInput input) return 1; } +@8 + // Get material properties function (for pixel shader) Material GetMaterialPS(MaterialInput input) { @@ -147,3 +149,5 @@ float4 PS_PostFx(PixelInput input) : SV_Target0 return float4(material.Emissive, material.Opacity); } + +@9 diff --git a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader index 6f393464b..303cb2fee 100644 --- a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader +++ b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader @@ -3,7 +3,6 @@ #define MATERIAL 1 @3 - #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" #include "./Flax/GBufferCommon.hlsl" @@ -21,7 +20,6 @@ float3 ViewDir; float TimeParam; float4 ViewInfo; float4 ScreenSize; -float4 LightmapArea; float3 WorldInvScale; float WorldDeterminantSign; float2 Dummy0; @@ -32,18 +30,8 @@ float3 GeometrySize; float Dummy1; @1META_CB_END -#if CAN_USE_LIGHTMAP - -// Irradiance and directionality prebaked lightmaps -Texture2D Lightmap0 : register(t0); -Texture2D Lightmap1 : register(t1); -Texture2D Lightmap2 : register(t2); - -#endif - // Material shader resources @2 - // Interpolants passed from the vertex shader struct VertexOutput { @@ -322,6 +310,8 @@ float4 GetVertexColor(MaterialInput input) #endif } +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -613,320 +603,6 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) #endif -#if USE_TESSELLATION - -// Interpolants passed from the hull shader to the domain shader -struct TessalationHSToDS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; -#if IS_MOTION_VECTORS_PASS - float3 PrevWorldPosition : TEXCOORD8; -#endif - float TessellationMultiplier : TESS; -}; - -// Interpolants passed from the domain shader and to the pixel shader -struct TessalationDSToPS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; -#if IS_MOTION_VECTORS_PASS - float3 PrevWorldPosition : TEXCOORD8; -#endif -}; - -MaterialInput GetMaterialInput(TessalationDSToPS input) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = input.VertexColor; -#endif - result.TBN = CalcTangentBasis(input.WorldNormal, input.WorldTangent); - result.TwoSidedSign = WorldDeterminantSign; - result.InstanceOrigin = input.InstanceOrigin; - result.InstanceParams = input.InstanceParams; - result.SvPosition = input.Position; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; -#endif - return result; -} - -struct TessalationPatch -{ - float EdgeTessFactor[3] : SV_TessFactor; - float InsideTessFactor : SV_InsideTessFactor; -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float3 B210 : POSITION4; - float3 B120 : POSITION5; - float3 B021 : POSITION6; - float3 B012 : POSITION7; - float3 B102 : POSITION8; - float3 B201 : POSITION9; - float3 B111 : CENTER; -#endif -}; - -TessalationPatch HS_PatchConstant(InputPatch input) -{ - TessalationPatch output; - - // Average tess factors along edges, and pick an edge tess factor for the interior tessellation - float4 tessellationMultipliers; - tessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier); - tessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier); - tessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier); - tessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier); - tessellationMultipliers = clamp(tessellationMultipliers, 1, MAX_TESSELLATION_FACTOR); - - output.EdgeTessFactor[0] = tessellationMultipliers.x; // 1->2 edge - output.EdgeTessFactor[1] = tessellationMultipliers.y; // 2->0 edge - output.EdgeTessFactor[2] = tessellationMultipliers.z; // 0->1 edge - output.InsideTessFactor = tessellationMultipliers.w; - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - // Calculate PN-Triangle coefficients - // Refer to Vlachos 2001 for the original formula - float3 p1 = input[0].WorldPosition; - float3 p2 = input[1].WorldPosition; - float3 p3 = input[2].WorldPosition; - float3 n1 = input[0].WorldNormal; - float3 n2 = input[1].WorldNormal; - float3 n3 = input[2].WorldNormal; - - // Calculate control points - output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f; - output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f; - output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f; - output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f; - output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f; - output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f; - float3 e = (output.B210 + output.B120 + output.B021 + - output.B012 + output.B102 + output.B201) / 6.0f; - float3 v = (p1 + p2 + p3) / 3.0f; - output.B111 = e + ((e - v) / 2.0f); -#endif - - return output; -} - -META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) -META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS) -[domain("tri")] -[partitioning("fractional_odd")] -[outputtopology("triangle_cw")] -[maxtessfactor(MAX_TESSELLATION_FACTOR)] -[outputcontrolpoints(3)] -[patchconstantfunc("HS_PatchConstant")] -TessalationHSToDS HS(InputPatch input, uint ControlPointID : SV_OutputControlPointID) -{ - TessalationHSToDS output; - - // Pass through shader -#define COPY(thing) output.thing = input[ControlPointID].thing; - COPY(Position); - COPY(WorldPosition); - COPY(TexCoord); - COPY(LightmapUV); -#if USE_VERTEX_COLOR - COPY(VertexColor); -#endif - COPY(WorldNormal); - COPY(WorldTangent); - COPY(InstanceOrigin); - COPY(InstanceParams); -#if IS_MOTION_VECTORS_PASS - COPY(PrevWorldPosition); -#endif - COPY(TessellationMultiplier); -#if USE_CUSTOM_VERTEX_INTERPOLATORS - COPY(CustomVSToPS); -#endif -#undef COPY - - return output; -} - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - -// Orthogonal projection on to plane -float3 ProjectOntoPlane(float3 planeNormal, float3 planePoint, float3 pointToProject) -{ - return pointToProject - dot(pointToProject-planePoint, planeNormal) * planeNormal; -} - -#endif - -META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) -[domain("tri")] -TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch input) -{ - TessalationDSToPS output; - - // Get the barycentric coords - float U = barycentricCoords.x; - float V = barycentricCoords.y; - float W = barycentricCoords.z; - - // Interpolate patch attributes to generated vertices -#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing -#define COPY(thing) output.thing = input[0].thing - INTERPOLATE(Position); -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float UU = U * U; - float VV = V * V; - float WW = W * W; - float UU3 = UU * 3.0f; - float VV3 = VV * 3.0f; - float WW3 = WW * 3.0f; - - // Interpolate using barycentric coordinates and PN Triangle control points - output.WorldPosition = - input[0].WorldPosition * UU * U + - input[1].WorldPosition * VV * V + - input[2].WorldPosition * WW * W + - constantData.B210 * UU3 * V + - constantData.B120 * VV3 * U + - constantData.B021 * VV3 * W + - constantData.B012 * WW3 * V + - constantData.B102 * WW3 * U + - constantData.B201 * UU3 * W + - constantData.B111 * 6.0f * W * U * V; -#if IS_MOTION_VECTORS_PASS - output.PrevWorldPosition = - input[0].PrevWorldPosition * UU * U + - input[1].PrevWorldPosition * VV * V + - input[2].PrevWorldPosition * WW * W + - constantData.B210 * UU3 * V + - constantData.B120 * VV3 * U + - constantData.B021 * VV3 * W + - constantData.B012 * WW3 * V + - constantData.B102 * WW3 * U + - constantData.B201 * UU3 * W + - constantData.B111 * 6.0f * W * U * V; -#endif -#else - INTERPOLATE(WorldPosition); -#if IS_MOTION_VECTORS_PASS - INTERPOLATE(PrevWorldPosition); -#endif -#endif - INTERPOLATE(TexCoord); - INTERPOLATE(LightmapUV); -#if USE_VERTEX_COLOR - INTERPOLATE(VertexColor); -#endif - INTERPOLATE(WorldNormal); - INTERPOLATE(WorldTangent); - COPY(InstanceOrigin); - COPY(InstanceParams); -#if USE_CUSTOM_VERTEX_INTERPOLATORS - UNROLL - for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++) - { - INTERPOLATE(CustomVSToPS[i]); - } -#endif -#undef INTERPOLATE -#undef COPY - - // Interpolating tangents can unnormalize it, so normalize it - output.WorldNormal = normalize(output.WorldNormal); - output.WorldTangent.xyz = normalize(output.WorldTangent.xyz); - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - // Orthogonal projection in the tangent planes - float3 posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].WorldPosition, output.WorldPosition); - float3 posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].WorldPosition, output.WorldPosition); - float3 posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].WorldPosition, output.WorldPosition); - - // Interpolate the projected points - output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#if IS_MOTION_VECTORS_PASS - posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].PrevWorldPosition, output.PrevWorldPosition); - posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].PrevWorldPosition, output.PrevWorldPosition); - posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].PrevWorldPosition, output.PrevWorldPosition); - output.PrevWorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#endif -#endif - - // Perform displacement mapping -#if USE_DISPLACEMENT - MaterialInput materialInput = GetMaterialInput(output); - Material material = GetMaterialDS(materialInput); - output.WorldPosition += material.WorldDisplacement; -#if IS_MOTION_VECTORS_PASS - output.PrevWorldPosition += material.WorldDisplacement; -#endif -#endif - - // Recalculate the clip space position - output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); - - return output; -} - -#endif - -#if USE_LIGHTMAP - -float3 SampleLightmap(Material material, MaterialInput materialInput) -{ - // Sample lightmaps - float4 lightmap0 = Lightmap0.Sample(SamplerLinearClamp, materialInput.LightmapUV); - float4 lightmap1 = Lightmap1.Sample(SamplerLinearClamp, materialInput.LightmapUV); - float4 lightmap2 = Lightmap2.Sample(SamplerLinearClamp, materialInput.LightmapUV); - - // Unpack H-basis - float3 h0 = float3(lightmap0.x, lightmap1.x, lightmap2.x); - float3 h1 = float3(lightmap0.y, lightmap1.y, lightmap2.y); - float3 h2 = float3(lightmap0.z, lightmap1.z, lightmap2.z); - float3 h3 = float3(lightmap0.w, lightmap1.w, lightmap2.w); - - // Sample baked diffuse irradiance from the H-basis coefficients - float3 normal = material.TangentNormal; -#if MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE - normal *= material.TangentNormal; -#endif - return GetHBasisIrradiance(normal, h0, h1, h2, h3) / PI; -} - -#endif - #if USE_DITHERED_LOD_TRANSITION void ClipLODTransition(PixelInput input) @@ -1081,3 +757,5 @@ float4 PS_MotionVectors(PixelInput input) : SV_Target0 return float4(0, 0, 0, 1); #endif } + +@9 diff --git a/Content/Editor/MaterialTemplates/SurfaceForward.shader b/Content/Editor/MaterialTemplates/SurfaceForward.shader index 7a97e5e5a..9ce0a5f5a 100644 --- a/Content/Editor/MaterialTemplates/SurfaceForward.shader +++ b/Content/Editor/MaterialTemplates/SurfaceForward.shader @@ -29,7 +29,6 @@ float3 ViewDir; float TimeParam; float4 ViewInfo; float4 ScreenSize; -float4 LightmapArea; float3 WorldInvScale; float WorldDeterminantSign; float2 Dummy0; @@ -319,6 +318,8 @@ float4 GetVertexColor(MaterialInput input) #endif } +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -387,12 +388,11 @@ VertexOutput VS(ModelInput input) #if USE_VERTEX_COLOR output.VertexColor = input.Color; #endif + output.LightmapUV = input.LightmapUV; #if USE_INSTANCING - output.LightmapUV = input.LightmapUV * input.InstanceLightmapArea.zw + input.InstanceLightmapArea.xy; output.InstanceOrigin = world[3].xyz; output.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); #else - output.LightmapUV = input.LightmapUV * LightmapArea.zw + LightmapArea.xy; output.InstanceOrigin = WorldMatrix[3].xyz; output.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); #endif @@ -576,256 +576,6 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) #endif -#if USE_TESSELLATION - -// Interpolants passed from the hull shader to the domain shader -struct TessalationHSToDS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3x3 TBN : TEXCOORD3; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; - float TessellationMultiplier : TESS; -}; - -// Interpolants passed from the domain shader and to the pixel shader -struct TessalationDSToPS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3x3 TBN : TEXCOORD3; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; -}; - -MaterialInput GetMaterialInput(TessalationDSToPS input) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = input.VertexColor; -#endif - result.TBN = input.TBN; - result.TwoSidedSign = WorldDeterminantSign; - result.InstanceOrigin = input.InstanceOrigin; - result.InstanceParams = input.InstanceParams; - result.SvPosition = input.Position; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; -#endif - return result; -} - -struct TessalationPatch -{ - float EdgeTessFactor[3] : SV_TessFactor; - float InsideTessFactor : SV_InsideTessFactor; -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float3 B210 : POSITION4; - float3 B120 : POSITION5; - float3 B021 : POSITION6; - float3 B012 : POSITION7; - float3 B102 : POSITION8; - float3 B201 : POSITION9; - float3 B111 : CENTER; -#endif -}; - -TessalationPatch HS_PatchConstant(InputPatch input) -{ - TessalationPatch output; - - // Average tess factors along edges, and pick an edge tess factor for the interior tessellation - float4 tessellationMultipliers; - tessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier); - tessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier); - tessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier); - tessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier); - tessellationMultipliers = clamp(tessellationMultipliers, 1, MAX_TESSELLATION_FACTOR); - - output.EdgeTessFactor[0] = tessellationMultipliers.x; // 1->2 edge - output.EdgeTessFactor[1] = tessellationMultipliers.y; // 2->0 edge - output.EdgeTessFactor[2] = tessellationMultipliers.z; // 0->1 edge - output.InsideTessFactor = tessellationMultipliers.w; - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - // Calculate PN-Triangle coefficients - // Refer to Vlachos 2001 for the original formula - float3 p1 = input[0].WorldPosition; - float3 p2 = input[1].WorldPosition; - float3 p3 = input[2].WorldPosition; - float3 n1 = input[0].TBN[2]; - float3 n2 = input[1].TBN[2]; - float3 n3 = input[2].TBN[2]; - - // Calculate control points - output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f; - output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f; - output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f; - output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f; - output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f; - output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f; - float3 e = (output.B210 + output.B120 + output.B021 + - output.B012 + output.B102 + output.B201) / 6.0f; - float3 v = (p1 + p2 + p3) / 3.0f; - output.B111 = e + ((e - v) / 2.0f); -#endif - - return output; -} - -META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS) -[domain("tri")] -[partitioning("fractional_odd")] -[outputtopology("triangle_cw")] -[maxtessfactor(MAX_TESSELLATION_FACTOR)] -[outputcontrolpoints(3)] -[patchconstantfunc("HS_PatchConstant")] -TessalationHSToDS HS(InputPatch input, uint ControlPointID : SV_OutputControlPointID) -{ - TessalationHSToDS output; - - // Pass through shader -#define COPY(thing) output.thing = input[ControlPointID].thing; - COPY(Position); - COPY(WorldPosition); - COPY(TexCoord); - COPY(LightmapUV); -#if USE_VERTEX_COLOR - COPY(VertexColor); -#endif - COPY(TBN); - COPY(InstanceOrigin); - COPY(InstanceParams); - COPY(TessellationMultiplier); -#if USE_CUSTOM_VERTEX_INTERPOLATORS - COPY(CustomVSToPS); -#endif -#undef COPY - - return output; -} - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - -// Orthogonal projection on to plane -float3 ProjectOntoPlane(float3 planeNormal, float3 planePoint, float3 pointToProject) -{ - return pointToProject - dot(pointToProject-planePoint, planeNormal) * planeNormal; -} - -#endif - -META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -[domain("tri")] -TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch input) -{ - TessalationDSToPS output; - - // Get the barycentric coords - float U = barycentricCoords.x; - float V = barycentricCoords.y; - float W = barycentricCoords.z; - - // Interpolate patch attributes to generated vertices -#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing -#define COPY(thing) output.thing = input[0].thing - INTERPOLATE(Position); -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float UU = U * U; - float VV = V * V; - float WW = W * W; - float UU3 = UU * 3.0f; - float VV3 = VV * 3.0f; - float WW3 = WW * 3.0f; - - // Interpolate using barycentric coordinates and PN Triangle control points - output.WorldPosition = - input[0].WorldPosition * UU * U + - input[1].WorldPosition * VV * V + - input[2].WorldPosition * WW * W + - constantData.B210 * UU3 * V + - constantData.B120 * VV3 * U + - constantData.B021 * VV3 * W + - constantData.B012 * WW3 * V + - constantData.B102 * WW3 * U + - constantData.B201 * UU3 * W + - constantData.B111 * 6.0f * W * U * V; -#else - INTERPOLATE(WorldPosition); -#endif - INTERPOLATE(TexCoord); - INTERPOLATE(LightmapUV); -#if USE_VERTEX_COLOR - INTERPOLATE(VertexColor); -#endif - INTERPOLATE(TBN[0]); - INTERPOLATE(TBN[1]); - INTERPOLATE(TBN[2]); - COPY(InstanceOrigin); - COPY(InstanceParams); -#if USE_CUSTOM_VERTEX_INTERPOLATORS - UNROLL - for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++) - { - INTERPOLATE(CustomVSToPS[i]); - } -#endif -#undef INTERPOLATE -#undef COPY - - // Interpolating normal can unnormalize it, so normalize it - output.TBN[0] = normalize(output.TBN[0]); - output.TBN[1] = normalize(output.TBN[1]); - output.TBN[2] = normalize(output.TBN[2]); - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - // Orthogonal projection in the tangent planes - float3 posProjectedU = ProjectOntoPlane(input[0].TBN[2], input[0].WorldPosition, output.WorldPosition); - float3 posProjectedV = ProjectOntoPlane(input[1].TBN[2], input[1].WorldPosition, output.WorldPosition); - float3 posProjectedW = ProjectOntoPlane(input[2].TBN[2], input[2].WorldPosition, output.WorldPosition); - - // Interpolate the projected points - output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#endif - - // Perform displacement mapping -#if USE_DISPLACEMENT - MaterialInput materialInput = GetMaterialInput(output); - Material material = GetMaterialDS(materialInput); - output.WorldPosition += material.WorldDisplacement; -#endif - - // Recalculate the clip space position - output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); - - return output; -} - -#endif - #if USE_DITHERED_LOD_TRANSITION void ClipLODTransition(PixelInput input) @@ -1009,3 +759,5 @@ void PS_Depth(PixelInput input OutColor = 0; #endif } + +@9 diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index 03a2081bf..0a6cb8028 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -39,15 +39,11 @@ float2 OffsetUV; float2 Dummy0; @1META_CB_END -#if CAN_USE_LIGHTMAP - // Irradiance and directionality prebaked lightmaps Texture2D Lightmap0 : register(t0); Texture2D Lightmap1 : register(t1); Texture2D Lightmap2 : register(t2); -#endif - // Terrain data Texture2D Heightmap : register(t3); Texture2D Splatmap0 : register(t4); @@ -216,6 +212,22 @@ float4 GetVertexColor(MaterialInput input) return 1; } +// Evaluates the H-Basis coefficients in the tangent space normal direction +float3 GetHBasisIrradiance(float3 n, float3 h0, float3 h1, float3 h2, float3 h3) +{ + // Band 0 + float3 color = h0 * (1.0f / sqrt(2.0f * PI)); + + // Band 1 + color += h1 * -sqrt(1.5f / PI) * n.y; + color += h2 * sqrt(1.5f / PI) * (2 * n.z - 1.0f); + color += h3 * -sqrt(1.5f / PI) * n.x; + + return color; +} + +@8 + // Get material properties function (for vertex shader) Material GetMaterialVS(MaterialInput input) { @@ -781,3 +793,5 @@ void PS_Depth(PixelInput input OutColor = 0; #endif } + +@9 diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 571e46243..966253aba 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -458,7 +458,6 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] }); options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrain && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] }); options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] }); - options.Macros.Add({ "CAN_USE_LIGHTMAP", Numbers[isSurfaceOrTerrain ? 1 : 0] }); #endif } diff --git a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp index c5166a088..27e9c7b38 100644 --- a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp @@ -44,7 +44,7 @@ void DecalMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(DecalMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(DecalMaterialShaderData) : nullptr; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = true; diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index 5a460079d..322726a3e 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "DeferredMaterialShader.h" +#include "MaterialShaderFeatures.h" #include "MaterialParams.h" #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Graphics/RenderView.h" @@ -28,7 +29,6 @@ PACK_STRUCT(struct DeferredMaterialShaderData { float TimeParam; Vector4 ViewInfo; Vector4 ScreenSize; - Rectangle LightmapArea; Vector3 WorldInvScale; float WorldDeterminantSign; Vector2 Dummy0; @@ -63,11 +63,21 @@ void DeferredMaterialShader::Bind(BindParameters& params) auto& drawCall = *params.FirstDrawCall; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0 && cb0->GetSize() != 0; + ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing + byte* cb = _cb0Data.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(DeferredMaterialShaderData); + int32 srv = 0; + + // Setup features + if (_info.TessellationMode != TessellationMethod::None) + TessellationFeature::Bind(params, cb, srv); + const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(DeferredMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = false; @@ -75,7 +85,6 @@ void DeferredMaterialShader::Bind(BindParameters& params) MaterialParams::Bind(params.ParamsLink, bindMeta); // Setup material constants data - auto materialData = reinterpret_cast(_cb0Data.Get()); if (hasCb0) { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); @@ -107,23 +116,6 @@ void DeferredMaterialShader::Bind(BindParameters& params) materialData->TemporalAAJitter = view.TemporalAAJitter; materialData->GeometrySize = drawCall.Surface.GeometrySize; } - const bool useLightmap = view.Flags & ViewFlags::GI -#if USE_EDITOR - && EnableLightmapsUsage -#endif - && drawCall.Surface.Lightmap != nullptr; - if (useLightmap) - { - // Bind lightmap textures - GPUTexture *lightmap0, *lightmap1, *lightmap2; - drawCall.Surface.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); - context->BindSR(0, lightmap0); - context->BindSR(1, lightmap1); - context->BindSR(2, lightmap2); - - // Set lightmap data - materialData->LightmapArea = drawCall.Surface.LightmapUVsArea; - } // Check if is using mesh skinning const bool useSkinning = drawCall.Surface.Skinning != nullptr; diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index 39e39a7d4..7dc68a85a 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "ForwardMaterialShader.h" +#include "MaterialShaderFeatures.h" #include "MaterialParams.h" #include "Engine/Engine/Time.h" #include "Engine/Graphics/GPULimits.h" @@ -29,7 +30,6 @@ PACK_STRUCT(struct ForwardMaterialShaderData { float TimeParam; Vector4 ViewInfo; Vector4 ScreenSize; - Rectangle LightmapArea; Vector3 WorldInvScale; float WorldDeterminantSign; Vector2 Dummy0; @@ -70,13 +70,22 @@ void ForwardMaterialShader::Bind(BindParameters& params) auto& drawCall = *params.FirstDrawCall; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0 && cb0->GetSize() != 0; + ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing const auto cb1 = _shader->GetCB(1); const bool hasCb1 = cb1 && cb1->GetSize() != 0; + byte* cb = _cb0Data.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(ForwardMaterialShaderData); + int32 srv = 0; + + // Setup features + if (_info.TessellationMode != TessellationMethod::None) + TessellationFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(ForwardMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = nullptr; // forward pass materials cannot sample scene color for now bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth; @@ -93,7 +102,6 @@ void ForwardMaterialShader::Bind(BindParameters& params) } // Setup material constants data - const auto materialData = reinterpret_cast(_cb0Data.Get()); if (hasCb0) { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); diff --git a/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp b/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp index 4b079dd54..23b327a1b 100644 --- a/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp @@ -36,7 +36,7 @@ void GUIMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(GUIMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(GUIMaterialShaderData) : nullptr; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = false; diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index f1e7856af..e85328133 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -29,7 +29,6 @@ public: /// /// Determines whether material is a surface shader. /// - /// true if material is surface shader; otherwise, false. FORCE_INLINE bool IsSurface() const { return GetInfo().Domain == MaterialDomain::Surface; @@ -38,7 +37,6 @@ public: /// /// Determines whether material is a post fx. /// - /// true if material is post fx; otherwise, false. FORCE_INLINE bool IsPostFx() const { return GetInfo().Domain == MaterialDomain::PostProcess; @@ -47,7 +45,6 @@ public: /// /// Determines whether material is a decal. /// - /// true if material is decal; otherwise, false. FORCE_INLINE bool IsDecal() const { return GetInfo().Domain == MaterialDomain::Decal; @@ -56,7 +53,6 @@ public: /// /// Determines whether material is a GUI shader. /// - /// true if material is GUI shader; otherwise, false. FORCE_INLINE bool IsGUI() const { return GetInfo().Domain == MaterialDomain::GUI; @@ -65,7 +61,6 @@ public: /// /// Determines whether material is a terrain shader. /// - /// true if material is terrain shader; otherwise, false. FORCE_INLINE bool IsTerrain() const { return GetInfo().Domain == MaterialDomain::Terrain; @@ -74,7 +69,6 @@ public: /// /// Determines whether material is a particle shader. /// - /// true if material is particle shader; otherwise, false. FORCE_INLINE bool IsParticle() const { return GetInfo().Domain == MaterialDomain::Particle; diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index 1f1d1bac6..ea70d4587 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -230,36 +230,28 @@ void MaterialParameter::Bind(BindMeta& meta) const switch (_type) { case MaterialParameterType::Bool: - if (meta.Buffer0) - *((int32*)(meta.Buffer0 + _offset)) = _asBool; + *((int32*)(meta.Constants + _offset)) = _asBool; break; case MaterialParameterType::Integer: - if (meta.Buffer0) - *((int32*)(meta.Buffer0 + _offset)) = _asInteger; + *((int32*)(meta.Constants + _offset)) = _asInteger; break; case MaterialParameterType::Float: - if (meta.Buffer0) - *((float*)(meta.Buffer0 + _offset)) = _asFloat; + *((float*)(meta.Constants + _offset)) = _asFloat; break; case MaterialParameterType::Vector2: - if (meta.Buffer0) - *((Vector2*)(meta.Buffer0 + _offset)) = _asVector2; + *((Vector2*)(meta.Constants + _offset)) = _asVector2; break; case MaterialParameterType::Vector3: - if (meta.Buffer0) - *((Vector3*)(meta.Buffer0 + _offset)) = _asVector3; + *((Vector3*)(meta.Constants + _offset)) = _asVector3; break; case MaterialParameterType::Vector4: - if (meta.Buffer0) - *((Vector4*)(meta.Buffer0 + _offset)) = _asVector4; + *((Vector4*)(meta.Constants + _offset)) = _asVector4; break; case MaterialParameterType::Color: - if (meta.Buffer0) - *((Color*)(meta.Buffer0 + _offset)) = _asColor; + *((Color*)(meta.Constants + _offset)) = _asColor; break; case MaterialParameterType::Matrix: - if (meta.Buffer0) - Matrix::Transpose(_asMatrix, *(Matrix*)(meta.Buffer0 + _offset)); + Matrix::Transpose(_asMatrix, *(Matrix*)(meta.Constants + _offset)); break; case MaterialParameterType::NormalMap: { @@ -336,11 +328,10 @@ void MaterialParameter::Bind(BindMeta& meta) const break; } case MaterialParameterType::ChannelMask: - if (meta.Buffer0) - *((Vector4*)(meta.Buffer0 + _offset)) = Vector4(_asInteger == 0, _asInteger == 1, _asInteger == 2, _asInteger == 3); + *((Vector4*)(meta.Constants + _offset)) = Vector4(_asInteger == 0, _asInteger == 1, _asInteger == 2, _asInteger == 3); break; case MaterialParameterType::GameplayGlobal: - if (meta.Buffer0 && _asAsset) + if (_asAsset) { const auto e = _asAsset.As()->Variables.TryGet(_name); if (e) @@ -348,26 +339,26 @@ void MaterialParameter::Bind(BindMeta& meta) const switch (e->Value.Type.Type) { case VariantType::Bool: - *((bool*)(meta.Buffer0 + _offset)) = e->Value.AsBool; + *((bool*)(meta.Constants + _offset)) = e->Value.AsBool; break; case VariantType::Int: - *((int32*)(meta.Buffer0 + _offset)) = e->Value.AsInt; + *((int32*)(meta.Constants + _offset)) = e->Value.AsInt; break; case VariantType::Uint: - *((uint32*)(meta.Buffer0 + _offset)) = e->Value.AsUint; + *((uint32*)(meta.Constants + _offset)) = e->Value.AsUint; break; case VariantType::Float: - *((float*)(meta.Buffer0 + _offset)) = e->Value.AsFloat; + *((float*)(meta.Constants + _offset)) = e->Value.AsFloat; break; case VariantType::Vector2: - *((Vector2*)(meta.Buffer0 + _offset)) = e->Value.AsVector2(); + *((Vector2*)(meta.Constants + _offset)) = e->Value.AsVector2(); break; case VariantType::Vector3: - *((Vector3*)(meta.Buffer0 + _offset)) = e->Value.AsVector3(); + *((Vector3*)(meta.Constants + _offset)) = e->Value.AsVector3(); break; case VariantType::Vector4: case VariantType::Color: - *((Vector4*)(meta.Buffer0 + _offset)) = e->Value.AsVector4(); + *((Vector4*)(meta.Constants + _offset)) = e->Value.AsVector4(); break; default: ; } diff --git a/Source/Engine/Graphics/Materials/MaterialParams.h b/Source/Engine/Graphics/Materials/MaterialParams.h index 997b18d34..ca48dc143 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.h +++ b/Source/Engine/Graphics/Materials/MaterialParams.h @@ -309,9 +309,9 @@ public: GPUContext* Context; /// - /// The pointer to the first constants buffer in memory. + /// The pointer to the constants buffer in the memory. /// - byte* Buffer0; + byte* Constants; /// /// The input scene color. It's optional and used in forward/postFx rendering. diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp new file mode 100644 index 000000000..a07e740f8 --- /dev/null +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "MaterialShaderFeatures.h" +#include "Engine/Graphics/RenderTask.h" +#include "Engine/Renderer/RenderList.h" +#include "Engine/Renderer/ShadowsPass.h" +#if USE_EDITOR +#include "Engine/Renderer/Lightmaps.h" +#endif +#include "Engine/Level/Scene/Lightmap.h" +#include "Engine/Level/Actors/EnvironmentProbe.h" + +void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv) +{ + auto context = params.GPUContext; + auto cache = params.RenderContext.List; + auto& view = params.RenderContext.View; + auto& drawCall = *params.FirstDrawCall; + auto& data = *(Data*)cb; + const int32 envProbeShaderRegisterIndex = srv + 0; + const int32 skyLightShaderRegisterIndex = srv + 1; + const int32 dirLightShaderRegisterIndex = srv + 2; + + // Set fog input + if (cache->Fog) + { + cache->Fog->GetExponentialHeightFogData(view, data.ExponentialHeightFog); + } + else + { + data.ExponentialHeightFog.FogMinOpacity = 1.0f; + data.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f; + } + + // Set directional light input + if (cache->DirectionalLights.HasItems()) + { + const auto& dirLight = cache->DirectionalLights.First(); + const auto shadowPass = ShadowsPass::Instance(); + const bool useShadow = shadowPass->LastDirLightIndex == 0; + if (useShadow) + { + data.DirectionalLightShadow = shadowPass->LastDirLight; + context->BindSR(dirLightShaderRegisterIndex, shadowPass->LastDirLightShadowMap); + } + else + { + context->UnBindSR(dirLightShaderRegisterIndex); + } + dirLight.SetupLightData(&data.DirectionalLight, view, useShadow); + } + else + { + data.DirectionalLight.Color = Vector3::Zero; + data.DirectionalLight.CastShadows = 0.0f; + context->UnBindSR(dirLightShaderRegisterIndex); + } + + // Set sky light + if (cache->SkyLights.HasItems()) + { + auto& skyLight = cache->SkyLights.First(); + skyLight.SetupLightData(&data.SkyLight, view, false); + const auto texture = skyLight.Image ? skyLight.Image->GetTexture() : nullptr; + context->BindSR(skyLightShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture)); + } + else + { + Platform::MemoryClear(&data.SkyLight, sizeof(data.SkyLight)); + context->UnBindSR(skyLightShaderRegisterIndex); + } + + // Set reflection probe data + EnvironmentProbe* probe = nullptr; + // TODO: optimize env probe searching for a transparent material - use spatial cache for renderer to find it + for (int32 i = 0; i < cache->EnvironmentProbes.Count(); i++) + { + const auto p = cache->EnvironmentProbes[i]; + if (p->GetSphere().Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) + { + probe = p; + break; + } + } + if (probe && probe->GetProbe()) + { + probe->SetupProbeData(&data.EnvironmentProbe); + const auto texture = probe->GetProbe()->GetTexture(); + context->BindSR(envProbeShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture)); + } + else + { + data.EnvironmentProbe.Data1 = Vector4::Zero; + context->UnBindSR(envProbeShaderRegisterIndex); + } + + // Set local lights + data.LocalLightsCount = 0; + for (int32 i = 0; i < cache->PointLights.Count(); i++) + { + const auto& light = cache->PointLights[i]; + if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) + { + light.SetupLightData(&data.LocalLights[data.LocalLightsCount], view, false); + data.LocalLightsCount++; + if (data.LocalLightsCount == MaxLocalLights) + break; + } + } + for (int32 i = 0; i < cache->SpotLights.Count(); i++) + { + const auto& light = cache->SpotLights[i]; + if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) + { + light.SetupLightData(&data.LocalLights[data.LocalLightsCount], view, false); + data.LocalLightsCount++; + if (data.LocalLightsCount == MaxLocalLights) + break; + } + } + + cb += sizeof(Data); + srv += SRVs; +} + +bool LightmapFeature::Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv) +{ + auto context = params.GPUContext; + auto& view = params.RenderContext.View; + auto& drawCall = *params.FirstDrawCall; + auto& data = *(Data*)cb; + + const bool useLightmap = view.Flags & ViewFlags::GI +#if USE_EDITOR + && EnableLightmapsUsage +#endif + && drawCall.Surface.Lightmap != nullptr; + if (useLightmap) + { + // Bind lightmap textures + GPUTexture *lightmap0, *lightmap1, *lightmap2; + drawCall.Surface.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); + context->BindSR(0, lightmap0); + context->BindSR(1, lightmap1); + context->BindSR(2, lightmap2); + + // Set lightmap data + data.LightmapArea = drawCall.Surface.LightmapUVsArea; + } + + srv += SRVs; + cb += sizeof(Data); + return useLightmap; +} + +#if USE_EDITOR + +void TessellationFeature::Generate(GeneratorData& data) +{ + data.Template = TEXT("Tessellation.hlsl"); + data.ConstantsSize = 0; + data.ResourcesCount = SRVs; +} + +void LightmapFeature::Generate(GeneratorData& data) +{ + data.Template = TEXT("Lightmap.hlsl"); + data.ConstantsSize = sizeof(Data); + data.ResourcesCount = SRVs; +} + +#endif diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h new file mode 100644 index 000000000..0a2349dfe --- /dev/null +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "MaterialShader.h" +#include "Engine/Core/Math/Rectangle.h" + +// Material shader features are plugin-based functionalities that are reusable between different material domains. +struct MaterialShaderFeature +{ +#if USE_EDITOR + struct GeneratorData + { + const Char* Template; + int32 ConstantsSize; + int32 ResourcesCount; + }; +#endif; +}; + +// Material shader feature that add support for Forward shading inside the material shader. +struct ForwardShadingFeature : MaterialShaderFeature +{ + enum { MaxLocalLights = 4 }; + + enum { SRVs = 3 }; + + PACK_STRUCT(struct Data + { + LightData DirectionalLight; + LightShadowData DirectionalLightShadow; + LightData SkyLight; + ProbeData EnvironmentProbe; + ExponentialHeightFogData ExponentialHeightFog; + Vector3 Dummy2; + uint32 LocalLightsCount; + LightData LocalLights[MaxLocalLights]; + }); + + static void Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv); +}; + +// Material shader feature that adds geometry hardware tessellation (using Hull and Domain shaders). +struct TessellationFeature : MaterialShaderFeature +{ + enum { SRVs = 0 }; + + static void Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv) + { + } +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif +}; + +// Material shader feature that adds lightmap sampling feature. +struct LightmapFeature : MaterialShaderFeature +{ + enum { SRVs = 3 }; + + PACK_STRUCT(struct Data + { + Rectangle LightmapArea; + }); + + static bool Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv); +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif +}; diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index 210c81e7d..5cfad0126 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -81,7 +81,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(ParticleMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(ParticleMaterialShaderData) : nullptr; bindMeta.Input = nullptr; bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth; @@ -105,7 +105,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) auto name = StringView(param.GetName().Get() + 9); const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name); - *((int32*)(bindMeta.Buffer0 + param.GetBindOffset())) = offset; + *((int32*)(bindMeta.Constants + param.GetBindOffset())) = offset; } } } diff --git a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp index 5c2fb1df0..2d9840103 100644 --- a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp @@ -33,7 +33,7 @@ void PostFxMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(PostFxMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(PostFxMaterialShaderData) : nullptr; bindMeta.Input = params.Input; bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = true; diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index 37f932a5a..68c5fbd20 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -60,7 +60,7 @@ void TerrainMaterialShader::Bind(BindParameters& params) // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCb0 ? _cb0Data.Get() + sizeof(TerrainMaterialShaderData) : nullptr; + bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(TerrainMaterialShaderData) : nullptr; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = false; diff --git a/Source/Engine/Particles/Graph/GPU/GPUParticles.cpp b/Source/Engine/Particles/Graph/GPU/GPUParticles.cpp index befd5b25d..cd16a9c52 100644 --- a/Source/Engine/Particles/Graph/GPU/GPUParticles.cpp +++ b/Source/Engine/Particles/Graph/GPU/GPUParticles.cpp @@ -155,7 +155,7 @@ void GPUParticles::Execute(GPUContext* context, ParticleEmitter* emitter, Partic // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Buffer0 = hasCB ? _cbData.Get() + sizeof(GPUParticlesData) : nullptr; + bindMeta.Constants = hasCB ? _cbData.Get() + sizeof(GPUParticlesData) : nullptr; bindMeta.Input = nullptr; if (viewTask) { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp index 90103c165..32af18364 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Texture.cpp @@ -163,27 +163,4 @@ void MaterialGenerator::linearizeSceneDepth(Node* caller, const Value& depth, Va value = writeLocal(VariantType::Float, String::Format(TEXT("ViewInfo.w / ({0}.x - ViewInfo.z)"), depth.Value), caller); } -byte MaterialGenerator::getStartSrvRegister(MaterialLayer* baseLayer) -{ - // Note: this must match material templates - switch (baseLayer->Domain) - { - case MaterialDomain::Surface: - return baseLayer->BlendMode == MaterialBlendMode::Transparent ? 3 : 3; - case MaterialDomain::PostProcess: - return 0; - case MaterialDomain::Decal: - return 1; - case MaterialDomain::GUI: - return 0; - case MaterialDomain::Terrain: - return 6; - case MaterialDomain::Particle: - return 5; - default: - CRASH; - return 0; - } -} - #endif diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 7abd29af9..61c05149d 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -4,7 +4,9 @@ #include "MaterialGenerator.h" #include "Engine/Visject/ShaderGraphUtilities.h" +#include "Engine/Platform/File.h" #include "Engine/Graphics/Materials/MaterialShader.h" +#include "Engine/Graphics/Materials/MaterialShaderFeatures.h" /// /// Material shader source code template has special marks for generated code. @@ -20,10 +22,86 @@ enum MaterialTemplateInputsMapping In_GetMaterialVS = 5, In_GetMaterialDS = 6, In_Includes = 7, + In_Utilities = 8, + In_Shaders = 9, In_MAX }; +/// +/// Material shader feature source code template has special marks for generated code. Each starts with '@' char and index of the mapped string. +/// +enum class FeatureTemplateInputsMapping +{ + Defines = 0, + Includes = 1, + Constants = 2, + Resources = 3, + Utilities = 4, + Shaders = 5, + MAX +}; + +struct FeatureData +{ + MaterialShaderFeature::GeneratorData Data; + String Inputs[(int32)FeatureTemplateInputsMapping::MAX]; + + bool Init(); +}; + +namespace +{ + // Loaded and parsed features data cache + Dictionary Features; +} + +bool FeatureData::Init() +{ + // Load template file + const String path = Globals::EngineContentFolder / TEXT("Editor/MaterialTemplates/Features/") + Data.Template; + String contents; + if (File::ReadAllText(path, contents)) + { + LOG(Error, "Cannot open file {0}", path); + return true; + } + + int32 i = 0; + const int32 length = contents.Length(); + + // Skip until input start + for (; i < length; i++) + { + if (contents[i] == '@') + break; + } + + // Load all inputs + do + { + // Parse input type + i++; + const int32 inIndex = contents[i++] - '0'; + ASSERT_LOW_LAYER(Math::IsInRange(inIndex, 0, (int32)FeatureTemplateInputsMapping::MAX - 1)); + + // Read until next input start + const Char* start = &contents[i]; + for (; i < length; i++) + { + const auto c = contents[i]; + if (c == '@') + break; + } + const Char* end = &contents[i]; + + // Set input + Inputs[inIndex].Set(start, (int32)(end - start)); + } while (i < length); + + return false; +} + MaterialValue MaterialGenerator::getUVs(VariantType::Vector2, TEXT("input.TexCoord")); MaterialValue MaterialGenerator::getTime(VariantType::Float, TEXT("TimeParam")); MaterialValue MaterialGenerator::getNormal(VariantType::Vector3, TEXT("input.TBN[2]")); @@ -53,6 +131,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo ASSERT_LOW_LAYER(_layers.Count() > 0); String inputs[In_MAX]; + Array> features; // Setup and prepare layers _writer.Clear(); @@ -87,6 +166,32 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo const MaterialGraphBox* layerInputBox = baseLayer->Root->GetBox(0); const bool isLayered = layerInputBox->HasConnection(); + // Initialize features +#define ADD_FEATURE(type) \ + { \ + StringAnsiView typeName(#type, ARRAY_COUNT(#type) - 1); \ + features.Add(typeName); \ + if (!Features.ContainsKey(typeName)) \ + { \ + auto& feature = Features[typeName]; \ + type::Generate(feature.Data); \ + if (feature.Init()) \ + return true; \ + } \ + } + switch (baseLayer->Domain) + { + case MaterialDomain::Surface: + if (materialInfo.TessellationMode != TessellationMethod::None) + ADD_FEATURE(TessellationFeature); + if (materialInfo.BlendMode == MaterialBlendMode::Opaque) + ADD_FEATURE(LightmapFeature); + break; + default: + break; + } +#undef ADD_FEATURE + // Check if material is using special features and update the metadata flags if (!isLayered) { @@ -240,11 +345,13 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo // Update material usage based on material generator outputs materialInfo.UsageFlags = baseLayer->UsageFlags; +#define WRITE_FEATURES(input) for (auto f : features) _writer.Write(Features[f].Inputs[(int32)FeatureTemplateInputsMapping::input]); // Defines { _writer.Write(TEXT("#define MATERIAL_MASK_THRESHOLD ({0})\n"), baseLayer->MaskThreshold); _writer.Write(TEXT("#define CUSTOM_VERTEX_INTERPOLATORS_COUNT ({0})\n"), _vsToPsInterpolants.Count()); - _writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})"), baseLayer->OpacityThreshold); + _writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})\n"), baseLayer->OpacityThreshold); + WRITE_FEATURES(Defines); inputs[In_Defines] = _writer.ToString(); _writer.Clear(); } @@ -252,31 +359,87 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo // Includes { for (auto& include : _includes) - { _writer.Write(TEXT("#include \"{0}\"\n"), include.Item); - } + WRITE_FEATURES(Includes); inputs[In_Includes] = _writer.ToString(); _writer.Clear(); } - // Check if material is using any parameters - if (_parameters.HasItems()) + // Constants { - ShaderGraphUtilities::GenerateShaderConstantBuffer(_writer, _parameters); + WRITE_FEATURES(Constants); + if (_parameters.HasItems()) + ShaderGraphUtilities::GenerateShaderConstantBuffer(_writer, _parameters); inputs[In_Constants] = _writer.ToString(); _writer.Clear(); + } - const int32 startRegister = getStartSrvRegister(baseLayer); - const auto error = ShaderGraphUtilities::GenerateShaderResources(_writer, _parameters, startRegister); - if (error) + // Resources + { + int32 srv = 0; + switch (baseLayer->Domain) { - OnError(nullptr, nullptr, error); - return true; + case MaterialDomain::Surface: + if (materialInfo.BlendMode != MaterialBlendMode::Opaque) + srv = 3; // Forward shading resources + break; + case MaterialDomain::Decal: + srv = 1; + break; + case MaterialDomain::Terrain: + srv = 6; + break; + case MaterialDomain::Particle: + srv = 5; + break; + } + for (auto f : features) + { + const auto& text = Features[f].Inputs[(int32)FeatureTemplateInputsMapping::Resources]; + const Char* str = text.Get(); + int32 prevIdx = 0, idx = 0; + while (true) + { + idx = text.Find(TEXT("__SRV__"), StringSearchCase::CaseSensitive, prevIdx); + if (idx == -1) + break; + int32 len = idx - prevIdx; + _writer.Write(StringView(str, len)); + str += len; + _writer.Write(StringUtils::ToString(srv)); + srv++; + str += ARRAY_COUNT("__SRV__") - 1; + prevIdx = idx + ARRAY_COUNT("__SRV__") - 1; + } + _writer.Write(StringView(str)); + } + if (_parameters.HasItems()) + { + const auto error = ShaderGraphUtilities::GenerateShaderResources(_writer, _parameters, srv); + if (error) + { + OnError(nullptr, nullptr, error); + return true; + } } inputs[In_ShaderResources] = _writer.ToString(); _writer.Clear(); } + // Utilities + { + WRITE_FEATURES(Utilities); + inputs[In_Utilities] = _writer.ToString(); + _writer.Clear(); + } + + // Shaders + { + WRITE_FEATURES(Shaders); + inputs[In_Shaders] = _writer.ToString(); + _writer.Clear(); + } + // Save material parameters data if (_parameters.HasItems()) MaterialParams::Save(parametersData, &_parameters); @@ -315,16 +478,15 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo LOG(Warning, "Unknown material domain."); return true; } - auto file = FileReadStream::Open(path); if (file == nullptr) { - LOG(Warning, "Cannot load material base source code."); + LOG(Error, "Cannot open file {0}", path); return true; } // Format template - uint32 length = file->GetLength(); + const uint32 length = file->GetLength(); Array tmp; for (uint32 i = 0; i < length; i++) { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h index 005b9c58c..9ae85cac8 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.h @@ -209,8 +209,6 @@ public: static MaterialGraphBoxesMapping MaterialGraphBoxesMappings[]; static const MaterialGraphBoxesMapping& GetMaterialRootNodeBox(MaterialGraphBoxes box); - - static byte getStartSrvRegister(MaterialLayer* baseLayer); }; #endif diff --git a/Source/Engine/Utilities/TextWriter.h b/Source/Engine/Utilities/TextWriter.h index d2702bf3c..54f2e996a 100644 --- a/Source/Engine/Utilities/TextWriter.h +++ b/Source/Engine/Utilities/TextWriter.h @@ -4,6 +4,8 @@ #include "Engine/Core/NonCopyable.h" #include "Engine/Core/Formatting.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringView.h" #include "Engine/Serialization/MemoryWriteStream.h" /// @@ -97,6 +99,24 @@ public: WriteLine(); } + /// + /// Write text to the buffer + /// + /// Data + void Write(const StringViewBase& text) + { + _buffer.WriteBytes((void*)text.Get(), text.Length() * sizeof(CharType)); + } + + /// + /// Write text to the buffer + /// + /// Data + void Write(const StringBase& text) + { + _buffer.WriteBytes((void*)text.Get(), text.Length() * sizeof(CharType)); + } + /// /// Write text to the buffer /// diff --git a/Source/Engine/Visject/ShaderGraphUtilities.cpp b/Source/Engine/Visject/ShaderGraphUtilities.cpp index c84a500e4..079b6e602 100644 --- a/Source/Engine/Visject/ShaderGraphUtilities.cpp +++ b/Source/Engine/Visject/ShaderGraphUtilities.cpp @@ -177,10 +177,6 @@ const Char* ShaderGraphUtilities::GenerateShaderResources(TextWriterUnicode& wri } } } - - if (startRegister != registerIndex) - writer.WriteLine(); - return nullptr; } diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index 8f47124dc..aef3fc579 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -173,24 +173,4 @@ float3 AOMultiBounce(float visibility, float3 albedo) return max(visibility, ((visibility * a + b) * visibility + c) * visibility); } -#if CAN_USE_LIGHTMAP - -// Evaluates the H-Basis coefficients in the tangent space normal direction -float3 GetHBasisIrradiance(in float3 n, in float3 h0, in float3 h1, in float3 h2, in float3 h3) -{ - float3 color = 0.0f; - - // Band 0 - color += h0 * (1.0f / sqrt(2.0f * PI)); - - // Band 1 - color += h1 * -sqrt(1.5f / PI) * n.y; - color += h2 * sqrt(1.5f / PI) * (2 * n.z - 1.0f); - color += h3 * -sqrt(1.5f / PI) * n.x; - - return color; -} - -#endif - #endif From 9e6243adcc1587a833182c1a7024ab755f8c19fc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Feb 2021 14:58:01 +0100 Subject: [PATCH 307/419] Refactor material shaders generator to use modular features as extensions --- Content/Editor/MaterialTemplates/Decal.shader | 3 - .../Features/Tessellation.hlsl | 159 +------ Content/Editor/MaterialTemplates/GUI.shader | 3 - .../Editor/MaterialTemplates/Particle.shader | 3 - .../MaterialTemplates/PostProcess.shader | 3 - .../MaterialTemplates/SurfaceDeferred.shader | 224 +++++---- .../MaterialTemplates/SurfaceForward.shader | 28 -- .../Editor/MaterialTemplates/Terrain.shader | 431 ++++-------------- .../Materials/DeferredMaterialShader.cpp | 13 +- .../Materials/MaterialShaderFeatures.cpp | 10 +- .../Materials/TerrainMaterialShader.cpp | 80 ++-- Source/Engine/Renderer/DrawCall.h | 12 +- .../MaterialGenerator/MaterialGenerator.cpp | 7 +- Source/Shaders/MaterialCommon.hlsl | 9 +- 14 files changed, 308 insertions(+), 677 deletions(-) diff --git a/Content/Editor/MaterialTemplates/Decal.shader b/Content/Editor/MaterialTemplates/Decal.shader index 35446b7e2..452937955 100644 --- a/Content/Editor/MaterialTemplates/Decal.shader +++ b/Content/Editor/MaterialTemplates/Decal.shader @@ -116,9 +116,6 @@ Material GetMaterialPS(MaterialInput input) @4 } -// Fix line for errors/warnings for shader code from template -#line 1000 - // Input macro specified by the material: DECAL_BLEND_MODE #define DECAL_BLEND_MODE_TRANSLUCENT 0 diff --git a/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl b/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl index c5ebbba92..f2fc714ee 100644 --- a/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl +++ b/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. @0// Tessellation: Defines +#define TessalationProjectOntoPlane(planeNormal, planePosition, pointToProject) pointToProject - dot(pointToProject - planePosition, planeNormal) * planeNormal @1// Tessellation: Includes @2// Tessellation: Constants @3// Tessellation: Resources @@ -11,22 +12,10 @@ // Interpolants passed from the hull shader to the domain shader struct TessalationHSToDS { - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; + float4 Position : SV_Position; + GeometryData Geometry; #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; -#if IS_MOTION_VECTORS_PASS - float3 PrevWorldPosition : TEXCOORD8; #endif float TessellationMultiplier : TESS; }; @@ -34,41 +23,19 @@ struct TessalationHSToDS // Interpolants passed from the domain shader and to the pixel shader struct TessalationDSToPS { - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; + float4 Position : SV_Position; + GeometryData Geometry; #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; -#if IS_MOTION_VECTORS_PASS - float3 PrevWorldPosition : TEXCOORD8; #endif }; MaterialInput GetMaterialInput(TessalationDSToPS input) { MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = input.VertexColor; -#endif - result.TBN = CalcTangentBasis(input.WorldNormal, input.WorldTangent); - result.TwoSidedSign = WorldDeterminantSign; - result.InstanceOrigin = input.InstanceOrigin; - result.InstanceParams = input.InstanceParams; result.SvPosition = input.Position; + GetGeometryMaterialInput(result, input.Geometry); + result.TwoSidedSign = WorldDeterminantSign; #if USE_CUSTOM_VERTEX_INTERPOLATORS result.CustomVSToPS = input.CustomVSToPS; #endif @@ -78,7 +45,7 @@ MaterialInput GetMaterialInput(TessalationDSToPS input) struct TessalationPatch { float EdgeTessFactor[3] : SV_TessFactor; - float InsideTessFactor : SV_InsideTessFactor; + float InsideTessFactor : SV_InsideTessFactor; #if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN float3 B210 : POSITION4; float3 B120 : POSITION5; @@ -110,20 +77,19 @@ TessalationPatch HS_PatchConstant(InputPatch input) #if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN // Calculate PN Triangle control points // Reference: [Vlachos 2001] - float3 p1 = input[0].WorldPosition; - float3 p2 = input[1].WorldPosition; - float3 p3 = input[2].WorldPosition; - float3 n1 = input[0].WorldNormal; - float3 n2 = input[1].WorldNormal; - float3 n3 = input[2].WorldNormal; + float3 p1 = input[0].Geometry.WorldPosition; + float3 p2 = input[1].Geometry.WorldPosition; + float3 p3 = input[2].Geometry.WorldPosition; + float3 n1 = input[0].Geometry.WorldNormal; + float3 n2 = input[1].Geometry.WorldNormal; + float3 n3 = input[2].Geometry.WorldNormal; output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f; output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f; output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f; output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f; output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f; output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f; - float3 e = (output.B210 + output.B120 + output.B021 + - output.B012 + output.B102 + output.B201) / 6.0f; + float3 e = (output.B210 + output.B120 + output.B021 + output.B012 + output.B102 + output.B201) / 6.0f; float3 v = (p1 + p2 + p3) / 3.0f; output.B111 = e + ((e - v) / 2.0f); #endif @@ -132,8 +98,6 @@ TessalationPatch HS_PatchConstant(InputPatch input) } META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS) [domain("tri")] [partitioning("fractional_odd")] @@ -148,19 +112,7 @@ TessalationHSToDS HS(InputPatch in // Pass through shader #define COPY(thing) output.thing = input[ControlPointID].thing; COPY(Position); - COPY(WorldPosition); - COPY(TexCoord); - COPY(LightmapUV); -#if USE_VERTEX_COLOR - COPY(VertexColor); -#endif - COPY(WorldNormal); - COPY(WorldTangent); - COPY(InstanceOrigin); - COPY(InstanceParams); -#if IS_MOTION_VECTORS_PASS - COPY(PrevWorldPosition); -#endif + COPY(Geometry); COPY(TessellationMultiplier); #if USE_CUSTOM_VERTEX_INTERPOLATORS COPY(CustomVSToPS); @@ -170,19 +122,7 @@ TessalationHSToDS HS(InputPatch in return output; } -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - -// Orthogonal projection on to plane -float3 ProjectOntoPlane(float3 planeNormal, float3 planePosition, float3 pointToProject) -{ - return pointToProject - dot(pointToProject - planePosition, planeNormal) * planeNormal; -} - -#endif - META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=0) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) [domain("tri")] TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch input) { @@ -194,22 +134,18 @@ TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : S float W = barycentricCoords.z; // Interpolate patch attributes to generated vertices + output.Geometry = InterpolateGeometry(input[0].Geometry, U, input[1].Geometry, V, input[2].Geometry, W); #define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing -#define COPY(thing) output.thing = input[0].thing INTERPOLATE(Position); #if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN + // Interpolate using barycentric coordinates and PN Triangle control points float UU = U * U; float VV = V * V; float WW = W * W; float UU3 = UU * 3.0f; float VV3 = VV * 3.0f; float WW3 = WW * 3.0f; - - // Interpolate using barycentric coordinates and PN Triangle control points - output.WorldPosition = - input[0].WorldPosition * UU * U + - input[1].WorldPosition * VV * V + - input[2].WorldPosition * WW * W + + float3 offset = constantData.B210 * UU3 * V + constantData.B120 * VV3 * U + constantData.B021 * VV3 * W + @@ -217,76 +153,31 @@ TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : S constantData.B102 * WW3 * U + constantData.B201 * UU3 * W + constantData.B111 * 6.0f * W * U * V; -#if IS_MOTION_VECTORS_PASS - output.PrevWorldPosition = - input[0].PrevWorldPosition * UU * U + - input[1].PrevWorldPosition * VV * V + - input[2].PrevWorldPosition * WW * W + - constantData.B210 * UU3 * V + - constantData.B120 * VV3 * U + - constantData.B021 * VV3 * W + - constantData.B012 * WW3 * V + - constantData.B102 * WW3 * U + - constantData.B201 * UU3 * W + - constantData.B111 * 6.0f * W * U * V; -#endif + InterpolateGeometryPositions(output.Geometry, input[0].Geometry, UU * U, input[1].Geometry, VV * V, input[2].Geometry, WW * W, offset); #else - INTERPOLATE(WorldPosition); -#if IS_MOTION_VECTORS_PASS - INTERPOLATE(PrevWorldPosition); + InterpolateGeometryPositions(output.Geometry, input[0].Geometry, U, input[1].Geometry, V, input[2].Geometry, W, float3(0, 0, 0)); #endif -#endif - INTERPOLATE(TexCoord); - INTERPOLATE(LightmapUV); -#if USE_VERTEX_COLOR - INTERPOLATE(VertexColor); -#endif - INTERPOLATE(WorldNormal); - INTERPOLATE(WorldTangent); - COPY(InstanceOrigin); - COPY(InstanceParams); #if USE_CUSTOM_VERTEX_INTERPOLATORS UNROLL for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++) - { INTERPOLATE(CustomVSToPS[i]); - } #endif #undef INTERPOLATE -#undef COPY - - // Interpolating tangents can unnormalize it, so normalize it - output.WorldNormal = normalize(output.WorldNormal); - output.WorldTangent.xyz = normalize(output.WorldTangent.xyz); #if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - // Orthogonal projection in the tangent planes - float3 posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].WorldPosition, output.WorldPosition); - float3 posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].WorldPosition, output.WorldPosition); - float3 posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].WorldPosition, output.WorldPosition); - - // Interpolate the projected points - output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#if IS_MOTION_VECTORS_PASS - posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].PrevWorldPosition, output.PrevWorldPosition); - posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].PrevWorldPosition, output.PrevWorldPosition); - posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].PrevWorldPosition, output.PrevWorldPosition); - output.PrevWorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#endif + // Orthogonal projection in the tangent planes with interpolation + ApplyGeometryPositionsPhongTess(output.Geometry, input[0].Geometry, input[1].Geometry, input[2].Geometry, U, V, W); #endif // Perform displacement mapping #if USE_DISPLACEMENT MaterialInput materialInput = GetMaterialInput(output); Material material = GetMaterialDS(materialInput); - output.WorldPosition += material.WorldDisplacement; -#if IS_MOTION_VECTORS_PASS - output.PrevWorldPosition += material.WorldDisplacement; -#endif + OffsetGeometryPositions(output.Geometry, material.WorldDisplacement); #endif // Recalculate the clip space position - output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); return output; } diff --git a/Content/Editor/MaterialTemplates/GUI.shader b/Content/Editor/MaterialTemplates/GUI.shader index 08fddde79..b927755ad 100644 --- a/Content/Editor/MaterialTemplates/GUI.shader +++ b/Content/Editor/MaterialTemplates/GUI.shader @@ -199,9 +199,6 @@ Material GetMaterialPS(MaterialInput input) @4 } -// Fix line for errors/warnings for shader code from template -#line 1000 - // Vertex Shader function for GUI materials rendering META_VS(true, FEATURE_LEVEL_ES2) META_VS_IN_ELEMENT(POSITION, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true) diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index e8d00d1db..f3121b6b0 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -332,9 +332,6 @@ Material GetMaterialPS(MaterialInput input) @4 } -// Fix line for errors/warnings for shader code from template -#line 1000 - // Calculates the transform matrix from mesh tangent space to local space half3x3 CalcTangentToLocal(ModelInput input) { diff --git a/Content/Editor/MaterialTemplates/PostProcess.shader b/Content/Editor/MaterialTemplates/PostProcess.shader index a3e5e42c7..681a04248 100644 --- a/Content/Editor/MaterialTemplates/PostProcess.shader +++ b/Content/Editor/MaterialTemplates/PostProcess.shader @@ -136,9 +136,6 @@ Material GetMaterialPS(MaterialInput input) @4 } -// Fix line for errors/warnings for shader code from template -#line 1000 - // Pixel Shader function for PostFx materials rendering META_PS(true, FEATURE_LEVEL_ES2) float4 PS_PostFx(PixelInput input) : SV_Target0 diff --git a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader index 303cb2fee..dcee96822 100644 --- a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader +++ b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader @@ -32,25 +32,30 @@ float Dummy1; // Material shader resources @2 + +// Geometry data passed though the graphics rendering stages up to the pixel shader +struct GeometryData +{ + float3 WorldPosition : TEXCOORD0; + float2 TexCoord : TEXCOORD1; + float2 LightmapUV : TEXCOORD2; +#if USE_VERTEX_COLOR + half4 VertexColor : COLOR; +#endif + float3 WorldNormal : TEXCOORD3; + float4 WorldTangent : TEXCOORD4; + float3 InstanceOrigin : TEXCOORD6; + float2 InstanceParams : TEXCOORD7; // x-PerInstanceRandom, y-LODDitherFactor + float3 PrevWorldPosition : TEXCOORD8; +}; + // Interpolants passed from the vertex shader struct VertexOutput { - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; + float4 Position : SV_Position; + GeometryData Geometry; #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; // x-PerInstanceRandom, y-LODDitherFactor -#if IS_MOTION_VECTORS_PASS - float3 PrevWorldPosition : TEXCOORD8; #endif #if USE_TESSELLATION float TessellationMultiplier : TESS; @@ -60,24 +65,12 @@ struct VertexOutput // Interpolants passed to the pixel shader struct PixelInput { - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3 WorldNormal : TEXCOORD3; - float4 WorldTangent : TEXCOORD4; + float4 Position : SV_Position; + GeometryData Geometry; #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; #endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; // x-PerInstanceRandom, y-LODDitherFactor -#if IS_MOTION_VECTORS_PASS - float3 PrevWorldPosition : TEXCOORD8; -#endif - bool IsFrontFace : SV_IsFrontFace; + bool IsFrontFace : SV_IsFrontFace; }; // Material properties generation input @@ -108,24 +101,75 @@ struct MaterialInput #endif }; -float3x3 CalcTangentBasis(float3 normal, float4 tangent) +// Extracts geometry data to the material input +void GetGeometryMaterialInput(inout MaterialInput result, in GeometryData geometry) { - float3 bitangent = cross(normal, tangent.xyz) * tangent.w; - return float3x3(tangent.xyz, bitangent, normal); + result.WorldPosition = geometry.WorldPosition; + result.TexCoord = geometry.TexCoord; +#if USE_LIGHTMAP + result.LightmapUV = geometry.LightmapUV; +#endif +#if USE_VERTEX_COLOR + result.VertexColor = geometry.VertexColor; +#endif + result.TBN = CalcTangentBasis(geometry.WorldNormal, geometry.WorldTangent); + result.InstanceOrigin = geometry.InstanceOrigin; + result.InstanceParams = geometry.InstanceParams; } +#if USE_TESSELLATION + +// Interpolates the geometry positions data only (used by the tessallation when generating vertices) +#define InterpolateGeometryPositions(output, p0, w0, p1, w1, p2, w2, offset) output.WorldPosition = p0.WorldPosition * w0 + p1.WorldPosition * w1 + p2.WorldPosition * w2 + offset; output.PrevWorldPosition = p0.PrevWorldPosition * w0 + p1.PrevWorldPosition * w1 + p2.PrevWorldPosition * w2 + offset + +// Offsets the geometry positions data only (used by the tessallation when generating vertices) +#define OffsetGeometryPositions(geometry, offset) geometry.WorldPosition += offset; geometry.PrevWorldPosition += offset + +// Applies the Phong tessallation to the geometry positions (used by the tessallation when doing Phong tess) +#define ApplyGeometryPositionsPhongTess(geometry, p0, p1, p2, U, V, W) \ + float3 posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.WorldPosition, geometry.WorldPosition); \ + float3 posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.WorldPosition, geometry.WorldPosition); \ + float3 posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.WorldPosition, geometry.WorldPosition); \ + geometry.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; \ + posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.PrevWorldPosition, geometry.PrevWorldPosition); \ + posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.PrevWorldPosition, geometry.PrevWorldPosition); \ + posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.PrevWorldPosition, geometry.PrevWorldPosition); \ + geometry.PrevWorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW + +// Interpolates the geometry data except positions (used by the tessallation when generating vertices) +GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2) +{ + GeometryData output = (GeometryData)0; + output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2; +#if USE_LIGHTMAP + output.LightmapUV = p0.LightmapUV * w0 + p1.LightmapUV * w1 + p2.LightmapUV * w2; +#endif +#if USE_VERTEX_COLOR + output.VertexColor = p0.VertexColor * w0 + p1.VertexColor * w1 + p2.VertexColor * w2; +#endif + output.WorldNormal = p0.WorldNormal * w0 + p1.WorldNormal * w1 + p2.WorldNormal * w2; + output.WorldNormal = normalize(output.WorldNormal); + output.WorldTangent = p0.WorldTangent * w0 + p1.WorldTangent * w1 + p2.WorldTangent * w2; + output.WorldTangent.xyz = normalize(output.WorldTangent.xyz); + output.InstanceOrigin = p0.InstanceOrigin; + output.InstanceParams = p0.InstanceParams; + return output; +} + +#endif + MaterialInput GetMaterialInput(ModelInput input, VertexOutput output, float3 localNormal) { MaterialInput result = (MaterialInput)0; - result.WorldPosition = output.WorldPosition; - result.TexCoord = output.TexCoord; + result.WorldPosition = output.Geometry.WorldPosition; + result.TexCoord = output.Geometry.TexCoord; #if USE_LIGHTMAP - result.LightmapUV = output.LightmapUV; + result.LightmapUV = output.Geometry.LightmapUV; #endif #if USE_VERTEX_COLOR - result.VertexColor = output.VertexColor; + result.VertexColor = output.Geometry.VertexColor; #endif - result.TBN = CalcTangentBasis(output.WorldNormal, output.WorldTangent); + result.TBN = CalcTangentBasis(output.Geometry.WorldNormal, output.Geometry.WorldTangent); result.TwoSidedSign = WorldDeterminantSign; result.SvPosition = output.Position; result.PreSkinnedPosition = input.Position.xyz; @@ -146,15 +190,15 @@ MaterialInput GetMaterialInput(ModelInput input, VertexOutput output, float3 loc MaterialInput GetMaterialInput(VertexOutput output, float3 localPosition, float3 localNormal) { MaterialInput result = (MaterialInput)0; - result.WorldPosition = output.WorldPosition; - result.TexCoord = output.TexCoord; + result.WorldPosition = output.Geometry.WorldPosition; + result.TexCoord = output.Geometry.TexCoord; #if USE_LIGHTMAP - result.LightmapUV = output.LightmapUV; + result.LightmapUV = output.Geometry.LightmapUV; #endif #if USE_VERTEX_COLOR - result.VertexColor = output.VertexColor; + result.VertexColor = output.Geometry.VertexColor; #endif - result.TBN = CalcTangentBasis(output.WorldNormal, output.WorldTangent); + result.TBN = CalcTangentBasis(output.Geometry.WorldNormal, output.Geometry.WorldTangent); result.TwoSidedSign = WorldDeterminantSign; result.InstanceOrigin = WorldMatrix[3].xyz; result.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); @@ -167,18 +211,18 @@ MaterialInput GetMaterialInput(VertexOutput output, float3 localPosition, float3 MaterialInput GetMaterialInput(PixelInput input) { MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; + result.WorldPosition = input.Geometry.WorldPosition; + result.TexCoord = input.Geometry.TexCoord; #if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; + result.LightmapUV = input.Geometry.LightmapUV; #endif #if USE_VERTEX_COLOR - result.VertexColor = input.VertexColor; + result.VertexColor = input.Geometry.VertexColor; #endif - result.TBN = CalcTangentBasis(input.WorldNormal, input.WorldTangent); + result.TBN = CalcTangentBasis(input.Geometry.WorldNormal, input.Geometry.WorldTangent); result.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); - result.InstanceOrigin = input.InstanceOrigin; - result.InstanceParams = input.InstanceParams; + result.InstanceOrigin = input.Geometry.InstanceOrigin; + result.InstanceParams = input.Geometry.InstanceParams; result.SvPosition = input.Position; #if USE_CUSTOM_VERTEX_INTERPOLATORS result.CustomVSToPS = input.CustomVSToPS; @@ -330,9 +374,6 @@ Material GetMaterialPS(MaterialInput input) @4 } -// Fix line for errors/warnings for shader code from template -#line 1000 - // Calculates the transform matrix from mesh tangent space to local space float3x3 CalcTangentToLocal(ModelInput input) { @@ -353,7 +394,6 @@ float3x3 CalcTangentToWorld(float4x4 world, float3x3 tangentToLocal) META_VS(IS_SURFACE, FEATURE_LEVEL_ES2) META_PERMUTATION_1(USE_INSTANCING=0) META_PERMUTATION_1(USE_INSTANCING=1) -META_PERMUTATION_2(USE_INSTANCING=0, IS_MOTION_VECTORS_PASS=1) META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, 0, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) @@ -371,34 +411,32 @@ VertexOutput VS(ModelInput input) // Compute world space vertex position float4x4 world = GetInstanceTransform(input); - output.WorldPosition = mul(float4(input.Position.xyz, 1), world).xyz; -#if IS_MOTION_VECTORS_PASS - output.PrevWorldPosition = mul(float4(input.Position.xyz, 1), PrevWorldMatrix).xyz; -#endif + output.Geometry.WorldPosition = mul(float4(input.Position.xyz, 1), world).xyz; + output.Geometry.PrevWorldPosition = mul(float4(input.Position.xyz, 1), PrevWorldMatrix).xyz; // Compute clip space position - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); // Pass vertex attributes - output.TexCoord = input.TexCoord; + output.Geometry.TexCoord = input.TexCoord; #if USE_VERTEX_COLOR - output.VertexColor = input.Color; + output.Geometry.VertexColor = input.Color; #endif - output.InstanceOrigin = world[3].xyz; + output.Geometry.InstanceOrigin = world[3].xyz; #if USE_INSTANCING - output.LightmapUV = input.LightmapUV * input.InstanceLightmapArea.zw + input.InstanceLightmapArea.xy; - output.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); + output.Geometry.LightmapUV = input.LightmapUV * input.InstanceLightmapArea.zw + input.InstanceLightmapArea.xy; + output.Geometry.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); #else - output.LightmapUV = input.LightmapUV * LightmapArea.zw + LightmapArea.xy; - output.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); + output.Geometry.LightmapUV = input.LightmapUV * LightmapArea.zw + LightmapArea.xy; + output.Geometry.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); #endif // Calculate tanget space to world space transformation matrix for unit vectors float3x3 tangentToLocal = CalcTangentToLocal(input); float3x3 tangentToWorld = CalcTangentToWorld(world, tangentToLocal); - output.WorldNormal = tangentToWorld[2]; - output.WorldTangent.xyz = tangentToWorld[0]; - output.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f; + output.Geometry.WorldNormal = tangentToWorld[2]; + output.Geometry.WorldTangent.xyz = tangentToWorld[0]; + output.Geometry.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f; // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS @@ -408,8 +446,8 @@ VertexOutput VS(ModelInput input) // Apply world position offset per-vertex #if USE_POSITION_OFFSET - output.WorldPosition += material.PositionOffset; - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); + output.Geometry.WorldPosition += material.PositionOffset; + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); #endif // Get tessalation multiplier (per vertex) @@ -439,7 +477,7 @@ float4 VS_Depth(ModelInput_PosOnly input) : SV_Position { float4x4 world = GetInstanceTransform(input); float3 worldPosition = mul(float4(input.Position.xyz, 1), world).xyz; - float4 position = mul(float4(worldPosition.xyz, 1), ViewProjectionMatrix); + float4 position = mul(float4(worldPosition, 1), ViewProjectionMatrix); return position; } @@ -524,8 +562,7 @@ float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data) // Vertex Shader function for GBuffers/Depth Pass (skinned mesh rendering) META_VS(IS_SURFACE, FEATURE_LEVEL_ES2) META_PERMUTATION_1(USE_SKINNING=1) -META_PERMUTATION_2(USE_SKINNING=1, IS_MOTION_VECTORS_PASS=1) -META_PERMUTATION_3(USE_SKINNING=1, IS_MOTION_VECTORS_PASS=1, PER_BONE_MOTION_BLUR=1) +META_PERMUTATION_2(USE_SKINNING=1, PER_BONE_MOTION_BLUR=1) META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 0, ALIGN, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 0, ALIGN, PER_VERTEX, 0, true) @@ -544,37 +581,35 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) // Compute world space vertex position float4x4 world = GetInstanceTransform(input); - output.WorldPosition = mul(float4(position, 1), world).xyz; -#if IS_MOTION_VECTORS_PASS + output.Geometry.WorldPosition = mul(float4(position, 1), world).xyz; #if PER_BONE_MOTION_BLUR float3 prevPosition = SkinPrevPosition(input); - output.PrevWorldPosition = mul(float4(prevPosition, 1), PrevWorldMatrix).xyz; + output.Geometry.PrevWorldPosition = mul(float4(prevPosition, 1), PrevWorldMatrix).xyz; #else - output.PrevWorldPosition = mul(float4(position, 1), PrevWorldMatrix).xyz; -#endif + output.Geometry.PrevWorldPosition = mul(float4(position, 1), PrevWorldMatrix).xyz; #endif // Compute clip space position - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); // Pass vertex attributes - output.TexCoord = input.TexCoord; + output.Geometry.TexCoord = input.TexCoord; #if USE_VERTEX_COLOR - output.VertexColor = float4(0, 0, 0, 1); + output.Geometry.VertexColor = float4(0, 0, 0, 1); #endif - output.LightmapUV = float2(0, 0); - output.InstanceOrigin = world[3].xyz; + output.Geometry.LightmapUV = float2(0, 0); + output.Geometry.InstanceOrigin = world[3].xyz; #if USE_INSTANCING - output.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); + output.Geometry.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); #else - output.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); + output.Geometry.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); #endif // Calculate tanget space to world space transformation matrix for unit vectors float3x3 tangentToWorld = CalcTangentToWorld(world, tangentToLocal); - output.WorldNormal = tangentToWorld[2]; - output.WorldTangent.xyz = tangentToWorld[0]; - output.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f; + output.Geometry.WorldNormal = tangentToWorld[2]; + output.Geometry.WorldTangent.xyz = tangentToWorld[0]; + output.Geometry.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f; // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS @@ -584,8 +619,8 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) // Apply world position offset per-vertex #if USE_POSITION_OFFSET - output.WorldPosition += material.PositionOffset; - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); + output.Geometry.WorldPosition += material.PositionOffset; + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); #endif // Get tessalation multiplier (per vertex) @@ -721,10 +756,8 @@ void PS_Depth(PixelInput input // Pixel Shader function for Motion Vectors Pass META_PS(true, FEATURE_LEVEL_ES2) -META_PERMUTATION_1(IS_MOTION_VECTORS_PASS=1) float4 PS_MotionVectors(PixelInput input) : SV_Target0 { -#if IS_MOTION_VECTORS_PASS // LOD masking ClipLODTransition(input); @@ -736,8 +769,8 @@ float4 PS_MotionVectors(PixelInput input) : SV_Target0 #endif // Calculate this and previosu frame pixel locations in clip space - float4 prevClipPos = mul(float4(input.PrevWorldPosition, 1), PrevViewProjectionMatrix); - float4 curClipPos = mul(float4(input.WorldPosition, 1), ViewProjectionMatrix); + float4 prevClipPos = mul(float4(input.Geometry.PrevWorldPosition, 1), PrevViewProjectionMatrix); + float4 curClipPos = mul(float4(input.Geometry.WorldPosition, 1), ViewProjectionMatrix); float2 prevHPos = prevClipPos.xy / prevClipPos.w; float2 curHPos = curClipPos.xy / curClipPos.w; @@ -753,9 +786,6 @@ float4 PS_MotionVectors(PixelInput input) : SV_Target0 // Calculate per-pixel motion vector return float4(vPosCur - vPosPrev, 0, 1); -#else - return float4(0, 0, 0, 1); -#endif } @9 diff --git a/Content/Editor/MaterialTemplates/SurfaceForward.shader b/Content/Editor/MaterialTemplates/SurfaceForward.shader index 9ce0a5f5a..0e9c6db5f 100644 --- a/Content/Editor/MaterialTemplates/SurfaceForward.shader +++ b/Content/Editor/MaterialTemplates/SurfaceForward.shader @@ -338,9 +338,6 @@ Material GetMaterialPS(MaterialInput input) @4 } -// Fix line for errors/warnings for shader code from template -#line 1000 - // Calculates the transform matrix from mesh tangent space to local space half3x3 CalcTangentToLocal(ModelInput input) { @@ -432,31 +429,6 @@ VertexOutput VS(ModelInput input) // The skeletal bones matrix buffer (stored as 4x3, 3 float4 behind each other) Buffer BoneMatrices : register(t0); -#if PER_BONE_MOTION_BLUR - -// The skeletal bones matrix buffer from the previous frame -Buffer PrevBoneMatrices : register(t1); - -float3x4 GetPrevBoneMatrix(int index) -{ - float4 a = PrevBoneMatrices[index * 3]; - float4 b = PrevBoneMatrices[index * 3 + 1]; - float4 c = PrevBoneMatrices[index * 3 + 2]; - return float3x4(a, b, c); -} - -float3 SkinPrevPosition(ModelInput_Skinned input) -{ - float4 position = float4(input.Position.xyz, 1); - float3x4 boneMatrix = input.BlendWeights.x * GetPrevBoneMatrix(input.BlendIndices.x); - boneMatrix += input.BlendWeights.y * GetPrevBoneMatrix(input.BlendIndices.y); - boneMatrix += input.BlendWeights.z * GetPrevBoneMatrix(input.BlendIndices.z); - boneMatrix += input.BlendWeights.w * GetPrevBoneMatrix(input.BlendIndices.w); - return mul(boneMatrix, position); -} - -#endif - // Cached skinning data to avoid multiple calculation struct SkinningData { diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index 0a6cb8028..c36d7f0c5 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -3,7 +3,6 @@ #define MATERIAL 1 @3 - // Enables/disables smooth terrain chunks LOD transitions (with morphing higher LOD near edges to the lower LOD in the neighbour) #define USE_SMOOTH_LOD_TRANSITION 1 @@ -26,7 +25,6 @@ float3 ViewDir; float TimeParam; float4 ViewInfo; float4 ScreenSize; -float4 LightmapArea; float3 WorldInvScale; float WorldDeterminantSign; float PerInstanceRandom; @@ -39,31 +37,32 @@ float2 OffsetUV; float2 Dummy0; @1META_CB_END -// Irradiance and directionality prebaked lightmaps -Texture2D Lightmap0 : register(t0); -Texture2D Lightmap1 : register(t1); -Texture2D Lightmap2 : register(t2); - // Terrain data -Texture2D Heightmap : register(t3); -Texture2D Splatmap0 : register(t4); -Texture2D Splatmap1 : register(t5); +Texture2D Heightmap : register(t0); +Texture2D Splatmap0 : register(t1); +Texture2D Splatmap1 : register(t2); // Material shader resources @2 -// Interpolants passed from the vertex shader -struct VertexOutput +// Geometry data passed though the graphics rendering stages up to the pixel shader +struct GeometryData { - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; - float3 WorldNormal : TEXCOORD3; - float HolesMask : TEXCOORD4; + float3 WorldPosition : TEXCOORD0; + float2 TexCoord : TEXCOORD1; + float2 LightmapUV : TEXCOORD2; + float3 WorldNormal : TEXCOORD3; + float HolesMask : TEXCOORD4; #if USE_TERRAIN_LAYERS float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5; #endif +}; + +// Interpolants passed from the vertex shader +struct VertexOutput +{ + float4 Position : SV_Position; + GeometryData Geometry; #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; #endif @@ -75,19 +74,12 @@ struct VertexOutput // Interpolants passed to the pixel shader struct PixelInput { - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; - float3 WorldNormal : TEXCOORD3; - float HolesMask : TEXCOORD4; -#if USE_TERRAIN_LAYERS - float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5; -#endif + float4 Position : SV_Position; + GeometryData Geometry; #if USE_CUSTOM_VERTEX_INTERPOLATORS float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; #endif - bool IsFrontFace : SV_IsFrontFace; + bool IsFrontFace : SV_IsFrontFace; }; // Material properties generation input @@ -112,20 +104,69 @@ struct MaterialInput #endif }; +// Extracts geometry data to the material input +void GetGeometryMaterialInput(inout MaterialInput result, in GeometryData geometry) +{ + result.WorldPosition = geometry.WorldPosition; + result.TexCoord = geometry.TexCoord; +#if USE_LIGHTMAP + result.LightmapUV = geometry.LightmapUV; +#endif + result.TBN = CalcTangentBasisFromWorldNormal(geometry.WorldNormal); + result.HolesMask = geometry.HolesMask; +#if USE_TERRAIN_LAYERS + result.Layers = geometry.Layers; +#endif +} + +#if USE_TESSELLATION + +// Interpolates the geometry positions data only (used by the tessallation when generating vertices) +#define InterpolateGeometryPositions(output, p0, w0, p1, w1, p2, w2, offset) output.WorldPosition = p0.WorldPosition * w0 + p1.WorldPosition * w1 + p2.WorldPosition * w2 + offset + +// Offsets the geometry positions data only (used by the tessallation when generating vertices) +#define OffsetGeometryPositions(geometry, offset) geometry.WorldPosition += offset + +// Applies the Phong tessallation to the geometry positions (used by the tessallation when doing Phong tess) +#define ApplyGeometryPositionsPhongTess(geometry, p0, p1, p2, U, V, W) \ + float3 posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.WorldPosition, geometry.WorldPosition); \ + float3 posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.WorldPosition, geometry.WorldPosition); \ + float3 posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.WorldPosition, geometry.WorldPosition); \ + geometry.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW + +// Interpolates the geometry data except positions (used by the tessallation when generating vertices) +GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2) +{ + GeometryData output = (GeometryData)0; + output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2; + output.LightmapUV = p0.LightmapUV * w0 + p1.LightmapUV * w1 + p2.LightmapUV * w2; + output.WorldNormal = p0.WorldNormal * w0 + p1.WorldNormal * w1 + p2.WorldNormal * w2; + output.WorldNormal = normalize(output.WorldNormal); + output.HolesMask = p0.HolesMask * w0 + p1.HolesMask * w1 + p2.HolesMask * w2; +#if USE_TERRAIN_LAYERS + UNROLL + for (int i = 0; i < TERRAIN_LAYERS_DATA_SIZE; i++) + output.Layers[i] = p0.Layers[i] * w0 + p1.Layers[i] * w1 + p2.Layers[i] * w2; +#endif + return output; +} + +#endif + MaterialInput GetMaterialInput(PixelInput input) { MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; + result.WorldPosition = input.Geometry.WorldPosition; + result.TexCoord = input.Geometry.TexCoord; #if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; + result.LightmapUV = input.Geometry.LightmapUV; #endif - result.TBN = CalcTangentBasisFromWorldNormal(input.WorldNormal); + result.TBN = CalcTangentBasisFromWorldNormal(input.Geometry.WorldNormal); result.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); result.SvPosition = input.Position; - result.HolesMask = input.HolesMask; + result.HolesMask = input.Geometry.HolesMask; #if USE_TERRAIN_LAYERS - result.Layers = input.Layers; + result.Layers = input.Geometry.Layers; #endif #if USE_CUSTOM_VERTEX_INTERPOLATORS result.CustomVSToPS = input.CustomVSToPS; @@ -212,20 +253,6 @@ float4 GetVertexColor(MaterialInput input) return 1; } -// Evaluates the H-Basis coefficients in the tangent space normal direction -float3 GetHBasisIrradiance(float3 n, float3 h0, float3 h1, float3 h2, float3 h3) -{ - // Band 0 - float3 color = h0 * (1.0f / sqrt(2.0f * PI)); - - // Band 1 - color += h1 * -sqrt(1.5f / PI) * n.y; - color += h2 * sqrt(1.5f / PI) * (2 * n.z - 1.0f); - color += h3 * -sqrt(1.5f / PI) * n.x; - - return color; -} - @8 // Get material properties function (for vertex shader) @@ -246,9 +273,6 @@ Material GetMaterialPS(MaterialInput input) @4 } -// Fix line for errors/warnings for shader code from template -#line 1000 - // Calculates LOD value (with fractional part for blending) float CalcLOD(float2 xy, float4 morph) { @@ -297,7 +321,7 @@ float3x3 CalcTangentToWorld(float4x4 world, float3x3 tangentToLocal) struct TerrainVertexInput { float2 TexCoord : TEXCOORD0; - float4 Morph : TEXCOORD1; + float4 Morph : TEXCOORD1; }; // Vertex Shader function for terrain rendering @@ -348,7 +372,7 @@ VertexOutput VS(TerrainVertexInput input) float2 normalTemp = float2(heightmapValue.b, heightmapValue.a) * 2.0f - 1.0f; float3 normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y); normal = normalize(normal); - output.HolesMask = isHole ? 0 : 1; + output.Geometry.HolesMask = isHole ? 0 : 1; if (isHole) { normal = float3(0, 1, 0); @@ -365,10 +389,10 @@ VertexOutput VS(TerrainVertexInput input) float3 position = float3(positionXZ.x, height, positionXZ.y); // Compute world space vertex position - output.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz; + output.Geometry.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz; // Compute clip space position - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); // Pass vertex attributes #if USE_SMOOTH_LOD_TRANSITION @@ -376,46 +400,46 @@ VertexOutput VS(TerrainVertexInput input) #else float2 texCoord = input.TexCoord; #endif - output.TexCoord = positionXZ * (1.0f / TerrainChunkSizeLOD0) + OffsetUV; - output.LightmapUV = texCoord * LightmapArea.zw + LightmapArea.xy; + output.Geometry.TexCoord = positionXZ * (1.0f / TerrainChunkSizeLOD0) + OffsetUV; + output.Geometry.LightmapUV = texCoord * LightmapArea.zw + LightmapArea.xy; // Extract terrain layers weights from the splatmap #if USE_TERRAIN_LAYERS - output.Layers[0] = splatmap0Value; + output.Geometry.Layers[0] = splatmap0Value; #if TERRAIN_LAYERS_DATA_SIZE > 1 - output.Layers[1] = splatmap1Value; + output.Geometry.Layers[1] = splatmap1Value; #endif #endif // Compute world space normal vector float3x3 tangentToLocal = CalcTangentBasisFromWorldNormal(normal); float3x3 tangentToWorld = CalcTangentToWorld(WorldMatrix, tangentToLocal); - output.WorldNormal = tangentToWorld[2]; + output.Geometry.WorldNormal = tangentToWorld[2]; // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS MaterialInput materialInput = (MaterialInput)0; - materialInput.WorldPosition = output.WorldPosition; - materialInput.TexCoord = output.TexCoord; + materialInput.WorldPosition = output.Geometry.WorldPosition; + materialInput.TexCoord = output.Geometry.TexCoord; #if USE_LIGHTMAP - materialInput.LightmapUV = output.LightmapUV; + materialInput.LightmapUV = output.Geometry.LightmapUV; #endif - materialInput.TBN = CalcTangentBasisFromWorldNormal(output.WorldNormal); + materialInput.TBN = CalcTangentBasisFromWorldNormal(output.Geometry.WorldNormal); materialInput.TwoSidedSign = WorldDeterminantSign; materialInput.SvPosition = output.Position; materialInput.PreSkinnedPosition = position; materialInput.PreSkinnedNormal = normal; - materialInput.HolesMask = output.HolesMask; + materialInput.HolesMask = output.Geometry.HolesMask; #if USE_TERRAIN_LAYERS - materialInput.Layers = output.Layers; + materialInput.Layers = output.Geometry.Layers; #endif Material material = GetMaterialVS(materialInput); #endif // Apply world position offset per-vertex #if USE_POSITION_OFFSET - output.WorldPosition += material.PositionOffset; - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); + output.Geometry.WorldPosition += material.PositionOffset; + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); #endif // Get tessalation multiplier (per vertex) @@ -431,277 +455,6 @@ VertexOutput VS(TerrainVertexInput input) return output; } -#if USE_TESSELLATION - -// Interpolants passed from the hull shader to the domain shader -struct TessalationHSToDS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; - float3 WorldNormal : TEXCOORD3; - float HolesMask : TEXCOORD4; -#if USE_TERRAIN_LAYERS - float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5; -#endif -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float TessellationMultiplier : TESS; -}; - -// Interpolants passed from the domain shader and to the pixel shader -struct TessalationDSToPS -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; - float3 WorldNormal : TEXCOORD3; - float HolesMask : TEXCOORD4; -#if USE_TERRAIN_LAYERS - float4 Layers[TERRAIN_LAYERS_DATA_SIZE] : TEXCOORD5; -#endif -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif -}; - -MaterialInput GetMaterialInput(TessalationDSToPS input) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; -#endif - result.TBN = CalcTangentBasisFromWorldNormal(input.WorldNormal); - result.TwoSidedSign = WorldDeterminantSign; - result.SvPosition = input.Position; - result.HolesMask = input.HolesMask; -#if USE_TERRAIN_LAYERS - result.Layers = input.Layers; -#endif -#if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; -#endif - return result; -} - -struct TessalationPatch -{ - float EdgeTessFactor[3] : SV_TessFactor; - float InsideTessFactor : SV_InsideTessFactor; -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float3 B210 : POSITION4; - float3 B120 : POSITION5; - float3 B021 : POSITION6; - float3 B012 : POSITION7; - float3 B102 : POSITION8; - float3 B201 : POSITION9; - float3 B111 : CENTER; -#endif -}; - -TessalationPatch HS_PatchConstant(InputPatch input) -{ - TessalationPatch output; - - // Average tess factors along edges, and pick an edge tess factor for the interior tessellation - float4 TessellationMultipliers; - TessellationMultipliers.x = 0.5f * (input[1].TessellationMultiplier + input[2].TessellationMultiplier); - TessellationMultipliers.y = 0.5f * (input[2].TessellationMultiplier + input[0].TessellationMultiplier); - TessellationMultipliers.z = 0.5f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier); - TessellationMultipliers.w = 0.333f * (input[0].TessellationMultiplier + input[1].TessellationMultiplier + input[2].TessellationMultiplier); - - TessellationMultipliers = clamp(TessellationMultipliers, 1, MAX_TESSELLATION_FACTOR); - - output.EdgeTessFactor[0] = TessellationMultipliers.x; // 1->2 edge - output.EdgeTessFactor[1] = TessellationMultipliers.y; // 2->0 edge - output.EdgeTessFactor[2] = TessellationMultipliers.z; // 0->1 edge - output.InsideTessFactor = TessellationMultipliers.w; - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - // Calculate PN-Triangle coefficients - // Refer to Vlachos 2001 for the original formula - float3 p1 = input[0].WorldPosition; - float3 p2 = input[1].WorldPosition; - float3 p3 = input[2].WorldPosition; - float3 n1 = input[0].WorldNormal; - float3 n2 = input[1].WorldNormal; - float3 n3 = input[2].WorldNormal; - - // Calculate control points - output.B210 = (2.0f * p1 + p2 - dot((p2 - p1), n1) * n1) / 3.0f; - output.B120 = (2.0f * p2 + p1 - dot((p1 - p2), n2) * n2) / 3.0f; - output.B021 = (2.0f * p2 + p3 - dot((p3 - p2), n2) * n2) / 3.0f; - output.B012 = (2.0f * p3 + p2 - dot((p2 - p3), n3) * n3) / 3.0f; - output.B102 = (2.0f * p3 + p1 - dot((p1 - p3), n3) * n3) / 3.0f; - output.B201 = (2.0f * p1 + p3 - dot((p3 - p1), n1) * n1) / 3.0f; - float3 e = (output.B210 + output.B120 + output.B021 + - output.B012 + output.B102 + output.B201) / 6.0f; - float3 v = (p1 + p2 + p3) / 3.0f; - output.B111 = e + ((e - v) / 2.0f); -#endif - - return output; -} - -META_HS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -META_HS_PATCH(TESSELLATION_IN_CONTROL_POINTS) -[domain("tri")] -[partitioning("fractional_odd")] -[outputtopology("triangle_cw")] -[maxtessfactor(MAX_TESSELLATION_FACTOR)] -[outputcontrolpoints(3)] -[patchconstantfunc("HS_PatchConstant")] -TessalationHSToDS HS(InputPatch input, uint ControlPointID : SV_OutputControlPointID) -{ - TessalationHSToDS output; - - // Pass through shader -#define COPY(thing) output.thing = input[ControlPointID].thing; - COPY(Position); - COPY(WorldPosition); - COPY(TexCoord); - COPY(LightmapUV); - COPY(WorldNormal); - COPY(HolesMask); - COPY(TessellationMultiplier); -#if USE_TERRAIN_LAYERS - COPY(Layers); -#endif -#if USE_CUSTOM_VERTEX_INTERPOLATORS - COPY(CustomVSToPS); -#endif -#undef COPY - - return output; -} - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - -// Orthogonal projection on to plane -float3 ProjectOntoPlane(float3 planeNormal, float3 planePoint, float3 pointToProject) -{ - return pointToProject - dot(pointToProject-planePoint, planeNormal) * planeNormal; -} - -#endif - -META_DS(USE_TESSELLATION, FEATURE_LEVEL_SM5) -[domain("tri")] -TessalationDSToPS DS(TessalationPatch constantData, float3 barycentricCoords : SV_DomainLocation, const OutputPatch input) -{ - TessalationDSToPS output; - - // Get the barycentric coords - float U = barycentricCoords.x; - float V = barycentricCoords.y; - float W = barycentricCoords.z; - - // Interpolate patch attributes to generated vertices -#define INTERPOLATE(thing) output.thing = U * input[0].thing + V * input[1].thing + W * input[2].thing -#define COPY(thing) output.thing = input[0].thing - INTERPOLATE(Position); -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PN - float UU = U * U; - float VV = V * V; - float WW = W * W; - float UU3 = UU * 3.0f; - float VV3 = VV * 3.0f; - float WW3 = WW * 3.0f; - - // Interpolate using barycentric coordinates and PN Triangle control points - output.WorldPosition = - input[0].WorldPosition * UU * U + - input[1].WorldPosition * VV * V + - input[2].WorldPosition * WW * W + - constantData.B210 * UU3 * V + - constantData.B120 * VV3 * U + - constantData.B021 * VV3 * W + - constantData.B012 * WW3 * V + - constantData.B102 * WW3 * U + - constantData.B201 * UU3 * W + - constantData.B111 * 6.0f * W * U * V; -#else - INTERPOLATE(WorldPosition); -#endif - INTERPOLATE(TexCoord); - INTERPOLATE(LightmapUV); - INTERPOLATE(WorldNormal); - INTERPOLATE(HolesMask); -#if USE_TERRAIN_LAYERS - UNROLL - for (int i = 0; i < TERRAIN_LAYERS_DATA_SIZE; i++) - { - INTERPOLATE(Layers[i]); - } -#endif -#if USE_CUSTOM_VERTEX_INTERPOLATORS - UNROLL - for (int i = 0; i < CUSTOM_VERTEX_INTERPOLATORS_COUNT; i++) - { - INTERPOLATE(CustomVSToPS[i]); - } -#endif -#undef INTERPOLATE -#undef COPY - - // Interpolating normal can unnormalize it, so normalize it - output.WorldNormal = normalize(output.WorldNormal); - -#if MATERIAL_TESSELLATION == MATERIAL_TESSELLATION_PHONG - // Orthogonal projection in the tangent planes - float3 posProjectedU = ProjectOntoPlane(input[0].WorldNormal, input[0].WorldPosition, output.WorldPosition); - float3 posProjectedV = ProjectOntoPlane(input[1].WorldNormal, input[1].WorldPosition, output.WorldPosition); - float3 posProjectedW = ProjectOntoPlane(input[2].WorldNormal, input[2].WorldPosition, output.WorldPosition); - - // Interpolate the projected points - output.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW; -#endif - - // Perform displacement mapping -#if USE_DISPLACEMENT - MaterialInput materialInput = GetMaterialInput(output); - Material material = GetMaterialDS(materialInput); - output.WorldPosition += material.WorldDisplacement; -#endif - - // Recalculate the clip space position - output.Position = mul(float4(output.WorldPosition, 1), ViewProjectionMatrix); - - return output; -} - -#endif - -#if USE_LIGHTMAP - -float3 SampleLightmap(Material material, MaterialInput materialInput) -{ - // Sample lightmaps - float4 lightmap0 = Lightmap0.Sample(SamplerLinearClamp, materialInput.LightmapUV); - float4 lightmap1 = Lightmap1.Sample(SamplerLinearClamp, materialInput.LightmapUV); - float4 lightmap2 = Lightmap2.Sample(SamplerLinearClamp, materialInput.LightmapUV); - - // Unpack H-basis - float3 h0 = float3(lightmap0.x, lightmap1.x, lightmap2.x); - float3 h1 = float3(lightmap0.y, lightmap1.y, lightmap2.y); - float3 h2 = float3(lightmap0.z, lightmap1.z, lightmap2.z); - float3 h3 = float3(lightmap0.w, lightmap1.w, lightmap2.w); - - // Sample baked diffuse irradiance from the H-basis coefficients - float3 normal = material.TangentNormal; -#if MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE - normal *= material.TangentNormal; -#endif - return GetHBasisIrradiance(normal, h0, h1, h2, h3) / PI; -} - -#endif - // Pixel Shader function for GBuffer Pass META_PS(true, FEATURE_LEVEL_ES2) META_PERMUTATION_1(USE_LIGHTMAP=0) diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index 322726a3e..585f4f1fd 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -194,7 +194,7 @@ bool DeferredMaterialShader::Load() psDesc.VS = _shader->GetVS("VS", 1); _cacheInstanced.Default.Init(psDesc); - // GBuffer Pass with lightmap (use pixel shader permutation for USE_LIGHTMAP=1) + // GBuffer Pass with lightmap (pixel shader permutation for USE_LIGHTMAP=1) psDesc.VS = _shader->GetVS("VS"); psDesc.PS = _shader->GetPS("PS_GBuffer", 1); _cache.DefaultLightmap.Init(psDesc); @@ -210,21 +210,16 @@ bool DeferredMaterialShader::Load() psDesc.DepthWriteEnable = false; psDesc.DepthTestEnable = true; psDesc.DepthFunc = ComparisonFunc::LessEqual; - if (useTess) - { - psDesc.HS = _shader->GetHS("HS", 1); - psDesc.DS = _shader->GetDS("DS", 1); - } - psDesc.VS = _shader->GetVS("VS", 2); + psDesc.VS = _shader->GetVS("VS"); psDesc.PS = _shader->GetPS("PS_MotionVectors"); _cache.MotionVectors.Init(psDesc); // Motion Vectors pass with skinning - psDesc.VS = _shader->GetVS("VS_Skinned", 1); + psDesc.VS = _shader->GetVS("VS_Skinned"); _cache.MotionVectorsSkinned.Init(psDesc); // Motion Vectors pass with skinning (with per-bone motion blur) - psDesc.VS = _shader->GetVS("VS_Skinned", 2); + psDesc.VS = _shader->GetVS("VS_Skinned", 1); _cache.MotionVectorsSkinnedPerBone.Init(psDesc); // Depth Pass diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index a07e740f8..0355b7435 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -139,13 +139,13 @@ bool LightmapFeature::Bind(MaterialShader::BindParameters& params, byte*& cb, in { // Bind lightmap textures GPUTexture *lightmap0, *lightmap1, *lightmap2; - drawCall.Surface.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); - context->BindSR(0, lightmap0); - context->BindSR(1, lightmap1); - context->BindSR(2, lightmap2); + drawCall.Features.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); + context->BindSR(srv + 0, lightmap0); + context->BindSR(srv + 1, lightmap1); + context->BindSR(srv + 2, lightmap2); // Set lightmap data - data.LightmapArea = drawCall.Surface.LightmapUVsArea; + data.LightmapArea = drawCall.Features.LightmapUVsArea; } srv += SRVs; diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index 68c5fbd20..633448fb5 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "TerrainMaterialShader.h" +#include "MaterialShaderFeatures.h" #include "MaterialParams.h" #include "Engine/Engine/Time.h" #include "Engine/Graphics/GPUContext.h" @@ -25,7 +26,6 @@ PACK_STRUCT(struct TerrainMaterialShaderData { float TimeParam; Vector4 ViewInfo; Vector4 ScreenSize; - Rectangle LightmapArea; Vector3 WorldInvScale; float WorldDeterminantSign; float PerInstanceRandom; @@ -56,11 +56,21 @@ void TerrainMaterialShader::Bind(BindParameters& params) auto& drawCall = *params.FirstDrawCall; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0->GetSize() != 0; + ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing + byte* cb = _cb0Data.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(TerrainMaterialShaderData); + int32 srv = 3; + + // Setup features + if (_info.TessellationMode != TessellationMethod::None) + TessellationFeature::Bind(params, cb, srv); + const bool useLightmap = LightmapFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(TerrainMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = false; @@ -68,64 +78,42 @@ void TerrainMaterialShader::Bind(BindParameters& params) MaterialParams::Bind(params.ParamsLink, bindMeta); // Setup material constants data - auto data = reinterpret_cast(_cb0Data.Get()); if (hasCb0) { - Matrix::Transpose(view.Frustum.GetMatrix(), data->ViewProjectionMatrix); - Matrix::Transpose(drawCall.World, data->WorldMatrix); - Matrix::Transpose(view.View, data->ViewMatrix); + Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); + Matrix::Transpose(drawCall.World, materialData->WorldMatrix); + Matrix::Transpose(view.View, materialData->ViewMatrix); - data->ViewPos = view.Position; - data->ViewFar = view.Far; - data->ViewDir = view.Direction; - data->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds(); - data->ViewInfo = view.ViewInfo; - data->ScreenSize = view.ScreenSize; - - // Extract per axis scales from LocalToWorld transform + materialData->ViewPos = view.Position; + materialData->ViewFar = view.Far; + materialData->ViewDir = view.Direction; + materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds(); + materialData->ViewInfo = view.ViewInfo; + materialData->ScreenSize = view.ScreenSize; const float scaleX = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13).Length(); const float scaleY = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23).Length(); const float scaleZ = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33).Length(); - data->WorldInvScale = Vector3( + materialData->WorldInvScale = Vector3( scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); - - data->WorldDeterminantSign = drawCall.WorldDeterminantSign; - data->PerInstanceRandom = drawCall.PerInstanceRandom; - data->CurrentLOD = drawCall.Terrain.CurrentLOD; - data->ChunkSizeNextLOD = drawCall.Terrain.ChunkSizeNextLOD; - data->TerrainChunkSizeLOD0 = drawCall.Terrain.TerrainChunkSizeLOD0; - data->HeightmapUVScaleBias = drawCall.Terrain.HeightmapUVScaleBias; - data->NeighborLOD = drawCall.Terrain.NeighborLOD; - data->OffsetUV = drawCall.Terrain.OffsetUV; - } - const bool useLightmap = view.Flags & ViewFlags::GI -#if USE_EDITOR - && EnableLightmapsUsage -#endif - && view.Pass == DrawPass::GBuffer - && drawCall.Terrain.Lightmap != nullptr; - if (useLightmap) - { - // Bind lightmap textures - GPUTexture *lightmap0, *lightmap1, *lightmap2; - drawCall.Terrain.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2); - context->BindSR(0, lightmap0); - context->BindSR(1, lightmap1); - context->BindSR(2, lightmap2); - - // Set lightmap data - data->LightmapArea = drawCall.Terrain.LightmapUVsArea; + materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; + materialData->PerInstanceRandom = drawCall.PerInstanceRandom; + materialData->CurrentLOD = drawCall.Terrain.CurrentLOD; + materialData->ChunkSizeNextLOD = drawCall.Terrain.ChunkSizeNextLOD; + materialData->TerrainChunkSizeLOD0 = drawCall.Terrain.TerrainChunkSizeLOD0; + materialData->HeightmapUVScaleBias = drawCall.Terrain.HeightmapUVScaleBias; + materialData->NeighborLOD = drawCall.Terrain.NeighborLOD; + materialData->OffsetUV = drawCall.Terrain.OffsetUV; } // Bind terrain textures - const auto heightmap = drawCall.Terrain.Patch->Heightmap.Get()->GetTexture(); + const auto heightmap = drawCall.Terrain.Patch->Heightmap->GetTexture(); const auto splatmap0 = drawCall.Terrain.Patch->Splatmap[0] ? drawCall.Terrain.Patch->Splatmap[0]->GetTexture() : nullptr; const auto splatmap1 = drawCall.Terrain.Patch->Splatmap[1] ? drawCall.Terrain.Patch->Splatmap[1]->GetTexture() : nullptr; - context->BindSR(3, heightmap); - context->BindSR(4, splatmap0); - context->BindSR(5, splatmap1); + context->BindSR(0, heightmap); + context->BindSR(1, splatmap0); + context->BindSR(2, splatmap1); // Bind constants if (hasCb0) diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index 31656145b..0e91366e2 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -167,8 +167,14 @@ struct DrawCall struct { const Lightmap* Lightmap; - SkinnedMeshDrawData* Skinning; Rectangle LightmapUVsArea; + } Features; + + struct + { + const Lightmap* Lightmap; + Rectangle LightmapUVsArea; + SkinnedMeshDrawData* Skinning; Vector3 GeometrySize; // Object geometry size in the world (unscaled). float LODDitherFactor; // The model LOD transition dither progress. Matrix PrevWorld; @@ -176,15 +182,15 @@ struct DrawCall struct { + const Lightmap* Lightmap; + Rectangle LightmapUVsArea; Vector4 HeightmapUVScaleBias; Vector4 NeighborLOD; Vector2 OffsetUV; float CurrentLOD; float ChunkSizeNextLOD; float TerrainChunkSizeLOD0; - Rectangle LightmapUVsArea; const class TerrainPatch* Patch; - const Lightmap* Lightmap; } Terrain; struct diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 61c05149d..b6b1b55e1 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -187,6 +187,11 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo if (materialInfo.BlendMode == MaterialBlendMode::Opaque) ADD_FEATURE(LightmapFeature); break; + case MaterialDomain::Terrain: + if (materialInfo.TessellationMode != TessellationMethod::None) + ADD_FEATURE(TessellationFeature); + ADD_FEATURE(LightmapFeature); + break; default: break; } @@ -387,7 +392,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo srv = 1; break; case MaterialDomain::Terrain: - srv = 6; + srv = 3; // Heightmap + 2 splatmaps break; case MaterialDomain::Particle: srv = 5; diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index aef3fc579..8fc981cd7 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -61,9 +61,6 @@ #ifndef MAX_TESSELLATION_FACTOR #define MAX_TESSELLATION_FACTOR 15 #endif -#ifndef IS_MOTION_VECTORS_PASS - #define IS_MOTION_VECTORS_PASS 0 -#endif #ifndef PER_BONE_MOTION_BLUR #define PER_BONE_MOTION_BLUR 0 #endif @@ -164,6 +161,12 @@ float3x3 CalcTangentBasisFromWorldNormal(float3 normal) return float3x3(tangent, bitangent, normal); } +float3x3 CalcTangentBasis(float3 normal, float4 tangent) +{ + float3 bitangent = cross(normal, tangent.xyz) * tangent.w; + return float3x3(tangent.xyz, bitangent, normal); +} + // [Jimenez et al. 2016, "Practical Realtime Strategies for Accurate Indirect Occlusion"] float3 AOMultiBounce(float visibility, float3 albedo) { From b5847eb0d632cbf2fa9f31e2465b97114709d090 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Feb 2021 15:27:38 +0100 Subject: [PATCH 308/419] Refactor material shaders generator to use modular features as extensions --- .../Features/Distortion.hlsl | 50 ++++++++++++++++++ .../Editor/MaterialTemplates/Particle.shader | 38 -------------- .../MaterialTemplates/SurfaceForward.shader | 51 ++----------------- .../Materials/DeferredMaterialShader.cpp | 2 - .../Materials/ForwardMaterialShader.cpp | 2 - .../Materials/MaterialShaderFeatures.cpp | 9 +++- .../Materials/MaterialShaderFeatures.h | 13 +++-- .../Materials/ParticleMaterialShader.cpp | 13 +++-- .../Materials/TerrainMaterialShader.cpp | 2 - .../MaterialGenerator/MaterialGenerator.cpp | 6 +++ 10 files changed, 86 insertions(+), 100 deletions(-) create mode 100644 Content/Editor/MaterialTemplates/Features/Distortion.hlsl diff --git a/Content/Editor/MaterialTemplates/Features/Distortion.hlsl b/Content/Editor/MaterialTemplates/Features/Distortion.hlsl new file mode 100644 index 000000000..c74014cca --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/Distortion.hlsl @@ -0,0 +1,50 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +@0// Distortion: Defines +@1// Distortion: Includes +@2// Distortion: Constants +@3// Distortion: Resources +@4// Distortion: Utilities +@5// Distortion: Shaders +#if USE_DISTORTION + +// Pixel Shader function for Distortion Pass +META_PS(USE_DISTORTION, FEATURE_LEVEL_ES2) +float4 PS_Distortion(PixelInput input) : SV_Target0 +{ +#if USE_DITHERED_LOD_TRANSITION + // LOD masking + ClipLODTransition(input); +#endif + + // Get material parameters + MaterialInput materialInput = GetMaterialInput(input); + Material material = GetMaterialPS(materialInput); + + // Masking +#if MATERIAL_MASKED + clip(material.Mask - MATERIAL_MASK_THRESHOLD); +#endif + + float3 viewNormal = normalize(TransformWorldVectorToView(materialInput, material.WorldNormal)); + float airIOR = 1.0f; +#if USE_PIXEL_NORMAL_OFFSET_REFRACTION + float3 viewVertexNormal = TransformWorldVectorToView(materialInput, TransformTangentVectorToWorld(materialInput, float3(0, 0, 1))); + float2 distortion = (viewVertexNormal.xy - viewNormal.xy) * (material.Refraction - airIOR); +#else + float2 distortion = viewNormal.xy * (material.Refraction - airIOR); +#endif + + // Clip if the distortion distance (squared) is too small to be noticed + clip(dot(distortion, distortion) - 0.00001); + + // Scale up for better precision in low/subtle refractions at the expense of artefacts at higher refraction + distortion *= 4.0f; + + // Use separate storage for positive and negative offsets + float2 addOffset = max(distortion, 0); + float2 subOffset = abs(min(distortion, 0)); + return float4(addOffset.x, addOffset.y, subOffset.x, subOffset.y); +} + +#endif diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index f3121b6b0..72d633028 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -802,44 +802,6 @@ float4 PS_Forward(PixelInput input) : SV_Target0 return output; } -#if USE_DISTORTION - -// Pixel Shader function for Distortion Pass -META_PS(USE_DISTORTION, FEATURE_LEVEL_ES2) -float4 PS_Distortion(PixelInput input) : SV_Target0 -{ - // Get material parameters - MaterialInput materialInput = GetMaterialInput(input); - Material material = GetMaterialPS(materialInput); - - // Masking -#if MATERIAL_MASKED - clip(material.Mask - MATERIAL_MASK_THRESHOLD); -#endif - - float3 viewNormal = normalize(TransformWorldVectorToView(materialInput, material.WorldNormal)); - float airIOR = 1.0f; -#if USE_PIXEL_NORMAL_OFFSET_REFRACTION - float3 viewVertexNormal = TransformWorldVectorToView(materialInput, TransformTangentVectorToWorld(materialInput, float3(0, 0, 1))); - float2 distortion = (viewVertexNormal.xy - viewNormal.xy) * (material.Refraction - airIOR); -#else - float2 distortion = viewNormal.xy * (material.Refraction - airIOR); -#endif - - // Clip if the distortion distance (squared) is too small to be noticed - clip(dot(distortion, distortion) - 0.00001); - - // Scale up for better precision in low/subtle refractions at the expense of artefacts at higher refraction - distortion *= 4.0f; - - // Use separate storage for positive and negative offsets - float2 addOffset = max(distortion, 0); - float2 subOffset = abs(min(distortion, 0)); - return float4(addOffset.x, addOffset.y, subOffset.x, subOffset.y); -} - -#endif - // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input diff --git a/Content/Editor/MaterialTemplates/SurfaceForward.shader b/Content/Editor/MaterialTemplates/SurfaceForward.shader index 0e9c6db5f..cece1b231 100644 --- a/Content/Editor/MaterialTemplates/SurfaceForward.shader +++ b/Content/Editor/MaterialTemplates/SurfaceForward.shader @@ -562,12 +562,6 @@ void ClipLODTransition(PixelInput input) } } -#else - -void ClipLODTransition(PixelInput input) -{ -} - #endif // Pixel Shader function for Forward Pass @@ -576,9 +570,11 @@ float4 PS_Forward(PixelInput input) : SV_Target0 { float4 output = 0; +#if USE_DITHERED_LOD_TRANSITION // LOD masking ClipLODTransition(input); // TODO: make model LOD transition smoother for transparent materials by using opacity to reduce aliasing +#endif // Get material parameters MaterialInput materialInput = GetMaterialInput(input); @@ -665,47 +661,6 @@ float4 PS_Forward(PixelInput input) : SV_Target0 return output; } -#if USE_DISTORTION - -// Pixel Shader function for Distortion Pass -META_PS(USE_DISTORTION, FEATURE_LEVEL_ES2) -float4 PS_Distortion(PixelInput input) : SV_Target0 -{ - // LOD masking - ClipLODTransition(input); - - // Get material parameters - MaterialInput materialInput = GetMaterialInput(input); - Material material = GetMaterialPS(materialInput); - - // Masking -#if MATERIAL_MASKED - clip(material.Mask - MATERIAL_MASK_THRESHOLD); -#endif - - float3 viewNormal = normalize(TransformWorldVectorToView(materialInput, material.WorldNormal)); - float airIOR = 1.0f; -#if USE_PIXEL_NORMAL_OFFSET_REFRACTION - float3 viewVertexNormal = TransformWorldVectorToView(materialInput, TransformTangentVectorToWorld(materialInput, float3(0, 0, 1))); - float2 distortion = (viewVertexNormal.xy - viewNormal.xy) * (material.Refraction - airIOR); -#else - float2 distortion = viewNormal.xy * (material.Refraction - airIOR); -#endif - - // Clip if the distortion distance (squared) is too small to be noticed - clip(dot(distortion, distortion) - 0.00001); - - // Scale up for better precision in low/subtle refractions at the expense of artefacts at higher refraction - distortion *= 4.0f; - - // Use separate storage for positive and negative offsets - float2 addOffset = max(distortion, 0); - float2 subOffset = abs(min(distortion, 0)); - return float4(addOffset.x, addOffset.y, subOffset.x, subOffset.y); -} - -#endif - // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input @@ -714,8 +669,10 @@ void PS_Depth(PixelInput input #endif ) { +#if USE_DITHERED_LOD_TRANSITION // LOD masking ClipLODTransition(input); +#endif // Get material parameters MaterialInput materialInput = GetMaterialInput(input); diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index 585f4f1fd..3bd71290d 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -70,8 +70,6 @@ void DeferredMaterialShader::Bind(BindParameters& params) int32 srv = 0; // Setup features - if (_info.TessellationMode != TessellationMethod::None) - TessellationFeature::Bind(params, cb, srv); const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv); // Setup parameters diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index 7dc68a85a..f410d477c 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -79,8 +79,6 @@ void ForwardMaterialShader::Bind(BindParameters& params) int32 srv = 0; // Setup features - if (_info.TessellationMode != TessellationMethod::None) - TessellationFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 0355b7435..0043a8d6e 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -159,7 +159,7 @@ void TessellationFeature::Generate(GeneratorData& data) { data.Template = TEXT("Tessellation.hlsl"); data.ConstantsSize = 0; - data.ResourcesCount = SRVs; + data.ResourcesCount = 0; } void LightmapFeature::Generate(GeneratorData& data) @@ -169,4 +169,11 @@ void LightmapFeature::Generate(GeneratorData& data) data.ResourcesCount = SRVs; } +void DistortionFeature::Generate(GeneratorData& data) +{ + data.Template = TEXT("Distortion.hlsl"); + data.ConstantsSize = 0; + data.ResourcesCount = 0; +} + #endif diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h index 0a2349dfe..9d2c06307 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h @@ -43,11 +43,6 @@ struct ForwardShadingFeature : MaterialShaderFeature // Material shader feature that adds geometry hardware tessellation (using Hull and Domain shaders). struct TessellationFeature : MaterialShaderFeature { - enum { SRVs = 0 }; - - static void Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv) - { - } #if USE_EDITOR static void Generate(GeneratorData& data); #endif @@ -68,3 +63,11 @@ struct LightmapFeature : MaterialShaderFeature static void Generate(GeneratorData& data); #endif }; + +// Material shader feature that adds distortion vectors rendering pass. +struct DistortionFeature : MaterialShaderFeature +{ +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif +}; diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index 5cfad0126..bfab56daf 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -72,16 +72,23 @@ void ParticleMaterialShader::Bind(BindParameters& params) auto& view = params.RenderContext.View; auto cache = params.RenderContext.List; auto& drawCall = *params.FirstDrawCall; + const uint32 sortedIndicesOffset = drawCall.Particle.Module->SortedIndicesOffset; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0->GetSize() != 0; + ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing const auto cb1 = _shader->GetCB(1); - const bool hasCb1 = cb1->GetSize() != 0; - const uint32 sortedIndicesOffset = drawCall.Particle.Module->SortedIndicesOffset; + const bool hasCb1 = cb1 && cb1->GetSize() != 0; + byte* cb = _cb0Data.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(ParticleMaterialShaderData); + int32 srv = 0; + + // Setup features // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(ParticleMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = nullptr; bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth; diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index 633448fb5..89a96d902 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -63,8 +63,6 @@ void TerrainMaterialShader::Bind(BindParameters& params) int32 srv = 3; // Setup features - if (_info.TessellationMode != TessellationMethod::None) - TessellationFeature::Bind(params, cb, srv); const bool useLightmap = LightmapFeature::Bind(params, cb, srv); // Setup parameters diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index b6b1b55e1..8550febad 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -186,6 +186,8 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo ADD_FEATURE(TessellationFeature); if (materialInfo.BlendMode == MaterialBlendMode::Opaque) ADD_FEATURE(LightmapFeature); + if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0) + ADD_FEATURE(DistortionFeature); break; case MaterialDomain::Terrain: if (materialInfo.TessellationMode != TessellationMethod::None) @@ -193,6 +195,10 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo ADD_FEATURE(LightmapFeature); break; default: + case MaterialDomain::Particle: + if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0) + ADD_FEATURE(DistortionFeature); + break; break; } #undef ADD_FEATURE From 7868c6505d84cc450ceb9fb7e3ba03d7ae3fe46f Mon Sep 17 00:00:00 2001 From: SilentCLD Date: Thu, 4 Feb 2021 17:49:54 +0000 Subject: [PATCH 309/419] [Editor/Docking] Bring window to front on focus --- Source/Editor/GUI/Docking/DockWindow.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index aa6d94ca8..56c78efed 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -307,6 +307,14 @@ namespace FlaxEditor.GUI.Docking _dockedTo?.SelectTab(this, autoFocus); } + /// + /// Brings the window to the front of the Z order. + /// + public void BringToFront() + { + _dockedTo?.RootWindow?.BringToFront(); + } + internal void OnUnlinkInternal() { OnUnlink(); @@ -412,6 +420,7 @@ namespace FlaxEditor.GUI.Docking base.Focus(); SelectTab(); + BringToFront(); } /// From ae785267c2426444bde9fa60e823e46f0ba58c50 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Feb 2021 22:32:37 +0100 Subject: [PATCH 310/419] Fix scaling rotated objects in world space Fixes #200 --- Source/Editor/Gizmo/TransformGizmoBase.cs | 63 ++++++++++++----------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 93dbeddaa..014143685 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -196,12 +196,10 @@ namespace FlaxEditor.Gizmo private void UpdateTranslateScale() { bool isScaling = _activeMode == Mode.Scale; - Vector3 delta = Vector3.Zero; Ray ray = Owner.MouseRay; - Matrix invRotationMatrix; - Matrix.Invert(ref _rotationMatrix, out invRotationMatrix); + Matrix.Invert(ref _rotationMatrix, out var invRotationMatrix); ray.Position = Vector3.Transform(ray.Position, invRotationMatrix); Vector3.TransformNormal(ref ray.Direction, ref invRotationMatrix, out ray.Direction); @@ -211,9 +209,7 @@ namespace FlaxEditor.Gizmo case Axis.X: { var plane = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z); - - float intersection; - if (ray.Intersects(ref plane, out intersection)) + if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) @@ -222,18 +218,14 @@ namespace FlaxEditor.Gizmo ? new Vector3(_tDelta.X, 0, 0) : new Vector3(_tDelta.X, _tDelta.Y, 0); } - break; } - case Axis.Z: case Axis.YZ: case Axis.Y: { var plane = new Plane(Vector3.Left, Vector3.Transform(Position, invRotationMatrix).X); - - float intersection; - if (ray.Intersects(ref plane, out intersection)) + if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) @@ -251,41 +243,31 @@ namespace FlaxEditor.Gizmo break; } } - break; } - case Axis.ZX: { var plane = new Plane(Vector3.Down, Vector3.Transform(Position, invRotationMatrix).Y); - - float intersection; - if (ray.Intersects(ref plane, out intersection)) + if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) _tDelta = _intersectPosition - _lastIntersectionPosition; delta = new Vector3(_tDelta.X, 0, _tDelta.Z); } - break; } - case Axis.Center: { - Vector3 gizmoToView = Position - Owner.ViewPosition; + var gizmoToView = Position - Owner.ViewPosition; var plane = new Plane(-Vector3.Normalize(gizmoToView), gizmoToView.Length); - - float intersection; - if (ray.Intersects(ref plane, out intersection)) + if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) _tDelta = _intersectPosition - _lastIntersectionPosition; } - delta = _tDelta; - break; } } @@ -299,14 +281,11 @@ namespace FlaxEditor.Gizmo if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping) { float snapValue = isScaling ? ScaleSnapValue : TranslationSnapValue; - _translationScaleSnapDelta += delta; - delta = new Vector3( (int)(_translationScaleSnapDelta.X / snapValue) * snapValue, (int)(_translationScaleSnapDelta.Y / snapValue) * snapValue, (int)(_translationScaleSnapDelta.Z / snapValue) * snapValue); - _translationScaleSnapDelta -= delta; } @@ -318,7 +297,30 @@ namespace FlaxEditor.Gizmo } else if (_activeMode == Mode.Scale) { - // Apply scale + // Scale + if (_activeTransformSpace == TransformSpace.World && _activeAxis != Axis.Center) + { + var deltaLocal = delta; + Quaternion orientation = GetSelectedObject(0).Orientation; + delta = Vector3.Transform(delta, orientation); + + // Fix axis sign of delta movement for rotated object in some cases (eg. rotated object by 90 deg on Y axis and scale in world space with Red/X axis) + switch (_activeAxis) + { + case Axis.X: + if (deltaLocal.X < 0) + delta *= -1; + break; + case Axis.Y: + if (deltaLocal.Y < 0) + delta *= -1; + break; + case Axis.Z: + if (deltaLocal.Z < 0) + delta *= -1; + break; + } + } _scaleDelta = delta; } } @@ -382,7 +384,7 @@ namespace FlaxEditor.Gizmo // Snap to ground if (_activeAxis == Axis.None && SelectionCount != 0 && Owner.SnapToGround) { - if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, int.MaxValue, false)) + if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, uint.MaxValue, false)) { StartTransforming(); var translationDelta = hit.Point - Position; @@ -408,7 +410,6 @@ namespace FlaxEditor.Gizmo case Mode.Translate: UpdateTranslateScale(); break; - case Mode.Rotate: UpdateRotate(dt); break; @@ -437,7 +438,7 @@ namespace FlaxEditor.Gizmo translationDelta = _translationDelta; _translationDelta = Vector3.Zero; - // Prevent from moving objects too far away, like to different galaxy or sth + // Prevent from moving objects too far away, like to a different galaxy or sth Vector3 prevMoveDelta = _accMoveDelta; _accMoveDelta += _translationDelta; if (_accMoveDelta.Length > Owner.ViewFarPlane * 0.7f) From db55f5ea3f838aed0767568480377b8581f66149 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Feb 2021 22:59:13 +0100 Subject: [PATCH 311/419] Fix crash when changing prefab root object --- .../Editor/Viewport/Previews/PrefabPreview.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index d0ed8a0c2..a9f5a7d22 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -72,7 +72,28 @@ namespace FlaxEditor.Viewport.Previews public Actor Instance { get => _instance; - internal set => _instance = value; + internal set + { + if (_instance == value) + return; + + if (_instance) + { + if (customControlLinked != null) + { + customControlLinked.Parent = null; + customControlLinked = null; + } + Task.RemoveCustomActor(_instance); + } + + _instance = value; + + if (_instance) + { + Task.AddCustomActor(_instance); + } + } } /// From 3661b19d6ccd0fb52156c9bce187a1789a986f33 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Feb 2021 23:34:44 +0100 Subject: [PATCH 312/419] Fix some prefabs issues --- Source/Editor/Editor.cs | 2 +- .../Editor/Viewport/Previews/PrefabPreview.cs | 73 +++++++++++-------- .../Windows/Assets/PrefabWindow.Hierarchy.cs | 5 +- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 59810043d..f81f81ddd 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -254,7 +254,7 @@ namespace FlaxEditor if (loadingPreview != null) { // Link it to the prefab preview to see it in the editor - loadingPreview.customControlLinked = control.Control; + loadingPreview.customControlLinked = control; return loadingPreview; } return null; diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index a9f5a7d22..aa04a41fd 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using FlaxEngine; -using FlaxEngine.GUI; using Object = FlaxEngine.Object; namespace FlaxEditor.Viewport.Previews @@ -19,7 +18,7 @@ namespace FlaxEditor.Viewport.Previews private Prefab _prefab; private Actor _instance; - internal Control customControlLinked; + internal UIControl customControlLinked; /// /// Gets or sets the prefab asset to preview. @@ -29,39 +28,47 @@ namespace FlaxEditor.Viewport.Previews get => _prefab; set { - if (_prefab != value) + if (_prefab == value) + return; + + if (_instance) { - if (_instance) + // Unlink UI control + if (customControlLinked) { - if (customControlLinked != null) - { - customControlLinked.Parent = null; - customControlLinked = null; - } - Task.RemoveCustomActor(_instance); - Object.Destroy(_instance); + if (customControlLinked.Control?.Parent == this) + customControlLinked.Control.Parent = null; + customControlLinked = null; } - _prefab = value; + // Remove for preview + Task.RemoveCustomActor(_instance); - if (_prefab) + // Delete + Object.Destroy(_instance); + } + + _prefab = value; + + if (_prefab) + { + _prefab.WaitForLoaded(); + + var prevPreview = LoadingPreview; + LoadingPreview = this; + + _instance = PrefabManager.SpawnPrefab(_prefab, null); + + LoadingPreview = prevPreview; + + if (_instance == null) { - _prefab.WaitForLoaded(); // TODO: use lazy prefab spawning to reduce stalls - - var prevPreview = LoadingPreview; - LoadingPreview = this; - - _instance = PrefabManager.SpawnPrefab(_prefab, null); - - LoadingPreview = prevPreview; - - if (_instance == null) - { - _prefab = null; - throw new FlaxException("Failed to spawn a prefab for the preview."); - } - Task.AddCustomActor(_instance); + _prefab = null; + throw new FlaxException("Failed to spawn a prefab for the preview."); } + + // Add to preview + Task.AddCustomActor(_instance); } } } @@ -79,11 +86,15 @@ namespace FlaxEditor.Viewport.Previews if (_instance) { - if (customControlLinked != null) + // Unlink UI control + if (customControlLinked) { - customControlLinked.Parent = null; + if (customControlLinked.Control?.Parent == this) + customControlLinked.Control.Parent = null; customControlLinked = null; } + + // Remove for preview Task.RemoveCustomActor(_instance); } @@ -91,6 +102,7 @@ namespace FlaxEditor.Viewport.Previews if (_instance) { + // Add to preview Task.AddCustomActor(_instance); } } @@ -108,7 +120,6 @@ namespace FlaxEditor.Viewport.Previews /// public override void OnDestroy() { - // Cleanup Prefab = null; base.OnDestroy(); diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 08621f2ec..bded7e147 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -69,6 +69,7 @@ namespace FlaxEditor.Windows.Assets bool hasSthSelected = Selection.Count > 0; bool isSingleActorSelected = Selection.Count == 1 && Selection[0] is ActorNode; bool isRootSelected = isSingleActorSelected && Selection[0] == Graph.Main; + bool hasPrefabLink = isSingleActorSelected && (Selection[0] as ActorNode).HasPrefabLink; // Create popup @@ -97,7 +98,7 @@ namespace FlaxEditor.Windows.Assets b.Enabled = hasSthSelected && !isRootSelected; b = contextMenu.AddButton("Set Root", SetRoot); - b.Enabled = isSingleActorSelected && !isRootSelected; + b.Enabled = isSingleActorSelected && !isRootSelected && hasPrefabLink; // Prefab options @@ -108,8 +109,6 @@ namespace FlaxEditor.Windows.Assets (Selection[0] as ActorNode).CanCreatePrefab && Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets; - bool hasPrefabLink = isSingleActorSelected && (Selection[0] as ActorNode).HasPrefabLink; - b = contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab); b.Enabled = hasPrefabLink; From 01777a2c1b5f87409b67ceeccac31d8ee637cdc5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 11:22:47 +0100 Subject: [PATCH 313/419] Refactor material shaders generator to use modular features as extensions --- .../Features/ForwardShading.hlsl | 123 ++++++++++++ Content/Editor/MaterialTemplates/GUI.shader | 3 +- .../Editor/MaterialTemplates/Particle.shader | 126 +----------- .../MaterialTemplates/PostProcess.shader | 2 +- .../MaterialTemplates/SurfaceDeferred.shader | 3 +- .../MaterialTemplates/SurfaceForward.shader | 124 ------------ .../Editor/MaterialTemplates/Terrain.shader | 3 +- .../Materials/ForwardMaterialShader.cpp | 136 +------------ .../Graphics/Materials/MaterialShader.cpp | 6 - .../Graphics/Materials/MaterialShader.h | 1 - .../Materials/MaterialShaderFeatures.cpp | 13 +- .../Materials/MaterialShaderFeatures.h | 3 + .../Materials/ParticleMaterialShader.cpp | 180 +++--------------- .../Materials/PostFxMaterialShader.cpp | 1 - .../DirectX/DX11/GPUContextDX11.cpp | 6 +- .../MaterialGenerator/MaterialGenerator.cpp | 15 +- 16 files changed, 182 insertions(+), 563 deletions(-) create mode 100644 Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl diff --git a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl new file mode 100644 index 000000000..085c66291 --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl @@ -0,0 +1,123 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +@0// Forward Shading: Defines +#define MAX_LOCAL_LIGHTS 4 +@1// Forward Shading: Includes +#include "./Flax/LightingCommon.hlsl" +#if USE_REFLECTIONS +#include "./Flax/ReflectionsCommon.hlsl" +#endif +#include "./Flax/Lighting.hlsl" +#include "./Flax/ShadowsSampling.hlsl" +#include "./Flax/ExponentialHeightFog.hlsl" +@2// Forward Shading: Constants +LightData DirectionalLight; +LightShadowData DirectionalLightShadow; +LightData SkyLight; +ProbeData EnvironmentProbe; +ExponentialHeightFogData ExponentialHeightFog; +float3 Dummy2; +uint LocalLightsCount; +LightData LocalLights[MAX_LOCAL_LIGHTS]; +@3// Forward Shading: Resources +TextureCube EnvProbe : register(t__SRV__); +TextureCube SkyLightTexture : register(t__SRV__); +Texture2DArray DirectionalLightShadowMap : register(t__SRV__); +@4// Forward Shading: Utilities +DECLARE_LIGHTSHADOWDATA_ACCESS(DirectionalLightShadow); +@5// Forward Shading: Shaders +// Pixel Shader function for Forward Pass +META_PS(USE_FORWARD, FEATURE_LEVEL_ES2) +float4 PS_Forward(PixelInput input) : SV_Target0 +{ + float4 output = 0; + +#if USE_DITHERED_LOD_TRANSITION + // LOD masking + ClipLODTransition(input); +#endif + + // Get material parameters + MaterialInput materialInput = GetMaterialInput(input); + Material material = GetMaterialPS(materialInput); + + // Masking +#if MATERIAL_MASKED + clip(material.Mask - MATERIAL_MASK_THRESHOLD); +#endif + + // Add emissive light + output = float4(material.Emissive, material.Opacity); + +#if MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT + + // Setup GBuffer data as proxy for lighting + GBufferSample gBuffer; + gBuffer.Normal = material.WorldNormal; + gBuffer.Roughness = material.Roughness; + gBuffer.Metalness = material.Metalness; + gBuffer.Color = material.Color; + gBuffer.Specular = material.Specular; + gBuffer.AO = material.AO; + gBuffer.ViewPos = mul(float4(materialInput.WorldPosition, 1), ViewMatrix).xyz; +#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE + gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity); +#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE + gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity); +#else + gBuffer.CustomData = float4(0, 0, 0, 0); +#endif + gBuffer.WorldPos = materialInput.WorldPosition; + gBuffer.ShadingModel = MATERIAL_SHADING_MODEL; + + // Calculate lighting from a single directional light + float4 shadowMask = 1.0f; + if (DirectionalLight.CastShadows > 0) + { + LightShadowData directionalLightShadowData = GetDirectionalLightShadowData(); + shadowMask.r = SampleShadow(DirectionalLight, directionalLightShadowData, DirectionalLightShadowMap, gBuffer, shadowMask.g); + } + float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false); + + // Calculate lighting from sky light + light += GetSkyLightLighting(SkyLight, gBuffer, SkyLightTexture); + + // Calculate lighting from local lights + LOOP + for (uint localLightIndex = 0; localLightIndex < LocalLightsCount; localLightIndex++) + { + const LightData localLight = LocalLights[localLightIndex]; + bool isSpotLight = localLight.SpotAngles.x > -2.0f; + shadowMask = 1.0f; + light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight); + } + +#if USE_REFLECTIONS + // Calculate reflections + light.rgb += GetEnvProbeLighting(ViewPos, EnvProbe, EnvironmentProbe, gBuffer) * light.a; +#endif + + // Add lighting (apply ambient occlusion) + output.rgb += light.rgb * gBuffer.AO; + +#if USE_FOG + // Calculate exponential height fog + float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0); + + // Apply fog to the output color +#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE + output = float4(output.rgb * fog.a + fog.rgb, output.a); +#elif MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT + output = float4(output.rgb * fog.a + fog.rgb, output.a); +#elif MATERIAL_BLEND == MATERIAL_BLEND_ADDITIVE + output = float4(output.rgb * fog.a + fog.rgb, output.a * fog.a); +#elif MATERIAL_BLEND == MATERIAL_BLEND_MULTIPLY + output = float4(lerp(float3(1, 1, 1), output.rgb, fog.aaa * fog.aaa), output.a); +#endif + +#endif + +#endif + + return output; +} diff --git a/Content/Editor/MaterialTemplates/GUI.shader b/Content/Editor/MaterialTemplates/GUI.shader index b927755ad..7eced6390 100644 --- a/Content/Editor/MaterialTemplates/GUI.shader +++ b/Content/Editor/MaterialTemplates/GUI.shader @@ -3,7 +3,6 @@ #define MATERIAL 1 @3 - #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" #include "./Flax/GBufferCommon.hlsl" @@ -22,7 +21,7 @@ float4 ViewInfo; float4 ScreenSize; @1META_CB_END -// Material shader resources +// Shader resources @2 // Interpolants passed from the vertex shader struct VertexOutput diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index 72d633028..a3befc24f 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -2,19 +2,13 @@ // Version: @0 #define MATERIAL 1 -#define MAX_LOCAL_LIGHTS 4 @3 +// Ribbons don't use sorted indices so overlap the segment distances buffer on the slot +#define HAS_SORTED_INDICES (!defined(_VS_Ribbon)) #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" #include "./Flax/GBufferCommon.hlsl" -#include "./Flax/LightingCommon.hlsl" -#if USE_REFLECTIONS -#include "./Flax/ReflectionsCommon.hlsl" -#endif -#include "./Flax/Lighting.hlsl" -#include "./Flax/ShadowsSampling.hlsl" -#include "./Flax/ExponentialHeightFog.hlsl" #include "./Flax/Matrix.hlsl" @7 struct SpriteInput @@ -55,44 +49,19 @@ uint RibbonSegmentCount; float4x4 WorldMatrixInverseTransposed; @1META_CB_END -// Secondary constantant buffer (for lighting) -META_CB_BEGIN(1, LightingData) -LightData DirectionalLight; -LightShadowData DirectionalLightShadow; -LightData SkyLight; -ProbeData EnvironmentProbe; -ExponentialHeightFogData ExponentialHeightFog; -float3 Dummy1; -uint LocalLightsCount; -LightData LocalLights[MAX_LOCAL_LIGHTS]; -META_CB_END - -DECLARE_LIGHTSHADOWDATA_ACCESS(DirectionalLightShadow); - // Particles attributes buffer ByteAddressBuffer ParticlesData : register(t0); -// Ribbons don't use sorted indices so overlap the segment distances buffer on the slot -#define HAS_SORTED_INDICES (!defined(_VS_Ribbon)) - #if HAS_SORTED_INDICES - // Sorted particles indices Buffer SortedIndices : register(t1); - #else - // Ribbon particles segments distances buffer Buffer SegmentDistances : register(t1); - #endif // Shader resources -TextureCube EnvProbe : register(t2); -TextureCube SkyLightTexture : register(t3); -Texture2DArray DirectionalLightShadowMap : register(t4); @2 - // Interpolants passed from the vertex shader struct VertexOutput { @@ -711,97 +680,6 @@ VertexOutput VS_Ribbon(uint vertexIndex : SV_VertexID) return output; } -// Pixel Shader function for Forward Pass -META_PS(USE_FORWARD, FEATURE_LEVEL_ES2) -float4 PS_Forward(PixelInput input) : SV_Target0 -{ - float4 output = 0; - - // Get material parameters - MaterialInput materialInput = GetMaterialInput(input); - Material material = GetMaterialPS(materialInput); - - // Masking -#if MATERIAL_MASKED - clip(material.Mask - MATERIAL_MASK_THRESHOLD); -#endif - - // Add emissive light - output = float4(material.Emissive, material.Opacity); - -#if MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT - - // Setup GBuffer data as proxy for lighting - GBufferSample gBuffer; - gBuffer.Normal = material.WorldNormal; - gBuffer.Roughness = material.Roughness; - gBuffer.Metalness = material.Metalness; - gBuffer.Color = material.Color; - gBuffer.Specular = material.Specular; - gBuffer.AO = material.AO; - gBuffer.ViewPos = mul(float4(materialInput.WorldPosition, 1), ViewMatrix).xyz; -#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE - gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity); -#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE - gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity); -#else - gBuffer.CustomData = float4(0, 0, 0, 0); -#endif - gBuffer.WorldPos = materialInput.WorldPosition; - gBuffer.ShadingModel = MATERIAL_SHADING_MODEL; - - // Calculate lighting from a single directional light - float4 shadowMask = 1.0f; - if (DirectionalLight.CastShadows > 0) - { - LightShadowData directionalLightShadowData = GetDirectionalLightShadowData(); - shadowMask.r = SampleShadow(DirectionalLight, directionalLightShadowData, DirectionalLightShadowMap, gBuffer, shadowMask.g); - } - float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false); - - // Calculate lighting from sky light - light += GetSkyLightLighting(SkyLight, gBuffer, SkyLightTexture); - - // Calculate lighting from local lights - LOOP - for (uint localLightIndex = 0; localLightIndex < LocalLightsCount; localLightIndex++) - { - const LightData localLight = LocalLights[localLightIndex]; - bool isSpotLight = localLight.SpotAngles.x > -2.0f; - shadowMask = 1.0f; - light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight); - } - -#if USE_REFLECTIONS - // Calculate reflections - light.rgb += GetEnvProbeLighting(ViewPos, EnvProbe, EnvironmentProbe, gBuffer) * light.a; -#endif - - // Add lighting (apply ambient occlusion) - output.rgb += light.rgb * gBuffer.AO; - -#if USE_FOG - // Calculate exponential height fog - float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0); - - // Apply fog to the output color -#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE - output = float4(output.rgb * fog.a + fog.rgb, output.a); -#elif MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT - output = float4(output.rgb * fog.a + fog.rgb, output.a); -#elif MATERIAL_BLEND == MATERIAL_BLEND_ADDITIVE - output = float4(output.rgb * fog.a + fog.rgb, output.a * fog.a); -#elif MATERIAL_BLEND == MATERIAL_BLEND_MULTIPLY - output = float4(lerp(float3(1, 1, 1), output.rgb, fog.aaa * fog.aaa), output.a); -#endif - -#endif - -#endif - - return output; -} - // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input diff --git a/Content/Editor/MaterialTemplates/PostProcess.shader b/Content/Editor/MaterialTemplates/PostProcess.shader index 681a04248..f20bdc610 100644 --- a/Content/Editor/MaterialTemplates/PostProcess.shader +++ b/Content/Editor/MaterialTemplates/PostProcess.shader @@ -20,7 +20,7 @@ float4 ScreenSize; float4 TemporalAAJitter; @1META_CB_END -// Material shader resources +// Shader resources @2 // Interpolants passed to the pixel shader struct PixelInput diff --git a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader index dcee96822..9ba1ea3de 100644 --- a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader +++ b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader @@ -30,9 +30,8 @@ float3 GeometrySize; float Dummy1; @1META_CB_END -// Material shader resources +// Shader resources @2 - // Geometry data passed though the graphics rendering stages up to the pixel shader struct GeometryData { diff --git a/Content/Editor/MaterialTemplates/SurfaceForward.shader b/Content/Editor/MaterialTemplates/SurfaceForward.shader index cece1b231..e7a86fb81 100644 --- a/Content/Editor/MaterialTemplates/SurfaceForward.shader +++ b/Content/Editor/MaterialTemplates/SurfaceForward.shader @@ -2,19 +2,10 @@ // Version: @0 #define MATERIAL 1 -#define MAX_LOCAL_LIGHTS 4 @3 - #include "./Flax/Common.hlsl" #include "./Flax/MaterialCommon.hlsl" #include "./Flax/GBufferCommon.hlsl" -#include "./Flax/LightingCommon.hlsl" -#if USE_REFLECTIONS -#include "./Flax/ReflectionsCommon.hlsl" -#endif -#include "./Flax/Lighting.hlsl" -#include "./Flax/ShadowsSampling.hlsl" -#include "./Flax/ExponentialHeightFog.hlsl" @7 // Primary constant buffer (with additional material parameters) META_CB_BEGIN(0, Data) @@ -38,26 +29,8 @@ float3 GeometrySize; float Dummy1; @1META_CB_END -// Secondary constantant buffer (for lighting) -META_CB_BEGIN(1, LightingData) -LightData DirectionalLight; -LightShadowData DirectionalLightShadow; -LightData SkyLight; -ProbeData EnvironmentProbe; -ExponentialHeightFogData ExponentialHeightFog; -float3 Dummy2; -uint LocalLightsCount; -LightData LocalLights[MAX_LOCAL_LIGHTS]; -META_CB_END - -DECLARE_LIGHTSHADOWDATA_ACCESS(DirectionalLightShadow); - // Shader resources -TextureCube EnvProbe : register(t0); -TextureCube SkyLightTexture : register(t1); -Texture2DArray DirectionalLightShadowMap : register(t2); @2 - // Interpolants passed from the vertex shader struct VertexOutput { @@ -564,103 +537,6 @@ void ClipLODTransition(PixelInput input) #endif -// Pixel Shader function for Forward Pass -META_PS(USE_FORWARD, FEATURE_LEVEL_ES2) -float4 PS_Forward(PixelInput input) : SV_Target0 -{ - float4 output = 0; - -#if USE_DITHERED_LOD_TRANSITION - // LOD masking - ClipLODTransition(input); - // TODO: make model LOD transition smoother for transparent materials by using opacity to reduce aliasing -#endif - - // Get material parameters - MaterialInput materialInput = GetMaterialInput(input); - Material material = GetMaterialPS(materialInput); - - // Masking -#if MATERIAL_MASKED - clip(material.Mask - MATERIAL_MASK_THRESHOLD); -#endif - - // Add emissive light - output = float4(material.Emissive, material.Opacity); - -#if MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT - - // Setup GBuffer data as proxy for lighting - GBufferSample gBuffer; - gBuffer.Normal = material.WorldNormal; - gBuffer.Roughness = material.Roughness; - gBuffer.Metalness = material.Metalness; - gBuffer.Color = material.Color; - gBuffer.Specular = material.Specular; - gBuffer.AO = material.AO; - gBuffer.ViewPos = mul(float4(materialInput.WorldPosition, 1), ViewMatrix).xyz; -#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE - gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity); -#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE - gBuffer.CustomData = float4(material.SubsurfaceColor, material.Opacity); -#else - gBuffer.CustomData = float4(0, 0, 0, 0); -#endif - gBuffer.WorldPos = materialInput.WorldPosition; - gBuffer.ShadingModel = MATERIAL_SHADING_MODEL; - - // Calculate lighting from a single directional light - float4 shadowMask = 1.0f; - if (DirectionalLight.CastShadows > 0) - { - LightShadowData directionalLightShadowData = GetDirectionalLightShadowData(); - shadowMask.r = SampleShadow(DirectionalLight, directionalLightShadowData, DirectionalLightShadowMap, gBuffer, shadowMask.g); - } - float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false); - - // Calculate lighting from sky light - light += GetSkyLightLighting(SkyLight, gBuffer, SkyLightTexture); - - // Calculate lighting from local lights - LOOP - for (uint localLightIndex = 0; localLightIndex < LocalLightsCount; localLightIndex++) - { - const LightData localLight = LocalLights[localLightIndex]; - bool isSpotLight = localLight.SpotAngles.x > -2.0f; - shadowMask = 1.0f; - light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight); - } - -#if USE_REFLECTIONS - // Calculate reflections - light.rgb += GetEnvProbeLighting(ViewPos, EnvProbe, EnvironmentProbe, gBuffer) * light.a; -#endif - - // Add lighting (apply ambient occlusion) - output.rgb += light.rgb * gBuffer.AO; - -#if USE_FOG - // Calculate exponential height fog - float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0); - - // Apply fog to the output color -#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE - output = float4(output.rgb * fog.a + fog.rgb, output.a); -#elif MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT - output = float4(output.rgb * fog.a + fog.rgb, output.a); -#elif MATERIAL_BLEND == MATERIAL_BLEND_ADDITIVE - output = float4(output.rgb * fog.a + fog.rgb, output.a * fog.a); -#elif MATERIAL_BLEND == MATERIAL_BLEND_MULTIPLY - output = float4(lerp(float3(1, 1, 1), output.rgb, fog.aaa * fog.aaa), output.a); -#endif - -#endif - -#endif - - return output; -} - // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index c36d7f0c5..f1c257b04 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -42,9 +42,8 @@ Texture2D Heightmap : register(t0); Texture2D Splatmap0 : register(t1); Texture2D Splatmap1 : register(t2); -// Material shader resources +// Shader resources @2 - // Geometry data passed though the graphics rendering stages up to the pixel shader struct GeometryData { diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index f410d477c..0d9108566 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -4,13 +4,14 @@ #include "MaterialShaderFeatures.h" #include "MaterialParams.h" #include "Engine/Engine/Time.h" +#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPULimits.h" -#include "Engine/Graphics/Models/SkinnedMeshDrawData.h" #include "Engine/Graphics/RenderView.h" -#include "Engine/Level/Actors/EnvironmentProbe.h" -#include "Engine/Renderer/DepthOfFieldPass.h" +#include "Engine/Graphics/RenderTask.h" +#include "Engine/Graphics/Models/SkinnedMeshDrawData.h" +#include "Engine/Graphics/Shaders/GPUConstantBuffer.h" +#include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Renderer/DrawCall.h" -#include "Engine/Renderer/ShadowsPass.h" #include "Engine/Renderer/RenderList.h" #if USE_EDITOR #include "Engine/Renderer/Lightmaps.h" @@ -39,17 +40,6 @@ PACK_STRUCT(struct ForwardMaterialShaderData { float Dummy1; }); -PACK_STRUCT(struct ForwardMaterialShaderLightingData { - LightData DirectionalLight; - LightShadowData DirectionalLightShadow; - LightData SkyLight; - ProbeData EnvironmentProbe; - ExponentialHeightFogData ExponentialHeightFog; - Vector3 Dummy2; - uint32 LocalLightsCount; - LightData LocalLights[MAX_LOCAL_LIGHTS]; - }); - DrawPass ForwardMaterialShader::GetDrawModes() const { return _drawModes; @@ -66,19 +56,17 @@ void ForwardMaterialShader::Bind(BindParameters& params) // Prepare auto context = params.GPUContext; auto& view = params.RenderContext.View; - auto cache = params.RenderContext.List; auto& drawCall = *params.FirstDrawCall; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0 && cb0->GetSize() != 0; ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing - const auto cb1 = _shader->GetCB(1); - const bool hasCb1 = cb1 && cb1->GetSize() != 0; byte* cb = _cb0Data.Get(); auto materialData = reinterpret_cast(cb); cb += sizeof(ForwardMaterialShaderData); int32 srv = 0; // Setup features + ForwardShadingFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; @@ -131,124 +119,12 @@ void ForwardMaterialShader::Bind(BindParameters& params) materialData->GeometrySize = drawCall.Surface.GeometrySize; } - // Setup lighting constants data - if (hasCb1) - { - auto& lightingData = *reinterpret_cast(_cb1Data.Get()); - const int32 envProbeShaderRegisterIndex = 0; - const int32 skyLightShaderRegisterIndex = 1; - const int32 dirLightShaderRegisterIndex = 2; - - // Set fog input - if (cache->Fog) - { - cache->Fog->GetExponentialHeightFogData(view, lightingData.ExponentialHeightFog); - } - else - { - lightingData.ExponentialHeightFog.FogMinOpacity = 1.0f; - lightingData.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f; - } - - // Set directional light input - if (cache->DirectionalLights.HasItems()) - { - const auto& dirLight = cache->DirectionalLights.First(); - const auto shadowPass = ShadowsPass::Instance(); - const bool useShadow = shadowPass->LastDirLightIndex == 0; - if (useShadow) - { - lightingData.DirectionalLightShadow = shadowPass->LastDirLight; - context->BindSR(dirLightShaderRegisterIndex, shadowPass->LastDirLightShadowMap); - } - else - { - context->UnBindSR(dirLightShaderRegisterIndex); - } - dirLight.SetupLightData(&lightingData.DirectionalLight, view, useShadow); - } - else - { - lightingData.DirectionalLight.Color = Vector3::Zero; - lightingData.DirectionalLight.CastShadows = 0.0f; - context->UnBindSR(dirLightShaderRegisterIndex); - } - - // Set sky light - if (cache->SkyLights.HasItems()) - { - auto& skyLight = cache->SkyLights.First(); - skyLight.SetupLightData(&lightingData.SkyLight, view, false); - const auto texture = skyLight.Image ? skyLight.Image->GetTexture() : nullptr; - context->BindSR(skyLightShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture)); - } - else - { - Platform::MemoryClear(&lightingData.SkyLight, sizeof(lightingData.SkyLight)); - context->UnBindSR(skyLightShaderRegisterIndex); - } - - // Set reflection probe data - EnvironmentProbe* probe = nullptr; - // TODO: optimize env probe searching for a transparent material - use spatial cache for renderer to find it - for (int32 i = 0; i < cache->EnvironmentProbes.Count(); i++) - { - const auto p = cache->EnvironmentProbes[i]; - if (p->GetSphere().Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) - { - probe = p; - break; - } - } - if (probe && probe->GetProbe()) - { - probe->SetupProbeData(&lightingData.EnvironmentProbe); - const auto texture = probe->GetProbe()->GetTexture(); - context->BindSR(envProbeShaderRegisterIndex, GET_TEXTURE_VIEW_SAFE(texture)); - } - else - { - lightingData.EnvironmentProbe.Data1 = Vector4::Zero; - context->UnBindSR(envProbeShaderRegisterIndex); - } - - // Set local lights - lightingData.LocalLightsCount = 0; - for (int32 i = 0; i < cache->PointLights.Count(); i++) - { - const auto& light = cache->PointLights[i]; - if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) - { - light.SetupLightData(&lightingData.LocalLights[lightingData.LocalLightsCount], view, false); - lightingData.LocalLightsCount++; - if (lightingData.LocalLightsCount == MAX_LOCAL_LIGHTS) - break; - } - } - for (int32 i = 0; i < cache->SpotLights.Count(); i++) - { - const auto& light = cache->SpotLights[i]; - if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) - { - light.SetupLightData(&lightingData.LocalLights[lightingData.LocalLightsCount], view, false); - lightingData.LocalLightsCount++; - if (lightingData.LocalLightsCount == MAX_LOCAL_LIGHTS) - break; - } - } - } - // Bind constants if (hasCb0) { context->UpdateCB(cb0, _cb0Data.Get()); context->BindCB(0, cb0); } - if (hasCb1) - { - context->UpdateCB(cb1, _cb1Data.Get()); - context->BindCB(1, cb1); - } // Select pipeline state based on current pass and render mode const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != 0 || view.Mode == ViewMode::Wireframe; diff --git a/Source/Engine/Graphics/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp index 478992959..ee1cf22c4 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp @@ -143,11 +143,6 @@ bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInf { _cb0Data.Resize(cb0->GetSize(), false); } - const auto cb1 = _shader->GetCB(1); - if (cb1) - { - _cb1Data.Resize(cb1->GetSize(), false); - } // Initialize the material based on type (create pipeline states and setup) if (Load()) @@ -163,6 +158,5 @@ void MaterialShader::Unload() { _isLoaded = false; _cb0Data.Resize(0, false); - _cb1Data.Resize(0, false); _shader->ReleaseGPU(); } diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index 58ba297fa..baa463d3f 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -53,7 +53,6 @@ protected: bool _isLoaded; GPUShader* _shader; Array _cb0Data; - Array _cb1Data; MaterialInfo _info; protected: diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 0043a8d6e..3ed31908c 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -155,23 +155,30 @@ bool LightmapFeature::Bind(MaterialShader::BindParameters& params, byte*& cb, in #if USE_EDITOR +void ForwardShadingFeature::Generate(GeneratorData& data) +{ + data.Template = TEXT("Features/ForwardShading.hlsl"); + data.ConstantsSize = sizeof(Data); + data.ResourcesCount = SRVs; +} + void TessellationFeature::Generate(GeneratorData& data) { - data.Template = TEXT("Tessellation.hlsl"); + data.Template = TEXT("Features/Tessellation.hlsl"); data.ConstantsSize = 0; data.ResourcesCount = 0; } void LightmapFeature::Generate(GeneratorData& data) { - data.Template = TEXT("Lightmap.hlsl"); + data.Template = TEXT("Features/Lightmap.hlsl"); data.ConstantsSize = sizeof(Data); data.ResourcesCount = SRVs; } void DistortionFeature::Generate(GeneratorData& data) { - data.Template = TEXT("Distortion.hlsl"); + data.Template = TEXT("Features/Distortion.hlsl"); data.ConstantsSize = 0; data.ResourcesCount = 0; } diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h index 9d2c06307..1ee46e2d1 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h @@ -38,6 +38,9 @@ struct ForwardShadingFeature : MaterialShaderFeature }); static void Bind(MaterialShader::BindParameters& params, byte*& cb, int32& srv); +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif }; // Material shader feature that adds geometry hardware tessellation (using Hull and Domain shaders). diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index bfab56daf..8ee49ae78 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -1,20 +1,19 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "ParticleMaterialShader.h" +#include "MaterialShaderFeatures.h" #include "MaterialParams.h" -#include "Engine/Renderer/DrawCall.h" -#include "Engine/Renderer/ShadowsPass.h" -#include "Engine/Graphics/RenderView.h" -#include "Engine/Renderer/RenderList.h" -#include "Engine/Graphics/GPUContext.h" -#include "Engine/Graphics/Shaders/GPUConstantBuffer.h" #include "Engine/Engine/Time.h" +#include "Engine/Renderer/DrawCall.h" +#include "Engine/Renderer/RenderList.h" +#include "Engine/Graphics/RenderView.h" +#include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPUDevice.h" -#include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/GPULimits.h" +#include "Engine/Graphics/RenderTask.h" +#include "Engine/Graphics/Shaders/GPUShader.h" +#include "Engine/Graphics/Shaders/GPUConstantBuffer.h" #include "Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h" -#include "Engine/Content/Assets/CubeTexture.h" -#include "Engine/Level/Actors/EnvironmentProbe.h" #define MAX_LOCAL_LIGHTS 4 @@ -49,17 +48,6 @@ PACK_STRUCT(struct ParticleMaterialShaderData { Matrix WorldMatrixInverseTransposed; }); -PACK_STRUCT(struct ParticleMaterialShaderLightingData { - LightData DirectionalLight; - LightShadowData DirectionalLightShadow; - LightData SkyLight; - ProbeData EnvironmentProbe; - ExponentialHeightFogData ExponentialHeightFog; - Vector3 Dummy1; - uint32 LocalLightsCount; - LightData LocalLights[MAX_LOCAL_LIGHTS]; - }); - DrawPass ParticleMaterialShader::GetDrawModes() const { return _drawModes; @@ -70,20 +58,18 @@ void ParticleMaterialShader::Bind(BindParameters& params) // Prepare auto context = params.GPUContext; auto& view = params.RenderContext.View; - auto cache = params.RenderContext.List; auto& drawCall = *params.FirstDrawCall; const uint32 sortedIndicesOffset = drawCall.Particle.Module->SortedIndicesOffset; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0->GetSize() != 0; ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing - const auto cb1 = _shader->GetCB(1); - const bool hasCb1 = cb1 && cb1->GetSize() != 0; byte* cb = _cb0Data.Get(); auto materialData = reinterpret_cast(cb); cb += sizeof(ParticleMaterialShaderData); - int32 srv = 0; - + int32 srv = 2; + // Setup features + ForwardShadingFeature::Bind(params, cb, srv); // Setup parameters MaterialParameter::BindMeta bindMeta; @@ -95,25 +81,23 @@ void ParticleMaterialShader::Bind(BindParameters& params) bindMeta.CanSampleGBuffer = true; MaterialParams::Bind(params.ParamsLink, bindMeta); - // Setup particles data and attributes binding info + // Setup particles data + context->BindSR(0, drawCall.Particle.Particles->GPU.Buffer->View()); + context->BindSR(1, drawCall.Particle.Particles->GPU.SortedIndices ? drawCall.Particle.Particles->GPU.SortedIndices->View() : nullptr); + + // Setup particles attributes binding info + if (hasCb0) { - context->BindSR(0, drawCall.Particle.Particles->GPU.Buffer->View()); - if (drawCall.Particle.Particles->GPU.SortedIndices) - context->BindSR(1, drawCall.Particle.Particles->GPU.SortedIndices->View()); - - if (hasCb0) + const auto& p = *params.ParamsLink->This; + for (int32 i = 0; i < p.Count(); i++) { - const auto& p = *params.ParamsLink->This; - for (int32 i = 0; i < p.Count(); i++) + const auto& param = p.At(i); + if (param.GetParameterType() == MaterialParameterType::Integer && param.GetName().StartsWith(TEXT("Particle."))) { - const auto& param = p.At(i); - if (param.GetParameterType() == MaterialParameterType::Integer && param.GetName().StartsWith(TEXT("Particle."))) - { - auto name = StringView(param.GetName().Get() + 9); + auto name = StringView(param.GetName().Get() + 9); - const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name); - *((int32*)(bindMeta.Constants + param.GetBindOffset())) = offset; - } + const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name); + *((int32*)(bindMeta.Constants + param.GetBindOffset())) = offset; } } } @@ -147,8 +131,6 @@ void ParticleMaterialShader::Bind(BindParameters& params) if (hasCb0) { - const auto materialData = reinterpret_cast(_cb0Data.Get()); - materialData->RibbonWidthOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1); materialData->RibbonTwistOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1); materialData->RibbonFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1); @@ -173,8 +155,6 @@ void ParticleMaterialShader::Bind(BindParameters& params) // Setup material constants data if (hasCb0) { - const auto materialData = reinterpret_cast(_cb0Data.Get()); - static StringView ParticlePosition(TEXT("Position")); static StringView ParticleSpriteSize(TEXT("SpriteSize")); static StringView ParticleSpriteFacingMode(TEXT("SpriteFacingMode")); @@ -207,124 +187,12 @@ void ParticleMaterialShader::Bind(BindParameters& params) Matrix::Invert(drawCall.World, materialData->WorldMatrixInverseTransposed); } - // Setup lighting constants data - if (hasCb1) - { - auto& lightingData = *reinterpret_cast(_cb1Data.Get()); - const int32 envProbeShaderRegisterIndex = 2; - const int32 skyLightShaderRegisterIndex = 3; - const int32 dirLightShaderRegisterIndex = 4; - - // Set fog input - if (cache->Fog) - { - cache->Fog->GetExponentialHeightFogData(view, lightingData.ExponentialHeightFog); - } - else - { - lightingData.ExponentialHeightFog.FogMinOpacity = 1.0f; - lightingData.ExponentialHeightFog.ApplyDirectionalInscattering = 0.0f; - } - - // Set directional light input - if (cache->DirectionalLights.HasItems()) - { - const auto& dirLight = cache->DirectionalLights.First(); - const auto shadowPass = ShadowsPass::Instance(); - const bool useShadow = shadowPass->LastDirLightIndex == 0; - if (useShadow) - { - lightingData.DirectionalLightShadow = shadowPass->LastDirLight; - context->BindSR(dirLightShaderRegisterIndex, shadowPass->LastDirLightShadowMap); - } - else - { - context->UnBindSR(dirLightShaderRegisterIndex); - } - dirLight.SetupLightData(&lightingData.DirectionalLight, view, useShadow); - } - else - { - lightingData.DirectionalLight.Color = Vector3::Zero; - lightingData.DirectionalLight.CastShadows = 0.0f; - context->UnBindSR(dirLightShaderRegisterIndex); - } - - // Set sky light - if (cache->SkyLights.HasItems()) - { - auto& skyLight = cache->SkyLights.Last(); - skyLight.SetupLightData(&lightingData.SkyLight, view, false); - const auto texture = skyLight.Image ? skyLight.Image->GetTexture() : nullptr; - context->BindSR(skyLightShaderRegisterIndex, texture); - } - else - { - Platform::MemoryClear(&lightingData.SkyLight, sizeof(lightingData.SkyLight)); - context->UnBindSR(skyLightShaderRegisterIndex); - } - - // Set reflection probe data - EnvironmentProbe* probe = nullptr; - // TODO: optimize env probe searching for a transparent material - use spatial cache for renderer to find it - for (int32 i = 0; i < cache->EnvironmentProbes.Count(); i++) - { - const auto p = cache->EnvironmentProbes[i]; - if (p->GetSphere().Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) - { - probe = p; - break; - } - } - if (probe && probe->GetProbe()) - { - probe->SetupProbeData(&lightingData.EnvironmentProbe); - const auto texture = probe->GetProbe()->GetTexture(); - context->BindSR(envProbeShaderRegisterIndex, texture); - } - else - { - lightingData.EnvironmentProbe.Data1 = Vector4::Zero; - context->UnBindSR(envProbeShaderRegisterIndex); - } - - // Set local lights - lightingData.LocalLightsCount = 0; - for (int32 i = 0; i < cache->PointLights.Count(); i++) - { - const auto& light = cache->PointLights[i]; - if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) - { - light.SetupLightData(&lightingData.LocalLights[lightingData.LocalLightsCount], view, false); - lightingData.LocalLightsCount++; - if (lightingData.LocalLightsCount == MAX_LOCAL_LIGHTS) - break; - } - } - for (int32 i = 0; i < cache->SpotLights.Count(); i++) - { - const auto& light = cache->SpotLights[i]; - if (BoundingSphere(light.Position, light.Radius).Contains(drawCall.World.GetTranslation()) != ContainmentType::Disjoint) - { - light.SetupLightData(&lightingData.LocalLights[lightingData.LocalLightsCount], view, false); - lightingData.LocalLightsCount++; - if (lightingData.LocalLightsCount == MAX_LOCAL_LIGHTS) - break; - } - } - } - // Bind constants if (hasCb0) { context->UpdateCB(cb0, _cb0Data.Get()); context->BindCB(0, cb0); } - if (hasCb1) - { - context->UpdateCB(cb1, _cb1Data.Get()); - context->BindCB(1, cb1); - } // Bind pipeline context->SetState(state); diff --git a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp index 2d9840103..8be86a4ac 100644 --- a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp @@ -26,7 +26,6 @@ void PostFxMaterialShader::Bind(BindParameters& params) // Prepare auto context = params.GPUContext; auto& view = params.RenderContext.View; - auto& drawCall = *params.FirstDrawCall; const auto cb0 = _shader->GetCB(0); const bool hasCb0 = cb0->GetSize() != 0; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index 45076f39f..9c9af3145 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -1,16 +1,16 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -#include "GPUShaderDX11.h" #if GRAPHICS_API_DIRECTX11 #include "GPUContextDX11.h" -#include "Engine/Core/Math/Viewport.h" -#include "Engine/Core/Math/Rectangle.h" +#include "GPUShaderDX11.h" #include "GPUShaderProgramDX11.h" #include "GPUPipelineStateDX11.h" #include "GPUTextureDX11.h" #include "GPUBufferDX11.h" #include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h" +#include "Engine/Core/Math/Viewport.h" +#include "Engine/Core/Math/Rectangle.h" #include "Engine/Profiler/RenderStats.h" #define DX11_CLEAR_SR_ON_STAGE_DISABLE 0 diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 8550febad..ea342bd53 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -59,7 +59,7 @@ namespace bool FeatureData::Init() { // Load template file - const String path = Globals::EngineContentFolder / TEXT("Editor/MaterialTemplates/Features/") + Data.Template; + const String path = Globals::EngineContentFolder / TEXT("Editor/MaterialTemplates/") + Data.Template; String contents; if (File::ReadAllText(path, contents)) { @@ -188,17 +188,20 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo ADD_FEATURE(LightmapFeature); if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0) ADD_FEATURE(DistortionFeature); + if (materialInfo.BlendMode != MaterialBlendMode::Opaque) + ADD_FEATURE(ForwardShadingFeature); break; case MaterialDomain::Terrain: if (materialInfo.TessellationMode != TessellationMethod::None) ADD_FEATURE(TessellationFeature); ADD_FEATURE(LightmapFeature); break; - default: case MaterialDomain::Particle: if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0) ADD_FEATURE(DistortionFeature); + ADD_FEATURE(ForwardShadingFeature); break; + default: break; } #undef ADD_FEATURE @@ -390,18 +393,14 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo int32 srv = 0; switch (baseLayer->Domain) { - case MaterialDomain::Surface: - if (materialInfo.BlendMode != MaterialBlendMode::Opaque) - srv = 3; // Forward shading resources - break; case MaterialDomain::Decal: - srv = 1; + srv = 1; // Depth buffer break; case MaterialDomain::Terrain: srv = 3; // Heightmap + 2 splatmaps break; case MaterialDomain::Particle: - srv = 5; + srv = 2; // Particles data + Sorted indices/Ribbon segments break; } for (auto f : features) From 089f0dab3acdc963a81845a999455e92bf99dd7a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 11:55:02 +0100 Subject: [PATCH 314/419] Refactor material shaders generator to use modular features as extensions --- .../Features/DeferredShading.hlsl | 84 +++++++++++++++++++ .../Features/ForwardShading.hlsl | 1 + .../MaterialTemplates/Features/Lightmap.hlsl | 2 +- .../MaterialTemplates/SurfaceDeferred.shader | 81 +----------------- .../Editor/MaterialTemplates/Terrain.shader | 72 ---------------- .../Materials/MaterialShaderFeatures.cpp | 7 ++ .../Materials/MaterialShaderFeatures.h | 8 ++ .../MaterialGenerator/MaterialGenerator.cpp | 3 + 8 files changed, 108 insertions(+), 150 deletions(-) create mode 100644 Content/Editor/MaterialTemplates/Features/DeferredShading.hlsl diff --git a/Content/Editor/MaterialTemplates/Features/DeferredShading.hlsl b/Content/Editor/MaterialTemplates/Features/DeferredShading.hlsl new file mode 100644 index 000000000..d3a7a2518 --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/DeferredShading.hlsl @@ -0,0 +1,84 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +@0// Deferred Shading: Defines +@1// Deferred Shading: Includes +@2// Deferred Shading: Constants +@3// Deferred Shading: Resources +@4// Deferred Shading: Utilities +@5// Deferred Shading: Shaders + +// Pixel Shader function for GBuffer Pass +META_PS(true, FEATURE_LEVEL_ES2) +META_PERMUTATION_1(USE_LIGHTMAP=0) +META_PERMUTATION_1(USE_LIGHTMAP=1) +void PS_GBuffer( + in PixelInput input + ,out float4 Light : SV_Target0 +#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE + // GBuffer + ,out float4 RT0 : SV_Target1 + ,out float4 RT1 : SV_Target2 + ,out float4 RT2 : SV_Target3 +#if USE_GBUFFER_CUSTOM_DATA + ,out float4 RT3 : SV_Target4 +#endif +#endif + ) +{ + Light = 0; + +#if USE_DITHERED_LOD_TRANSITION + // LOD masking + ClipLODTransition(input); +#endif + + // Get material parameters + MaterialInput materialInput = GetMaterialInput(input); + Material material = GetMaterialPS(materialInput); + + // Masking +#if MATERIAL_MASKED + clip(material.Mask - MATERIAL_MASK_THRESHOLD); +#endif + +#if USE_LIGHTMAP + float3 diffuseColor = GetDiffuseColor(material.Color, material.Metalness); + float3 specularColor = GetSpecularColor(material.Color, material.Specular, material.Metalness); + + // Sample lightmap + float3 diffuseIndirectLighting = SampleLightmap(material, materialInput); + + // Apply static indirect light + Light.rgb = diffuseColor * diffuseIndirectLighting * AOMultiBounce(material.AO, diffuseColor); +#endif + +#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE + + // Pack material properties to GBuffer + RT0 = float4(material.Color, material.AO); + RT1 = float4(material.WorldNormal * 0.5 + 0.5, MATERIAL_SHADING_MODEL * (1.0 / 3.0)); + RT2 = float4(material.Roughness, material.Metalness, material.Specular, 0); + + // Custom data +#if USE_GBUFFER_CUSTOM_DATA +#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE + RT3 = float4(material.SubsurfaceColor, material.Opacity); +#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE + RT3 = float4(material.SubsurfaceColor, material.Opacity); +#else + RT3 = float4(0, 0, 0, 0); +#endif +#endif + + // Add light emission +#if USE_EMISSIVE + Light.rgb += material.Emissive; +#endif + +#else + + // Handle blending as faked forward pass (use Light buffer and skip GBuffer modification) + Light = float4(material.Emissive, material.Opacity); + +#endif +} diff --git a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl index 085c66291..c21fe37d2 100644 --- a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl +++ b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl @@ -26,6 +26,7 @@ Texture2DArray DirectionalLightShadowMap : register(t__SRV__); @4// Forward Shading: Utilities DECLARE_LIGHTSHADOWDATA_ACCESS(DirectionalLightShadow); @5// Forward Shading: Shaders + // Pixel Shader function for Forward Pass META_PS(USE_FORWARD, FEATURE_LEVEL_ES2) float4 PS_Forward(PixelInput input) : SV_Target0 diff --git a/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl b/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl index 6f6a44d8b..33ef567ca 100644 --- a/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl +++ b/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl @@ -50,4 +50,4 @@ float3 SampleLightmap(Material material, MaterialInput materialInput) } #endif -@5// Lightmap: Shaders \ No newline at end of file +@5// Lightmap: Shaders diff --git a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader index 9ba1ea3de..17f88e179 100644 --- a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader +++ b/Content/Editor/MaterialTemplates/SurfaceDeferred.shader @@ -651,85 +651,8 @@ void ClipLODTransition(PixelInput input) } } -#else - -void ClipLODTransition(PixelInput input) -{ -} - #endif -// Pixel Shader function for GBuffer Pass -META_PS(true, FEATURE_LEVEL_ES2) -META_PERMUTATION_1(USE_LIGHTMAP=0) -META_PERMUTATION_1(USE_LIGHTMAP=1) -void PS_GBuffer( - in PixelInput input - ,out float4 Light : SV_Target0 -#if MATERIAL_DOMAIN == MATERIAL_DOMAIN_SURFACE - // GBuffer - ,out float4 RT0 : SV_Target1 - ,out float4 RT1 : SV_Target2 - ,out float4 RT2 : SV_Target3 -#if USE_GBUFFER_CUSTOM_DATA - ,out float4 RT3 : SV_Target4 -#endif -#endif - ) -{ - Light = 0; - - // LOD masking - ClipLODTransition(input); - - // Get material parameters - MaterialInput materialInput = GetMaterialInput(input); - Material material = GetMaterialPS(materialInput); - -#if MATERIAL_DOMAIN == MATERIAL_DOMAIN_SURFACE - - // Masking -#if MATERIAL_MASKED - clip(material.Mask - MATERIAL_MASK_THRESHOLD); -#endif - -#if USE_LIGHTMAP - - float3 diffuseColor = GetDiffuseColor(material.Color, material.Metalness); - float3 specularColor = GetSpecularColor(material.Color, material.Specular, material.Metalness); - - // Sample lightmap - float3 diffuseIndirectLighting = SampleLightmap(material, materialInput); - - // Apply static indirect light - Light.rgb = diffuseColor * diffuseIndirectLighting * AOMultiBounce(material.AO, diffuseColor); - -#endif - - // Pack material properties to GBuffer - RT0 = float4(material.Color, material.AO); - RT1 = float4(material.WorldNormal * 0.5 + 0.5, MATERIAL_SHADING_MODEL * (1.0 / 3.0)); - RT2 = float4(material.Roughness, material.Metalness, material.Specular, 0); - - // Custom data -#if USE_GBUFFER_CUSTOM_DATA -#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE - RT3 = float4(material.SubsurfaceColor, material.Opacity); -#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE - RT3 = float4(material.SubsurfaceColor, material.Opacity); -#else - RT3 = float4(0, 0, 0, 0); -#endif -#endif - -#endif - - // Add light emission -#if USE_EMISSIVE - Light.rgb += material.Emissive; -#endif -} - // Pixel Shader function for Depth Pass META_PS(IS_SURFACE, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input @@ -738,8 +661,10 @@ void PS_Depth(PixelInput input #endif ) { +#if USE_DITHERED_LOD_TRANSITION // LOD masking ClipLODTransition(input); +#endif #if MATERIAL_MASKED // Perform per pixel clipping if material requries it @@ -757,8 +682,10 @@ void PS_Depth(PixelInput input META_PS(true, FEATURE_LEVEL_ES2) float4 PS_MotionVectors(PixelInput input) : SV_Target0 { +#if USE_DITHERED_LOD_TRANSITION // LOD masking ClipLODTransition(input); +#endif #if MATERIAL_MASKED // Perform per pixel clipping if material requries it diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index f1c257b04..ef8afa2a6 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -454,78 +454,6 @@ VertexOutput VS(TerrainVertexInput input) return output; } -// Pixel Shader function for GBuffer Pass -META_PS(true, FEATURE_LEVEL_ES2) -META_PERMUTATION_1(USE_LIGHTMAP=0) -META_PERMUTATION_1(USE_LIGHTMAP=1) -void PS_GBuffer( - in PixelInput input - ,out float4 Light : SV_Target0 -#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE - ,out float4 RT0 : SV_Target1 - ,out float4 RT1 : SV_Target2 - ,out float4 RT2 : SV_Target3 -#if USE_GBUFFER_CUSTOM_DATA - ,out float4 RT3 : SV_Target4 -#endif -#endif - ) -{ - Light = 0; - - // Get material parameters - MaterialInput materialInput = GetMaterialInput(input); - Material material = GetMaterialPS(materialInput); - - // Masking -#if MATERIAL_MASKED - clip(material.Mask - MATERIAL_MASK_THRESHOLD); -#endif - -#if USE_LIGHTMAP - - float3 diffuseColor = GetDiffuseColor(material.Color, material.Metalness); - float3 specularColor = GetSpecularColor(material.Color, material.Specular, material.Metalness); - - // Sample lightmap - float3 diffuseIndirectLighting = SampleLightmap(material, materialInput); - - // Apply static indirect light - Light.rgb = diffuseColor * diffuseIndirectLighting * AOMultiBounce(material.AO, diffuseColor); - -#endif - -#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE - - // Pack material properties to GBuffer - RT0 = float4(material.Color, material.AO); - RT1 = float4(material.WorldNormal * 0.5 + 0.5, MATERIAL_SHADING_MODEL * (1.0 / 3.0)); - RT2 = float4(material.Roughness, material.Metalness, material.Specular, 0); - - // Custom data -#if USE_GBUFFER_CUSTOM_DATA -#if MATERIAL_SHADING_MODEL == SHADING_MODEL_SUBSURFACE - RT3 = float4(material.SubsurfaceColor, material.Opacity); -#elif MATERIAL_SHADING_MODEL == SHADING_MODEL_FOLIAGE - RT3 = float4(material.SubsurfaceColor, material.Opacity); -#else - RT3 = float4(0, 0, 0, 0); -#endif -#endif - - // Add light emission -#if USE_EMISSIVE - Light.rgb += material.Emissive; -#endif - -#else - - // Handle blending as faked forward pass (use Light buffer and skip GBuffer modification) - Light = float4(material.Emissive, material.Opacity); - -#endif -} - // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 3ed31908c..2a6a97ffa 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -162,6 +162,13 @@ void ForwardShadingFeature::Generate(GeneratorData& data) data.ResourcesCount = SRVs; } +void DeferredShadingFeature::Generate(GeneratorData& data) +{ + data.Template = TEXT("Features/DeferredShading.hlsl"); + data.ConstantsSize = 0; + data.ResourcesCount = 0; +} + void TessellationFeature::Generate(GeneratorData& data) { data.Template = TEXT("Features/Tessellation.hlsl"); diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h index 1ee46e2d1..3128a688d 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h @@ -43,6 +43,14 @@ struct ForwardShadingFeature : MaterialShaderFeature #endif }; +// Material shader feature that add support for Deferred shading inside the material shader. +struct DeferredShadingFeature : MaterialShaderFeature +{ +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif +}; + // Material shader feature that adds geometry hardware tessellation (using Hull and Domain shaders). struct TessellationFeature : MaterialShaderFeature { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index ea342bd53..531efe315 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -186,6 +186,8 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo ADD_FEATURE(TessellationFeature); if (materialInfo.BlendMode == MaterialBlendMode::Opaque) ADD_FEATURE(LightmapFeature); + if (materialInfo.BlendMode == MaterialBlendMode::Opaque) + ADD_FEATURE(DeferredShadingFeature); if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0) ADD_FEATURE(DistortionFeature); if (materialInfo.BlendMode != MaterialBlendMode::Opaque) @@ -195,6 +197,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo if (materialInfo.TessellationMode != TessellationMethod::None) ADD_FEATURE(TessellationFeature); ADD_FEATURE(LightmapFeature); + ADD_FEATURE(DeferredShadingFeature); break; case MaterialDomain::Particle: if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0) From 2da9ed455660f4898e13ee350ff59f38864e1072 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 11:55:21 +0100 Subject: [PATCH 315/419] Fix crash when using Find/FindLast on empty String --- Source/Engine/Core/Types/String.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Core/Types/String.h b/Source/Engine/Core/Types/String.h index 4d1ef4e7f..cf21a1236 100644 --- a/Source/Engine/Core/Types/String.h +++ b/Source/Engine/Core/Types/String.h @@ -220,7 +220,7 @@ public: /// The index of the found substring or -1 if not found. int32 Find(const T* subStr, StringSearchCase searchCase = StringSearchCase::CaseSensitive, int32 startPosition = -1) const { - if (subStr == nullptr) + if (subStr == nullptr || !_data) return -1; const T* start = _data; if (startPosition != -1) @@ -241,7 +241,7 @@ public: int32 FindLast(const T* subStr, StringSearchCase searchCase = StringSearchCase::CaseSensitive, int32 startPosition = -1) const { const int32 subStrLen = StringUtils::Length(subStr); - if (subStrLen == 0) + if (subStrLen == 0 || !_data) return -1; if (startPosition == -1) startPosition = Length(); From 300f948515d487266e6bd7c74429f01cce5c0b4b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 13:57:00 +0100 Subject: [PATCH 316/419] Refactor material shaders generator to use modular features as extensions --- ...{SurfaceDeferred.shader => Surface.shader} | 55 +- .../MaterialTemplates/SurfaceForward.shader | 568 ------------------ .../Materials/DeferredMaterialShader.cpp | 2 +- .../Materials/ForwardMaterialShader.cpp | 3 +- .../MaterialGenerator/MaterialGenerator.cpp | 8 +- 5 files changed, 45 insertions(+), 591 deletions(-) rename Content/Editor/MaterialTemplates/{SurfaceDeferred.shader => Surface.shader} (94%) delete mode 100644 Content/Editor/MaterialTemplates/SurfaceForward.shader diff --git a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader b/Content/Editor/MaterialTemplates/Surface.shader similarity index 94% rename from Content/Editor/MaterialTemplates/SurfaceDeferred.shader rename to Content/Editor/MaterialTemplates/Surface.shader index 17f88e179..8c09e7611 100644 --- a/Content/Editor/MaterialTemplates/SurfaceDeferred.shader +++ b/Content/Editor/MaterialTemplates/Surface.shader @@ -229,28 +229,38 @@ MaterialInput GetMaterialInput(PixelInput input) return result; } -#if USE_INSTANCING -#define INSTANCE_TRANS_WORLD float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)) -#else -#define INSTANCE_TRANS_WORLD WorldMatrix -#endif - // Gets the local to world transform matrix (supports instancing) float4x4 GetInstanceTransform(ModelInput input) { - return INSTANCE_TRANS_WORLD; +#if USE_INSTANCING + return float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)); +#else + return WorldMatrix; +#endif } float4x4 GetInstanceTransform(ModelInput_Skinned input) { - return INSTANCE_TRANS_WORLD; +#if USE_INSTANCING + return float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)); +#else + return WorldMatrix; +#endif } float4x4 GetInstanceTransform(ModelInput_PosOnly input) { - return INSTANCE_TRANS_WORLD; +#if USE_INSTANCING + return float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)); +#else + return WorldMatrix; +#endif } float4x4 GetInstanceTransform(MaterialInput input) { - return INSTANCE_TRANS_WORLD; +#if USE_INSTANCING + return float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)); +#else + return WorldMatrix; +#endif } // Removes the scale vector from the local to world transformation matrix (supports instancing) @@ -390,7 +400,7 @@ float3x3 CalcTangentToWorld(float4x4 world, float3x3 tangentToLocal) } // Vertex Shader function for GBuffer Pass and Depth Pass (with full vertex data) -META_VS(IS_SURFACE, FEATURE_LEVEL_ES2) +META_VS(true, FEATURE_LEVEL_ES2) META_PERMUTATION_1(USE_INSTANCING=0) META_PERMUTATION_1(USE_INSTANCING=1) META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) @@ -426,7 +436,11 @@ VertexOutput VS(ModelInput input) output.Geometry.LightmapUV = input.LightmapUV * input.InstanceLightmapArea.zw + input.InstanceLightmapArea.xy; output.Geometry.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); #else +#if USE_LIGHTMAP output.Geometry.LightmapUV = input.LightmapUV * LightmapArea.zw + LightmapArea.xy; +#else + output.Geometry.LightmapUV = input.LightmapUV; +#endif output.Geometry.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); #endif @@ -463,7 +477,7 @@ VertexOutput VS(ModelInput input) } // Vertex Shader function for Depth Pass -META_VS(IS_SURFACE, FEATURE_LEVEL_ES2) +META_VS(true, FEATURE_LEVEL_ES2) META_PERMUTATION_1(USE_INSTANCING=0) META_PERMUTATION_1(USE_INSTANCING=1) META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) @@ -559,7 +573,7 @@ float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data) } // Vertex Shader function for GBuffers/Depth Pass (skinned mesh rendering) -META_VS(IS_SURFACE, FEATURE_LEVEL_ES2) +META_VS(true, FEATURE_LEVEL_ES2) META_PERMUTATION_1(USE_SKINNING=1) META_PERMUTATION_2(USE_SKINNING=1, PER_BONE_MOTION_BLUR=1) META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) @@ -654,7 +668,7 @@ void ClipLODTransition(PixelInput input) #endif // Pixel Shader function for Depth Pass -META_PS(IS_SURFACE, FEATURE_LEVEL_ES2) +META_PS(true, FEATURE_LEVEL_ES2) void PS_Depth(PixelInput input #if GLSL , out float4 OutColor : SV_Target0 @@ -666,12 +680,19 @@ void PS_Depth(PixelInput input ClipLODTransition(input); #endif -#if MATERIAL_MASKED - // Perform per pixel clipping if material requries it +#if MATERIAL_MASKED || MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE + // Get material parameters MaterialInput materialInput = GetMaterialInput(input); Material material = GetMaterialPS(materialInput); + + // Perform per pixel clipping +#if MATERIAL_MASKED clip(material.Mask - MATERIAL_MASK_THRESHOLD); #endif +#if MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE + clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD); +#endif +#endif #if GLSL OutColor = 0; @@ -679,7 +700,7 @@ void PS_Depth(PixelInput input } // Pixel Shader function for Motion Vectors Pass -META_PS(true, FEATURE_LEVEL_ES2) +META_PS(USE_DEFERRED, FEATURE_LEVEL_ES2) float4 PS_MotionVectors(PixelInput input) : SV_Target0 { #if USE_DITHERED_LOD_TRANSITION diff --git a/Content/Editor/MaterialTemplates/SurfaceForward.shader b/Content/Editor/MaterialTemplates/SurfaceForward.shader deleted file mode 100644 index e7a86fb81..000000000 --- a/Content/Editor/MaterialTemplates/SurfaceForward.shader +++ /dev/null @@ -1,568 +0,0 @@ -// File generated by Flax Materials Editor -// Version: @0 - -#define MATERIAL 1 -@3 -#include "./Flax/Common.hlsl" -#include "./Flax/MaterialCommon.hlsl" -#include "./Flax/GBufferCommon.hlsl" -@7 -// Primary constant buffer (with additional material parameters) -META_CB_BEGIN(0, Data) -float4x4 ViewProjectionMatrix; -float4x4 WorldMatrix; -float4x4 ViewMatrix; -float4x4 PrevViewProjectionMatrix; -float4x4 PrevWorldMatrix; -float3 ViewPos; -float ViewFar; -float3 ViewDir; -float TimeParam; -float4 ViewInfo; -float4 ScreenSize; -float3 WorldInvScale; -float WorldDeterminantSign; -float2 Dummy0; -float LODDitherFactor; -float PerInstanceRandom; -float3 GeometrySize; -float Dummy1; -@1META_CB_END - -// Shader resources -@2 -// Interpolants passed from the vertex shader -struct VertexOutput -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3x3 TBN : TEXCOORD3; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; // x-PerInstanceRandom, y-LODDitherFactor -#if USE_TESSELLATION - float TessellationMultiplier : TESS; -#endif -}; - -// Interpolants passed to the pixel shader -struct PixelInput -{ - float4 Position : SV_Position; - float3 WorldPosition : TEXCOORD0; - float2 TexCoord : TEXCOORD1; - float2 LightmapUV : TEXCOORD2; -#if USE_VERTEX_COLOR - half4 VertexColor : COLOR; -#endif - float3x3 TBN : TEXCOORD3; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; -#endif - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; // x-PerInstanceRandom, y-LODDitherFactor - bool IsFrontFace : SV_IsFrontFace; -}; - -// Material properties generation input -struct MaterialInput -{ - float3 WorldPosition; - float TwoSidedSign; - float2 TexCoord; -#if USE_LIGHTMAP - float2 LightmapUV; -#endif -#if USE_VERTEX_COLOR - half4 VertexColor; -#endif - float3x3 TBN; - float4 SvPosition; - float3 PreSkinnedPosition; - float3 PreSkinnedNormal; - float3 InstanceOrigin; - float2 InstanceParams; -#if USE_INSTANCING - float3 InstanceTransform1; - float3 InstanceTransform2; - float3 InstanceTransform3; -#endif -#if USE_CUSTOM_VERTEX_INTERPOLATORS - float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT]; -#endif -}; - -MaterialInput GetMaterialInput(ModelInput input, VertexOutput output, float3 localNormal) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = output.WorldPosition; - result.TexCoord = output.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = output.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = output.VertexColor; -#endif - result.TBN = output.TBN; - result.TwoSidedSign = WorldDeterminantSign; - result.SvPosition = output.Position; - result.PreSkinnedPosition = input.Position.xyz; - result.PreSkinnedNormal = localNormal; -#if USE_INSTANCING - result.InstanceOrigin = input.InstanceOrigin.xyz; - result.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); - result.InstanceTransform1 = input.InstanceTransform1.xyz; - result.InstanceTransform2 = input.InstanceTransform2.xyz; - result.InstanceTransform3 = input.InstanceTransform3.xyz; -#else - result.InstanceOrigin = WorldMatrix[3].xyz; - result.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); -#endif - return result; -} - -MaterialInput GetMaterialInput(VertexOutput output, float3 localPosition, float3 localNormal) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = output.WorldPosition; - result.TexCoord = output.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = output.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = output.VertexColor; -#endif - result.TBN = output.TBN; - result.TwoSidedSign = WorldDeterminantSign; - result.InstanceOrigin = WorldMatrix[3].xyz; - result.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); - result.SvPosition = output.Position; - result.PreSkinnedPosition = localPosition; - result.PreSkinnedNormal = localNormal; - return result; -} - -MaterialInput GetMaterialInput(PixelInput input) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.WorldPosition; - result.TexCoord = input.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = input.VertexColor; -#endif - result.TBN = input.TBN; - result.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); - result.InstanceOrigin = input.InstanceOrigin; - result.InstanceParams = input.InstanceParams; - result.SvPosition = input.Position; -#if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; -#endif - return result; -} - -#if USE_INSTANCING -#define INSTANCE_TRANS_WORLD float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)) -#else -#define INSTANCE_TRANS_WORLD WorldMatrix -#endif - -// Gets the local to world transform matrix (supports instancing) -float4x4 GetInstanceTransform(ModelInput input) -{ - return INSTANCE_TRANS_WORLD; -} -float4x4 GetInstanceTransform(ModelInput_Skinned input) -{ - return INSTANCE_TRANS_WORLD; -} -float4x4 GetInstanceTransform(MaterialInput input) -{ - return INSTANCE_TRANS_WORLD; -} - -// Removes the scale vector from the local to world transformation matrix (supports instancing) -float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) -{ -#if USE_INSTANCING - // Extract per axis scales from localToWorld transform - float scaleX = length(localToWorld[0]); - float scaleY = length(localToWorld[1]); - float scaleZ = length(localToWorld[2]); - float3 invScale = float3( - scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, - scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, - scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); -#else - float3 invScale = WorldInvScale; -#endif - localToWorld[0] *= invScale.x; - localToWorld[1] *= invScale.y; - localToWorld[2] *= invScale.z; - return localToWorld; -} - -// Transforms a vector from tangent space to world space -float3 TransformTangentVectorToWorld(MaterialInput input, float3 tangentVector) -{ - return mul(tangentVector, input.TBN); -} - -// Transforms a vector from world space to tangent space -float3 TransformWorldVectorToTangent(MaterialInput input, float3 worldVector) -{ - return mul(input.TBN, worldVector); -} - -// Transforms a vector from world space to view space -float3 TransformWorldVectorToView(MaterialInput input, float3 worldVector) -{ - return mul(worldVector, (float3x3)ViewMatrix); -} - -// Transforms a vector from view space to world space -float3 TransformViewVectorToWorld(MaterialInput input, float3 viewVector) -{ - return mul((float3x3)ViewMatrix, viewVector); -} - -// Transforms a vector from local space to world space -float3 TransformLocalVectorToWorld(MaterialInput input, float3 localVector) -{ - float3x3 localToWorld = (float3x3)GetInstanceTransform(input); - //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); - return mul(localVector, localToWorld); -} - -// Transforms a vector from local space to world space -float3 TransformWorldVectorToLocal(MaterialInput input, float3 worldVector) -{ - float3x3 localToWorld = (float3x3)GetInstanceTransform(input); - //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); - return mul(localToWorld, worldVector); -} - -// Gets the current object position (supports instancing) -float3 GetObjectPosition(MaterialInput input) -{ - return input.InstanceOrigin.xyz; -} - -// Gets the current object size (supports instancing) -float3 GetObjectSize(MaterialInput input) -{ - float4x4 world = GetInstanceTransform(input); - return GeometrySize * float3(world._m00, world._m11, world._m22); -} - -// Get the current object random value (supports instancing) -float GetPerInstanceRandom(MaterialInput input) -{ - return input.InstanceParams.x; -} - -// Get the current object LOD transition dither factor (supports instancing) -float GetLODDitherFactor(MaterialInput input) -{ -#if USE_DITHERED_LOD_TRANSITION - return input.InstanceParams.y; -#else - return 0; -#endif -} - -// Gets the interpolated vertex color (in linear space) -float4 GetVertexColor(MaterialInput input) -{ -#if USE_VERTEX_COLOR - return input.VertexColor; -#else - return 1; -#endif -} - -@8 - -// Get material properties function (for vertex shader) -Material GetMaterialVS(MaterialInput input) -{ -@5 -} - -// Get material properties function (for domain shader) -Material GetMaterialDS(MaterialInput input) -{ -@6 -} - -// Get material properties function (for pixel shader) -Material GetMaterialPS(MaterialInput input) -{ -@4 -} - -// Calculates the transform matrix from mesh tangent space to local space -half3x3 CalcTangentToLocal(ModelInput input) -{ - float bitangentSign = input.Tangent.w ? -1.0f : +1.0f; - float3 normal = input.Normal.xyz * 2.0 - 1.0; - float3 tangent = input.Tangent.xyz * 2.0 - 1.0; - float3 bitangent = cross(normal, tangent) * bitangentSign; - return float3x3(tangent, bitangent, normal); -} - -half3x3 CalcTangentToWorld(in float4x4 world, in half3x3 tangentToLocal) -{ - half3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world); - return mul(tangentToLocal, localToWorld); -} - -// Vertex Shader function for Forward/Depth Pass -META_VS(IS_SURFACE, FEATURE_LEVEL_ES2) -META_PERMUTATION_1(USE_INSTANCING=0) -META_PERMUTATION_1(USE_INSTANCING=1) -META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, 0, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(COLOR, 0, R8G8B8A8_UNORM, 2, 0, PER_VERTEX, 0, USE_VERTEX_COLOR) -META_VS_IN_ELEMENT(ATTRIBUTE,0, R32G32B32A32_FLOAT,3, 0, PER_INSTANCE, 1, USE_INSTANCING) -META_VS_IN_ELEMENT(ATTRIBUTE,1, R32G32B32A32_FLOAT,3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING) -META_VS_IN_ELEMENT(ATTRIBUTE,2, R32G32B32_FLOAT, 3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING) -META_VS_IN_ELEMENT(ATTRIBUTE,3, R32G32B32_FLOAT, 3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING) -META_VS_IN_ELEMENT(ATTRIBUTE,4, R16G16B16A16_FLOAT,3, ALIGN, PER_INSTANCE, 1, USE_INSTANCING) -VertexOutput VS(ModelInput input) -{ - VertexOutput output; - - // Compute world space vertex position - float4x4 world = GetInstanceTransform(input); - output.WorldPosition = mul(float4(input.Position.xyz, 1), world).xyz; - - // Compute clip space position - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); - - // Pass vertex attributes - output.TexCoord = input.TexCoord; -#if USE_VERTEX_COLOR - output.VertexColor = input.Color; -#endif - output.LightmapUV = input.LightmapUV; -#if USE_INSTANCING - output.InstanceOrigin = world[3].xyz; - output.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); -#else - output.InstanceOrigin = WorldMatrix[3].xyz; - output.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); -#endif - - // Calculate tanget space to world space transformation matrix for unit vectors - half3x3 tangentToLocal = CalcTangentToLocal(input); - half3x3 tangentToWorld = CalcTangentToWorld(world, tangentToLocal); - output.TBN = tangentToWorld; - - // Get material input params if need to evaluate any material property -#if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS - MaterialInput materialInput = GetMaterialInput(input, output, tangentToLocal[2].xyz); - Material material = GetMaterialVS(materialInput); -#endif - - // Apply world position offset per-vertex -#if USE_POSITION_OFFSET - output.WorldPosition += material.PositionOffset; - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); -#endif - - // Get tessalation multiplier (per vertex) -#if USE_TESSELLATION - output.TessellationMultiplier = material.TessellationMultiplier; -#endif - - // Copy interpolants for other shader stages -#if USE_CUSTOM_VERTEX_INTERPOLATORS - output.CustomVSToPS = material.CustomVSToPS; -#endif - - return output; -} - -#if USE_SKINNING - -// The skeletal bones matrix buffer (stored as 4x3, 3 float4 behind each other) -Buffer BoneMatrices : register(t0); - -// Cached skinning data to avoid multiple calculation -struct SkinningData -{ - float3x4 BlendMatrix; -}; - -// Calculates the transposed transform matrix for the given bone index -float3x4 GetBoneMatrix(int index) -{ - float4 a = BoneMatrices[index * 3]; - float4 b = BoneMatrices[index * 3 + 1]; - float4 c = BoneMatrices[index * 3 + 2]; - return float3x4(a, b, c); -} - -// Calculates the transposed transform matrix for the given vertex (uses blending) -float3x4 GetBoneMatrix(ModelInput_Skinned input) -{ - float3x4 boneMatrix = input.BlendWeights.x * GetBoneMatrix(input.BlendIndices.x); - boneMatrix += input.BlendWeights.y * GetBoneMatrix(input.BlendIndices.y); - boneMatrix += input.BlendWeights.z * GetBoneMatrix(input.BlendIndices.z); - boneMatrix += input.BlendWeights.w * GetBoneMatrix(input.BlendIndices.w); - return boneMatrix; -} - -// Transforms the vertex position by weighted sum of the skinning matrices -float3 SkinPosition(ModelInput_Skinned input, SkinningData data) -{ - float4 position = float4(input.Position.xyz, 1); - return mul(data.BlendMatrix, position); -} - -// Transforms the vertex position by weighted sum of the skinning matrices -half3x3 SkinTangents(ModelInput_Skinned input, SkinningData data) -{ - // Unpack vertex tangent frame - float bitangentSign = input.Tangent.w ? -1.0f : +1.0f; - float3 normal = input.Normal.xyz * 2.0 - 1.0; - float3 tangent = input.Tangent.xyz * 2.0 - 1.0; - - // Apply skinning - tangent = mul(data.BlendMatrix, float4(tangent, 0)); - normal = mul(data.BlendMatrix, float4(normal, 0)); - - float3 bitangent = cross(normal, tangent) * bitangentSign; - return half3x3(tangent, bitangent, normal); -} - -// Vertex Shader function for Forward/Depth Pass (skinned mesh rendering) -META_VS(IS_SURFACE, FEATURE_LEVEL_ES2) -META_PERMUTATION_1(USE_SKINNING=1) -META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 0, ALIGN, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 0, ALIGN, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 0, ALIGN, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(BLENDINDICES, 0, R8G8B8A8_UINT, 0, ALIGN, PER_VERTEX, 0, true) -META_VS_IN_ELEMENT(BLENDWEIGHT, 0, R16G16B16A16_FLOAT,0, ALIGN, PER_VERTEX, 0, true) -VertexOutput VS_Skinned(ModelInput_Skinned input) -{ - VertexOutput output; - - // Perform skinning - SkinningData data; - data.BlendMatrix = GetBoneMatrix(input); - float3 position = SkinPosition(input, data); - half3x3 tangentToLocal = SkinTangents(input, data); - - // Compute world space vertex position - float4x4 world = GetInstanceTransform(input); - output.WorldPosition = mul(float4(position, 1), world).xyz; - - // Compute clip space position - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); - - // Pass vertex attributes - output.TexCoord = input.TexCoord; -#if USE_VERTEX_COLOR - output.VertexColor = float4(0, 0, 0, 1); -#endif - output.LightmapUV = float2(0, 0); -#if USE_INSTANCING - output.InstanceOrigin = world[3].xyz; - output.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); -#else - output.InstanceOrigin = WorldMatrix[3].xyz; - output.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); -#endif - - // Calculate tanget space to world space transformation matrix for unit vectors - half3x3 tangentToWorld = CalcTangentToWorld(world, tangentToLocal); - output.TBN = tangentToWorld; - - // Get material input params if need to evaluate any material property -#if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS - MaterialInput materialInput = GetMaterialInput(output, input.Position.xyz, tangentToLocal[2].xyz); - Material material = GetMaterialVS(materialInput); -#endif - - // Apply world position offset per-vertex -#if USE_POSITION_OFFSET - output.WorldPosition += material.PositionOffset; - output.Position = mul(float4(output.WorldPosition.xyz, 1), ViewProjectionMatrix); -#endif - - // Get tessalation multiplier (per vertex) -#if USE_TESSELLATION - output.TessellationMultiplier = material.TessellationMultiplier; -#endif - - // Copy interpolants for other shader stages -#if USE_CUSTOM_VERTEX_INTERPOLATORS - output.CustomVSToPS = material.CustomVSToPS; -#endif - - return output; -} - -#endif - -#if USE_DITHERED_LOD_TRANSITION - -void ClipLODTransition(PixelInput input) -{ - float ditherFactor = input.InstanceParams.y; - if (abs(ditherFactor) > 0.001) - { - float randGrid = cos(dot(floor(input.Position.xy), float2(347.83452793, 3343.28371863))); - float randGridFrac = frac(randGrid * 1000.0); - half mask = (ditherFactor < 0.0) ? (ditherFactor + 1.0 > randGridFrac) : (ditherFactor < randGridFrac); - clip(mask - 0.001); - } -} - -#endif - -// Pixel Shader function for Depth Pass -META_PS(true, FEATURE_LEVEL_ES2) -void PS_Depth(PixelInput input -#if GLSL - , out float4 OutColor : SV_Target0 -#endif - ) -{ -#if USE_DITHERED_LOD_TRANSITION - // LOD masking - ClipLODTransition(input); -#endif - - // Get material parameters - MaterialInput materialInput = GetMaterialInput(input); - Material material = GetMaterialPS(materialInput); - - // Perform per pixel clipping -#if MATERIAL_MASKED - clip(material.Mask - MATERIAL_MASK_THRESHOLD); -#endif - clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD); - -#if GLSL - OutColor = 0; -#endif -} - -@9 diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index 3bd71290d..4afbdd60d 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -67,7 +67,7 @@ void DeferredMaterialShader::Bind(BindParameters& params) byte* cb = _cb0Data.Get(); auto materialData = reinterpret_cast(cb); cb += sizeof(DeferredMaterialShaderData); - int32 srv = 0; + int32 srv = 2; // Setup features const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv); diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index 0d9108566..bf9f566ef 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -36,6 +36,7 @@ PACK_STRUCT(struct ForwardMaterialShaderData { Vector2 Dummy0; float LODDitherFactor; float PerInstanceRandom; + Vector4 TemporalAAJitter; Vector3 GeometrySize; float Dummy1; }); @@ -63,7 +64,7 @@ void ForwardMaterialShader::Bind(BindParameters& params) byte* cb = _cb0Data.Get(); auto materialData = reinterpret_cast(cb); cb += sizeof(ForwardMaterialShaderData); - int32 srv = 0; + int32 srv = 2; // Setup features ForwardShadingFeature::Bind(params, cb, srv); diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 531efe315..fb0585f83 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -396,6 +396,9 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo int32 srv = 0; switch (baseLayer->Domain) { + case MaterialDomain::Surface: + srv = 2; // Skinning Bones + Prev Bones + break; case MaterialDomain::Decal: srv = 1; // Depth buffer break; @@ -467,10 +470,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo switch (materialInfo.Domain) { case MaterialDomain::Surface: - if (materialInfo.BlendMode == MaterialBlendMode::Opaque) - path /= TEXT("SurfaceDeferred.shader"); - else - path /= TEXT("SurfaceForward.shader"); + path /= TEXT("Surface.shader"); break; case MaterialDomain::PostProcess: path /= TEXT("PostProcess.shader"); From d8304a217875a2308b5771c1355560250de216a2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 14:54:29 +0100 Subject: [PATCH 317/419] Refactor material shaders generator to use modular features as extensions --- .../Features/MotionVectors.hlsl | 44 +++++ .../Features/Tessellation.hlsl | 11 +- .../Editor/MaterialTemplates/Particle.shader | 13 +- .../Editor/MaterialTemplates/Surface.shader | 173 ++++-------------- .../Materials/DecalMaterialShader.cpp | 17 +- .../Materials/DeferredMaterialShader.cpp | 13 +- .../Materials/ForwardMaterialShader.cpp | 12 +- .../Graphics/Materials/GUIMaterialShader.cpp | 18 +- .../Graphics/Materials/MaterialShader.cpp | 32 ++-- .../Graphics/Materials/MaterialShader.h | 21 ++- .../Materials/MaterialShaderFeatures.cpp | 15 +- .../Materials/MaterialShaderFeatures.h | 10 +- .../Materials/ParticleMaterialShader.cpp | 34 ++-- .../Materials/PostFxMaterialShader.cpp | 17 +- .../Materials/TerrainMaterialShader.cpp | 12 +- .../MaterialGenerator/MaterialGenerator.cpp | 2 + 16 files changed, 185 insertions(+), 259 deletions(-) create mode 100644 Content/Editor/MaterialTemplates/Features/MotionVectors.hlsl diff --git a/Content/Editor/MaterialTemplates/Features/MotionVectors.hlsl b/Content/Editor/MaterialTemplates/Features/MotionVectors.hlsl new file mode 100644 index 000000000..992eb6805 --- /dev/null +++ b/Content/Editor/MaterialTemplates/Features/MotionVectors.hlsl @@ -0,0 +1,44 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +@0// Motion Vectors: Defines +@1// Motion Vectors: Includes +@2// Motion Vectors: Constants +@3// Motion Vectors: Resources +@4// Motion Vectors: Utilities +@5// Motion Vectors: Shaders + +// Pixel Shader function for Motion Vectors Pass +META_PS(true, FEATURE_LEVEL_ES2) +float4 PS_MotionVectors(PixelInput input) : SV_Target0 +{ +#if USE_DITHERED_LOD_TRANSITION + // LOD masking + ClipLODTransition(input); +#endif + +#if MATERIAL_MASKED + // Perform per pixel clipping if material requries it + MaterialInput materialInput = GetMaterialInput(input); + Material material = GetMaterialPS(materialInput); + clip(material.Mask - MATERIAL_MASK_THRESHOLD); +#endif + + // Calculate this and previosu frame pixel locations in clip space + float4 prevClipPos = mul(float4(input.Geometry.PrevWorldPosition, 1), PrevViewProjectionMatrix); + float4 curClipPos = mul(float4(input.Geometry.WorldPosition, 1), ViewProjectionMatrix); + float2 prevHPos = prevClipPos.xy / prevClipPos.w; + float2 curHPos = curClipPos.xy / curClipPos.w; + + // Revert temporal jitter offset + prevHPos -= TemporalAAJitter.zw; + curHPos -= TemporalAAJitter.xy; + + // Clip Space -> UV Space + float2 vPosPrev = prevHPos.xy * 0.5f + 0.5f; + float2 vPosCur = curHPos.xy * 0.5f + 0.5f; + vPosPrev.y = 1.0 - vPosPrev.y; + vPosCur.y = 1.0 - vPosCur.y; + + // Calculate per-pixel motion vector + return float4(vPosCur - vPosPrev, 0, 1); +} diff --git a/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl b/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl index f2fc714ee..e2e0b54ea 100644 --- a/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl +++ b/Content/Editor/MaterialTemplates/Features/Tessellation.hlsl @@ -32,14 +32,13 @@ struct TessalationDSToPS MaterialInput GetMaterialInput(TessalationDSToPS input) { - MaterialInput result = (MaterialInput)0; - result.SvPosition = input.Position; - GetGeometryMaterialInput(result, input.Geometry); - result.TwoSidedSign = WorldDeterminantSign; + MaterialInput output = GetGeometryMaterialInput(input.Geometry); + output.SvPosition = input.Position; + output.TwoSidedSign = WorldDeterminantSign; #if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; + output.CustomVSToPS = input.CustomVSToPS; #endif - return result; + return output; } struct TessalationPatch diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index a3befc24f..109fe668b 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -141,14 +141,11 @@ MaterialInput GetMaterialInput(PixelInput input) } // Gets the local to world transform matrix (supports instancing) -float4x4 GetInstanceTransform(ModelInput input) -{ - return WorldMatrix; -} -float4x4 GetInstanceTransform(MaterialInput input) -{ - return WorldMatrix; -} +#if USE_INSTANCING +#define GetInstanceTransform(input) float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)) +#else +#define GetInstanceTransform(input) WorldMatrix; +#endif // Removes the scale vector from the local to world transformation matrix (supports instancing) float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) diff --git a/Content/Editor/MaterialTemplates/Surface.shader b/Content/Editor/MaterialTemplates/Surface.shader index 8c09e7611..f3cc8499e 100644 --- a/Content/Editor/MaterialTemplates/Surface.shader +++ b/Content/Editor/MaterialTemplates/Surface.shader @@ -101,19 +101,21 @@ struct MaterialInput }; // Extracts geometry data to the material input -void GetGeometryMaterialInput(inout MaterialInput result, in GeometryData geometry) +MaterialInput GetGeometryMaterialInput(GeometryData geometry) { - result.WorldPosition = geometry.WorldPosition; - result.TexCoord = geometry.TexCoord; + MaterialInput output = (MaterialInput)0; + output.WorldPosition = geometry.WorldPosition; + output.TexCoord = geometry.TexCoord; #if USE_LIGHTMAP - result.LightmapUV = geometry.LightmapUV; + output.LightmapUV = geometry.LightmapUV; #endif #if USE_VERTEX_COLOR - result.VertexColor = geometry.VertexColor; + output.VertexColor = geometry.VertexColor; #endif - result.TBN = CalcTangentBasis(geometry.WorldNormal, geometry.WorldTangent); - result.InstanceOrigin = geometry.InstanceOrigin; - result.InstanceParams = geometry.InstanceParams; + output.TBN = CalcTangentBasis(geometry.WorldNormal, geometry.WorldTangent); + output.InstanceOrigin = geometry.InstanceOrigin; + output.InstanceParams = geometry.InstanceParams; + return output; } #if USE_TESSELLATION @@ -157,111 +159,23 @@ GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, flo #endif -MaterialInput GetMaterialInput(ModelInput input, VertexOutput output, float3 localNormal) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = output.Geometry.WorldPosition; - result.TexCoord = output.Geometry.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = output.Geometry.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = output.Geometry.VertexColor; -#endif - result.TBN = CalcTangentBasis(output.Geometry.WorldNormal, output.Geometry.WorldTangent); - result.TwoSidedSign = WorldDeterminantSign; - result.SvPosition = output.Position; - result.PreSkinnedPosition = input.Position.xyz; - result.PreSkinnedNormal = localNormal; -#if USE_INSTANCING - result.InstanceOrigin = input.InstanceOrigin.xyz; - result.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); - result.InstanceTransform1 = input.InstanceTransform1.xyz; - result.InstanceTransform2 = input.InstanceTransform2.xyz; - result.InstanceTransform3 = input.InstanceTransform3.xyz; -#else - result.InstanceOrigin = WorldMatrix[3].xyz; - result.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); -#endif - return result; -} - -MaterialInput GetMaterialInput(VertexOutput output, float3 localPosition, float3 localNormal) -{ - MaterialInput result = (MaterialInput)0; - result.WorldPosition = output.Geometry.WorldPosition; - result.TexCoord = output.Geometry.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = output.Geometry.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = output.Geometry.VertexColor; -#endif - result.TBN = CalcTangentBasis(output.Geometry.WorldNormal, output.Geometry.WorldTangent); - result.TwoSidedSign = WorldDeterminantSign; - result.InstanceOrigin = WorldMatrix[3].xyz; - result.InstanceParams = float2(PerInstanceRandom, LODDitherFactor); - result.SvPosition = output.Position; - result.PreSkinnedPosition = localPosition; - result.PreSkinnedNormal = localNormal; - return result; -} - MaterialInput GetMaterialInput(PixelInput input) { - MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.Geometry.WorldPosition; - result.TexCoord = input.Geometry.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.Geometry.LightmapUV; -#endif -#if USE_VERTEX_COLOR - result.VertexColor = input.Geometry.VertexColor; -#endif - result.TBN = CalcTangentBasis(input.Geometry.WorldNormal, input.Geometry.WorldTangent); - result.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); - result.InstanceOrigin = input.Geometry.InstanceOrigin; - result.InstanceParams = input.Geometry.InstanceParams; - result.SvPosition = input.Position; + MaterialInput output = GetGeometryMaterialInput(input.Geometry); + output.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); + output.SvPosition = input.Position; #if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; + output.CustomVSToPS = input.CustomVSToPS; #endif - return result; + return output; } // Gets the local to world transform matrix (supports instancing) -float4x4 GetInstanceTransform(ModelInput input) -{ #if USE_INSTANCING - return float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)); +#define GetInstanceTransform(input) float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)) #else - return WorldMatrix; +#define GetInstanceTransform(input) WorldMatrix; #endif -} -float4x4 GetInstanceTransform(ModelInput_Skinned input) -{ -#if USE_INSTANCING - return float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)); -#else - return WorldMatrix; -#endif -} -float4x4 GetInstanceTransform(ModelInput_PosOnly input) -{ -#if USE_INSTANCING - return float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)); -#else - return WorldMatrix; -#endif -} -float4x4 GetInstanceTransform(MaterialInput input) -{ -#if USE_INSTANCING - return float4x4(float4(input.InstanceTransform1.xyz, 0.0f), float4(input.InstanceTransform2.xyz, 0.0f), float4(input.InstanceTransform3.xyz, 0.0f), float4(input.InstanceOrigin.xyz, 1.0f)); -#else - return WorldMatrix; -#endif -} // Removes the scale vector from the local to world transformation matrix (supports instancing) float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) @@ -453,7 +367,16 @@ VertexOutput VS(ModelInput input) // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS - MaterialInput materialInput = GetMaterialInput(input, output, tangentToLocal[2].xyz); + MaterialInput materialInput = GetGeometryMaterialInput(output.Geometry); + materialInput.TwoSidedSign = WorldDeterminantSign; + materialInput.SvPosition = output.Position; + materialInput.PreSkinnedPosition = input.Position.xyz; + materialInput.PreSkinnedNormal = tangentToLocal[2].xyz; +#if USE_INSTANCING + materialInput.InstanceTransform1 = input.InstanceTransform1.xyz; + materialInput.InstanceTransform2 = input.InstanceTransform2.xyz; + materialInput.InstanceTransform3 = input.InstanceTransform3.xyz; +#endif Material material = GetMaterialVS(materialInput); #endif @@ -626,7 +549,11 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) // Get material input params if need to evaluate any material property #if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS - MaterialInput materialInput = GetMaterialInput(output, input.Position.xyz, tangentToLocal[2].xyz); + MaterialInput materialInput = GetGeometryMaterialInput(output.Geometry); + materialInput.TwoSidedSign = WorldDeterminantSign; + materialInput.SvPosition = output.Position; + materialInput.PreSkinnedPosition = input.Position.xyz; + materialInput.PreSkinnedNormal = tangentToLocal[2].xyz; Material material = GetMaterialVS(materialInput); #endif @@ -699,40 +626,4 @@ void PS_Depth(PixelInput input #endif } -// Pixel Shader function for Motion Vectors Pass -META_PS(USE_DEFERRED, FEATURE_LEVEL_ES2) -float4 PS_MotionVectors(PixelInput input) : SV_Target0 -{ -#if USE_DITHERED_LOD_TRANSITION - // LOD masking - ClipLODTransition(input); -#endif - -#if MATERIAL_MASKED - // Perform per pixel clipping if material requries it - MaterialInput materialInput = GetMaterialInput(input); - Material material = GetMaterialPS(materialInput); - clip(material.Mask - MATERIAL_MASK_THRESHOLD); -#endif - - // Calculate this and previosu frame pixel locations in clip space - float4 prevClipPos = mul(float4(input.Geometry.PrevWorldPosition, 1), PrevViewProjectionMatrix); - float4 curClipPos = mul(float4(input.Geometry.WorldPosition, 1), ViewProjectionMatrix); - float2 prevHPos = prevClipPos.xy / prevClipPos.w; - float2 curHPos = curClipPos.xy / curClipPos.w; - - // Revert temporal jitter offset - prevHPos -= TemporalAAJitter.zw; - curHPos -= TemporalAAJitter.xy; - - // Clip Space -> UV Space - float2 vPosPrev = prevHPos.xy * 0.5f + 0.5f; - float2 vPosCur = curHPos.xy * 0.5f + 0.5f; - vPosPrev.y = 1.0 - vPosPrev.y; - vPosCur.y = 1.0 - vPosCur.y; - - // Calculate per-pixel motion vector - return float4(vPosCur - vPosPrev, 0, 1); -} - @9 diff --git a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp index 27e9c7b38..e141996dc 100644 --- a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp @@ -37,14 +37,16 @@ void DecalMaterialShader::Bind(BindParameters& params) auto context = params.GPUContext; auto& view = params.RenderContext.View; auto& drawCall = *params.FirstDrawCall; - const auto cb0 = _shader->GetCB(0); - const bool hasCb0 = cb0->GetSize() != 0; + byte* cb = _cbData.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(DecalMaterialShaderData); + int32 srv = 0; const bool isCameraInside = OrientedBoundingBox(Vector3::Half, params.FirstDrawCall->World).Contains(view.Position) == ContainmentType::Contains; // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(DecalMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = true; @@ -55,10 +57,7 @@ void DecalMaterialShader::Bind(BindParameters& params) context->BindSR(0, GET_TEXTURE_VIEW_SAFE(params.RenderContext.Buffers->DepthBuffer)); // Setup material constants data - if (hasCb0) { - const auto materialData = reinterpret_cast(_cb0Data.Get()); - Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); Matrix::Transpose(view.View, materialData->ViewMatrix); @@ -85,10 +84,10 @@ void DecalMaterialShader::Bind(BindParameters& params) } // Bind constants - if (hasCb0) + if (_cb) { - context->UpdateCB(cb0, _cb0Data.Get()); - context->BindCB(0, cb0); + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); } // Bind pipeline diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index 4afbdd60d..d8b40c3a6 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -61,10 +61,7 @@ void DeferredMaterialShader::Bind(BindParameters& params) auto context = params.GPUContext; auto& view = params.RenderContext.View; auto& drawCall = *params.FirstDrawCall; - const auto cb0 = _shader->GetCB(0); - const bool hasCb0 = cb0 && cb0->GetSize() != 0; - ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing - byte* cb = _cb0Data.Get(); + byte* cb = _cbData.Get(); auto materialData = reinterpret_cast(cb); cb += sizeof(DeferredMaterialShaderData); int32 srv = 2; @@ -82,8 +79,6 @@ void DeferredMaterialShader::Bind(BindParameters& params) bindMeta.CanSampleGBuffer = false; MaterialParams::Bind(params.ParamsLink, bindMeta); - // Setup material constants data - if (hasCb0) { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); @@ -131,10 +126,10 @@ void DeferredMaterialShader::Bind(BindParameters& params) } // Bind constants - if (hasCb0) + if (_cb) { - context->UpdateCB(cb0, _cb0Data.Get()); - context->BindCB(0, cb0); + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); } // Select pipeline state based on current pass and render mode diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index bf9f566ef..addd7e96a 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -58,10 +58,7 @@ void ForwardMaterialShader::Bind(BindParameters& params) auto context = params.GPUContext; auto& view = params.RenderContext.View; auto& drawCall = *params.FirstDrawCall; - const auto cb0 = _shader->GetCB(0); - const bool hasCb0 = cb0 && cb0->GetSize() != 0; - ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing - byte* cb = _cb0Data.Get(); + byte* cb = _cbData.Get(); auto materialData = reinterpret_cast(cb); cb += sizeof(ForwardMaterialShaderData); int32 srv = 2; @@ -89,7 +86,6 @@ void ForwardMaterialShader::Bind(BindParameters& params) } // Setup material constants data - if (hasCb0) { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); @@ -121,10 +117,10 @@ void ForwardMaterialShader::Bind(BindParameters& params) } // Bind constants - if (hasCb0) + if (_cb) { - context->UpdateCB(cb0, _cb0Data.Get()); - context->BindCB(0, cb0); + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); } // Select pipeline state based on current pass and render mode diff --git a/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp b/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp index 23b327a1b..9e1d9ea21 100644 --- a/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp @@ -28,15 +28,16 @@ void GUIMaterialShader::Bind(BindParameters& params) { // Prepare auto context = params.GPUContext; - auto& view = params.RenderContext.View; - const auto cb0 = _shader->GetCB(0); - const bool hasCb0 = cb0->GetSize() != 0; + byte* cb = _cbData.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(GUIMaterialShaderData); + int32 srv = 0; const auto ps = context->IsDepthBufferBinded() ? _cache.Depth : _cache.NoDepth; // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(GUIMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = nullptr; bindMeta.Buffers = nullptr; bindMeta.CanSampleDepth = false; @@ -44,10 +45,7 @@ void GUIMaterialShader::Bind(BindParameters& params) MaterialParams::Bind(params.ParamsLink, bindMeta); // Setup material constants data - if (hasCb0) { - auto materialData = reinterpret_cast(_cb0Data.Get()); - const auto viewProjectionMatrix = (Matrix*)params.CustomData; Matrix::Transpose(*viewProjectionMatrix, materialData->ViewProjectionMatrix); Matrix::Transpose(Matrix::Identity, materialData->WorldMatrix); @@ -62,10 +60,10 @@ void GUIMaterialShader::Bind(BindParameters& params) } // Bind constants - if (hasCb0) + if (_cb) { - context->UpdateCB(cb0, _cb0Data.Get()); - context->BindCB(0, cb0); + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); } // Bind pipeline diff --git a/Source/Engine/Graphics/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp index ee1cf22c4..2d7998b8a 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp @@ -14,18 +14,15 @@ #include "GUIMaterialShader.h" #include "TerrainMaterialShader.h" #include "ParticleMaterialShader.h" +//#include "DeformableMaterialShader.h" -GPUPipelineState* MaterialShader::PipelineStateCache::GetPS(CullMode mode, bool wireframe) +GPUPipelineState* MaterialShader::PipelineStateCache::InitPS(CullMode mode, bool wireframe) { - const int32 index = static_cast(mode) + (wireframe ? 3 : 0); - if (PS[index]) - return PS[index]; - Desc.CullMode = mode; Desc.Wireframe = wireframe; - PS[index] = GPUDevice::Instance->CreatePipelineState(); - PS[index]->Init(Desc); - return PS[index]; + auto ps = GPUDevice::Instance->CreatePipelineState(); + ps->Init(Desc); + return ps; } MaterialShader::MaterialShader(const String& name) @@ -65,6 +62,9 @@ MaterialShader* MaterialShader::Create(const String& name, MemoryReadStream& sha case MaterialDomain::Particle: material = New(name); break; + /*case MaterialDomain::Deformable: + material = New(name); + break;*/ default: LOG(Fatal, "Unknown material type."); return nullptr; @@ -138,10 +138,17 @@ bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInf } // Init memory for a constant buffer - const auto cb0 = _shader->GetCB(0); - if (cb0) + _cb = _shader->GetCB(0); + if (_cb) { - _cb0Data.Resize(cb0->GetSize(), false); + int32 cbSize = _cb->GetSize(); + if (cbSize == 0) + { + // Handle unused constant buffer (eg. postFx returning solid color) + cbSize = 1024; + _cb = nullptr; + } + _cbData.Resize(cbSize, false); } // Initialize the material based on type (create pipeline states and setup) @@ -157,6 +164,7 @@ bool MaterialShader::Load(MemoryReadStream& shaderCacheStream, const MaterialInf void MaterialShader::Unload() { _isLoaded = false; - _cb0Data.Resize(0, false); + _cb = nullptr; + _cbData.Resize(0, false); _shader->ReleaseGPU(); } diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index baa463d3f..102ac4d41 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -9,10 +9,11 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 146 +#define MATERIAL_GRAPH_VERSION 147 class Material; class GPUShader; +class GPUConstantBuffer; class MemoryReadStream; /// @@ -37,7 +38,16 @@ protected: Desc = desc; } - GPUPipelineState* GetPS(CullMode mode, bool wireframe); + GPUPipelineState* GetPS(CullMode mode, bool wireframe) + { + const int32 index = static_cast(mode) + (wireframe ? 3 : 0); + auto ps = PS[index]; + if (!ps) + PS[index] = ps = InitPS(mode, wireframe); + return ps; + } + + GPUPipelineState* InitPS(CullMode mode, bool wireframe); void Release() { @@ -52,7 +62,8 @@ protected: bool _isLoaded; GPUShader* _shader; - Array _cb0Data; + GPUConstantBuffer* _cb; + Array _cbData; MaterialInfo _info; protected: @@ -89,10 +100,8 @@ public: /// The created and loaded material or null if failed. static MaterialShader* CreateDummy(MemoryReadStream& shaderCacheStream, const MaterialInfo& info); -public: - /// - /// Clear loaded data + /// Clears the loaded data. /// virtual void Unload(); diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 2a6a97ffa..abc28a0de 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -158,36 +158,31 @@ bool LightmapFeature::Bind(MaterialShader::BindParameters& params, byte*& cb, in void ForwardShadingFeature::Generate(GeneratorData& data) { data.Template = TEXT("Features/ForwardShading.hlsl"); - data.ConstantsSize = sizeof(Data); - data.ResourcesCount = SRVs; } void DeferredShadingFeature::Generate(GeneratorData& data) { data.Template = TEXT("Features/DeferredShading.hlsl"); - data.ConstantsSize = 0; - data.ResourcesCount = 0; } void TessellationFeature::Generate(GeneratorData& data) { data.Template = TEXT("Features/Tessellation.hlsl"); - data.ConstantsSize = 0; - data.ResourcesCount = 0; } void LightmapFeature::Generate(GeneratorData& data) { data.Template = TEXT("Features/Lightmap.hlsl"); - data.ConstantsSize = sizeof(Data); - data.ResourcesCount = SRVs; } void DistortionFeature::Generate(GeneratorData& data) { data.Template = TEXT("Features/Distortion.hlsl"); - data.ConstantsSize = 0; - data.ResourcesCount = 0; +} + +void MotionVectorsFeature::Generate(GeneratorData& data) +{ + data.Template = TEXT("Features/MotionVectors.hlsl"); } #endif diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h index 3128a688d..a70d9ada8 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.h @@ -12,8 +12,6 @@ struct MaterialShaderFeature struct GeneratorData { const Char* Template; - int32 ConstantsSize; - int32 ResourcesCount; }; #endif; }; @@ -82,3 +80,11 @@ struct DistortionFeature : MaterialShaderFeature static void Generate(GeneratorData& data); #endif }; + +// Material shader feature that adds motion vectors rendering pass. +struct MotionVectorsFeature : MaterialShaderFeature +{ +#if USE_EDITOR + static void Generate(GeneratorData& data); +#endif +}; diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index 8ee49ae78..eda7fff22 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -60,10 +60,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) auto& view = params.RenderContext.View; auto& drawCall = *params.FirstDrawCall; const uint32 sortedIndicesOffset = drawCall.Particle.Module->SortedIndicesOffset; - const auto cb0 = _shader->GetCB(0); - const bool hasCb0 = cb0->GetSize() != 0; - ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing - byte* cb = _cb0Data.Get(); + byte* cb = _cbData.Get(); auto materialData = reinterpret_cast(cb); cb += sizeof(ParticleMaterialShaderData); int32 srv = 2; @@ -86,7 +83,6 @@ void ParticleMaterialShader::Bind(BindParameters& params) context->BindSR(1, drawCall.Particle.Particles->GPU.SortedIndices ? drawCall.Particle.Particles->GPU.SortedIndices->View() : nullptr); // Setup particles attributes binding info - if (hasCb0) { const auto& p = *params.ParamsLink->This; for (int32 i = 0; i < p.Count(); i++) @@ -129,19 +125,16 @@ void ParticleMaterialShader::Bind(BindParameters& params) static StringView ParticleRibbonTwist(TEXT("RibbonTwist")); static StringView ParticleRibbonFacingVector(TEXT("RibbonFacingVector")); - if (hasCb0) - { - materialData->RibbonWidthOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1); - materialData->RibbonTwistOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1); - materialData->RibbonFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1); + materialData->RibbonWidthOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1); + materialData->RibbonTwistOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1); + materialData->RibbonFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1); - materialData->RibbonUVTilingDistance = drawCall.Particle.Ribbon.UVTilingDistance; - materialData->RibbonUVScale.X = drawCall.Particle.Ribbon.UVScaleX; - materialData->RibbonUVScale.Y = drawCall.Particle.Ribbon.UVScaleY; - materialData->RibbonUVOffset.X = drawCall.Particle.Ribbon.UVOffsetX; - materialData->RibbonUVOffset.Y = drawCall.Particle.Ribbon.UVOffsetY; - materialData->RibbonSegmentCount = drawCall.Particle.Ribbon.SegmentCount; - } + materialData->RibbonUVTilingDistance = drawCall.Particle.Ribbon.UVTilingDistance; + materialData->RibbonUVScale.X = drawCall.Particle.Ribbon.UVScaleX; + materialData->RibbonUVScale.Y = drawCall.Particle.Ribbon.UVScaleY; + materialData->RibbonUVOffset.X = drawCall.Particle.Ribbon.UVOffsetX; + materialData->RibbonUVOffset.Y = drawCall.Particle.Ribbon.UVOffsetY; + materialData->RibbonSegmentCount = drawCall.Particle.Ribbon.SegmentCount; if (drawCall.Particle.Ribbon.SegmentDistances) context->BindSR(1, drawCall.Particle.Ribbon.SegmentDistances->View()); @@ -153,7 +146,6 @@ void ParticleMaterialShader::Bind(BindParameters& params) GPUPipelineState* state = psCache->GetPS(cullMode, wireframe); // Setup material constants data - if (hasCb0) { static StringView ParticlePosition(TEXT("Position")); static StringView ParticleSpriteSize(TEXT("SpriteSize")); @@ -188,10 +180,10 @@ void ParticleMaterialShader::Bind(BindParameters& params) } // Bind constants - if (hasCb0) + if (_cb) { - context->UpdateCB(cb0, _cb0Data.Get()); - context->BindCB(0, cb0); + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); } // Bind pipeline diff --git a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp index 8be86a4ac..890275f68 100644 --- a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp @@ -26,13 +26,15 @@ void PostFxMaterialShader::Bind(BindParameters& params) // Prepare auto context = params.GPUContext; auto& view = params.RenderContext.View; - const auto cb0 = _shader->GetCB(0); - const bool hasCb0 = cb0->GetSize() != 0; + byte* cb = _cbData.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(PostFxMaterialShaderData); + int32 srv = 0; // Setup parameters MaterialParameter::BindMeta bindMeta; bindMeta.Context = context; - bindMeta.Constants = hasCb0 ? _cb0Data.Get() + sizeof(PostFxMaterialShaderData) : nullptr; + bindMeta.Constants = cb; bindMeta.Input = params.Input; bindMeta.Buffers = params.RenderContext.Buffers; bindMeta.CanSampleDepth = true; @@ -40,10 +42,7 @@ void PostFxMaterialShader::Bind(BindParameters& params) MaterialParams::Bind(params.ParamsLink, bindMeta); // Setup material constants data - if (hasCb0) { - const auto materialData = reinterpret_cast(_cb0Data.Get()); - Matrix::Transpose(view.View, materialData->ViewMatrix); materialData->ViewPos = view.Position; materialData->ViewFar = view.Far; @@ -55,10 +54,10 @@ void PostFxMaterialShader::Bind(BindParameters& params) } // Bind constants - if (hasCb0) + if (_cb) { - context->UpdateCB(cb0, _cb0Data.Get()); - context->BindCB(0, cb0); + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); } // Bind pipeline diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index 89a96d902..80988e405 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -54,10 +54,7 @@ void TerrainMaterialShader::Bind(BindParameters& params) auto context = params.GPUContext; auto& view = params.RenderContext.View; auto& drawCall = *params.FirstDrawCall; - const auto cb0 = _shader->GetCB(0); - const bool hasCb0 = cb0->GetSize() != 0; - ASSERT(hasCb0 && "TODO: fix it"); // TODO: always make cb pointer valid even if cb is missing - byte* cb = _cb0Data.Get(); + byte* cb = _cbData.Get(); auto materialData = reinterpret_cast(cb); cb += sizeof(TerrainMaterialShaderData); int32 srv = 3; @@ -76,7 +73,6 @@ void TerrainMaterialShader::Bind(BindParameters& params) MaterialParams::Bind(params.ParamsLink, bindMeta); // Setup material constants data - if (hasCb0) { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); @@ -114,10 +110,10 @@ void TerrainMaterialShader::Bind(BindParameters& params) context->BindSR(2, splatmap1); // Bind constants - if (hasCb0) + if (_cb) { - context->UpdateCB(cb0, _cb0Data.Get()); - context->BindCB(0, cb0); + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); } // Select pipeline state based on current pass and render mode diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index fb0585f83..2f54efe82 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -185,6 +185,8 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo if (materialInfo.TessellationMode != TessellationMethod::None) ADD_FEATURE(TessellationFeature); if (materialInfo.BlendMode == MaterialBlendMode::Opaque) + ADD_FEATURE(MotionVectorsFeature); + if (materialInfo.BlendMode == MaterialBlendMode::Opaque) ADD_FEATURE(LightmapFeature); if (materialInfo.BlendMode == MaterialBlendMode::Opaque) ADD_FEATURE(DeferredShadingFeature); From 5a561b1278128e22a8770aefce06680193ee92a9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 15:30:01 +0100 Subject: [PATCH 318/419] Update materials --- Content/Editor/Camera/M_Camera.flax | 4 +-- .../Editor/CubeTexturePreviewMaterial.flax | 4 +-- Content/Editor/DefaultFontMaterial.flax | 4 +-- .../Editor/Gizmo/FoliageBrushMaterial.flax | 4 +-- Content/Editor/Gizmo/Material.flax | 4 +-- Content/Editor/Gizmo/MaterialWire.flax | 4 +-- .../Gizmo/SelectionOutlineMaterial.flax | 4 +-- .../Gizmo/VertexColorsPreviewMaterial.flax | 4 +-- Content/Editor/Highlight Material.flax | 4 +-- Content/Editor/Icons/IconsMaterial.flax | 4 +-- Content/Editor/IesProfilePreviewMaterial.flax | 4 +-- .../Editor/MaterialTemplates/Terrain.shader | 36 ++++++++----------- .../Particles/Particle Material Color.flax | 4 +-- Content/Editor/Particles/Smoke Material.flax | 4 +-- .../Editor/Terrain/Circle Brush Material.flax | 4 +-- .../Terrain/Highlight Terrain Material.flax | 4 +-- Content/Editor/TexturePreviewMaterial.flax | 4 +-- Content/Editor/Wires Debug Material.flax | 4 +-- Content/Engine/DefaultMaterial.flax | 4 +-- Content/Engine/DefaultTerrainMaterial.flax | 4 +-- Content/Engine/SingleColorMaterial.flax | 4 +-- Content/Engine/SkyboxMaterial.flax | 4 +-- 22 files changed, 56 insertions(+), 64 deletions(-) diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index b5473caa3..9e2154ed9 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8b77651be81af931c1e63c1cb411b9f2764b4b0be4a7fa51e2c3c043a063435 -size 38085 +oid sha256:919822dc6234c58fe9efa48676845cb3f780cc2ad3a8b8edcb2badb6cfb8183c +size 30119 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index 90bc9f368..c16307620 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:959f9d4006dc11b0ecbd57cbb406213b2519a388de0d798bff69d61c2042c89b -size 39859 +oid sha256:16b75ef1da889ff19d3f7cbd52073358cd0a433cd84912bd9802e3c21fcf90f2 +size 31676 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index a0f703a85..9fc057b01 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e424d2436f4973a0ecef28c241e222e7e5f992a2ae017e943f4ba033608b3bd7 -size 38383 +oid sha256:7addee999e9325dac84f0ed70a817dbbdd549b84dfa7d0260403cca89abb399a +size 30200 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index d8cc3fb6a..8b33f3404 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3b239a3122fe40c386c3161576cf74c94248d92a3d501b18ed2d658f18742a2 -size 41087 +oid sha256:792fd8ca00b3ad5b06a43a66cbca624e96af845c157519bf6038c08ae3d6dcd6 +size 34661 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index b4917f502..fb9729627 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85435f0342a96eeebad705692df74ec19871e28bc35a3d3046f8f63dee4d9334 -size 35945 +oid sha256:28de48669af8729327e4d3797076c1c28674ec86edf90d365d5f7426d7f23f01 +size 31187 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index 888846500..d0a37c641 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ab58f495cdd9fee4c81332a291bbee4f1adc327d7d14b5158d4360aac1ef8fa -size 35158 +oid sha256:e00d2943edb8ab1a1cdd2c7adfabc5e418fd9c0f414ed54a97ba33a3c1b23aac +size 30400 diff --git a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax index 48a87ba88..6a082e985 100644 --- a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax +++ b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f578d4b9e0dbd295de0674cf46cfc5d2894db225fd2c3cfc443c6f2e9c7167d3 -size 15941 +oid sha256:ae4775c4ade6c4e50f81b68a8a8546696f58bb1d701f2d8397a47d757707782f +size 15863 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index e42b74129..5dbab8006 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2abe350d858eeb42158b6c5fa8b0875a332bc003038fe846bf63aa3a9117577 -size 39316 +oid sha256:d5c14383f163282e144d9af8d89a817ea4f450ef200164dcd6f4af1406936d4a +size 31134 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index 9f1de8fcd..48ec19fdb 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0eba0cb1115fb6510ed32159a8fa5a81bbfabfd0e6ec00f4d5ccbfa0426fad97 -size 35356 +oid sha256:6bf05e8133b46a764b6bda2a0b80a9a7dd44fcf3a9169e4da7a99d72fade6c67 +size 28995 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index 91bba0296..30c96a8ef 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51bceae1877ea1859c8757a1e21abade8cf21eda8857d3204bf4b0239fc66d18 -size 35289 +oid sha256:6820dc041a4dc8fa66ac4dbacaf28efffb9a7083ac16b53850d8bfdb3b4fe80a +size 28927 diff --git a/Content/Editor/IesProfilePreviewMaterial.flax b/Content/Editor/IesProfilePreviewMaterial.flax index f9e249e81..7dbe080c9 100644 --- a/Content/Editor/IesProfilePreviewMaterial.flax +++ b/Content/Editor/IesProfilePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:946c1c4723c46a55c2d47a67009810a58c6e790b0af27137507e2c5cb9f3d64e -size 18669 +oid sha256:58bba43288fb4b2dbcc8a6401ac765fd269bb231c1b19c64aa2bb9ca72e9c106 +size 18415 diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index ef8afa2a6..d7624dfd6 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -104,18 +104,20 @@ struct MaterialInput }; // Extracts geometry data to the material input -void GetGeometryMaterialInput(inout MaterialInput result, in GeometryData geometry) +MaterialInput GetGeometryMaterialInput(GeometryData geometry) { - result.WorldPosition = geometry.WorldPosition; - result.TexCoord = geometry.TexCoord; + MaterialInput output = (MaterialInput)0; + output.WorldPosition = geometry.WorldPosition; + output.TexCoord = geometry.TexCoord; #if USE_LIGHTMAP - result.LightmapUV = geometry.LightmapUV; + output.LightmapUV = geometry.LightmapUV; #endif - result.TBN = CalcTangentBasisFromWorldNormal(geometry.WorldNormal); - result.HolesMask = geometry.HolesMask; + output.TBN = CalcTangentBasisFromWorldNormal(geometry.WorldNormal); + output.HolesMask = geometry.HolesMask; #if USE_TERRAIN_LAYERS - result.Layers = geometry.Layers; + output.Layers = geometry.Layers; #endif + return output; } #if USE_TESSELLATION @@ -154,23 +156,13 @@ GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, flo MaterialInput GetMaterialInput(PixelInput input) { - MaterialInput result = (MaterialInput)0; - result.WorldPosition = input.Geometry.WorldPosition; - result.TexCoord = input.Geometry.TexCoord; -#if USE_LIGHTMAP - result.LightmapUV = input.Geometry.LightmapUV; -#endif - result.TBN = CalcTangentBasisFromWorldNormal(input.Geometry.WorldNormal); - result.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); - result.SvPosition = input.Position; - result.HolesMask = input.Geometry.HolesMask; -#if USE_TERRAIN_LAYERS - result.Layers = input.Geometry.Layers; -#endif + MaterialInput output = GetGeometryMaterialInput(input.Geometry); + output.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); + output.SvPosition = input.Position; #if USE_CUSTOM_VERTEX_INTERPOLATORS - result.CustomVSToPS = input.CustomVSToPS; + output.CustomVSToPS = input.CustomVSToPS; #endif - return result; + return output; } // Removes the scale vector from the local to world transformation matrix diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index 6c83bfc35..577b1689e 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bf5ada0acae289fa7fbbbd6135779ef544d36999de9df0f29b2b9ce10ad52bb -size 30474 +oid sha256:15b814eb7cf2a263562004f3d440835ef50e6cf11be211c8b0171040ce87972d +size 29326 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index a3ae100ca..a29ea1649 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c8642a2a7024ae38ae371d95c3411d8e362f42ef98c54dc9da9c061d2a568c1 -size 36552 +oid sha256:6f0df0dea34c8105df1f26a60f5ba013e28d125297b1d630b62d6333ce3ec966 +size 35425 diff --git a/Content/Editor/Terrain/Circle Brush Material.flax b/Content/Editor/Terrain/Circle Brush Material.flax index dc30e39fe..08aed3e07 100644 --- a/Content/Editor/Terrain/Circle Brush Material.flax +++ b/Content/Editor/Terrain/Circle Brush Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd0529d1088670eb9edd3533f9493d259bcc9797d0e01f12922f6f3b05daef4d -size 33136 +oid sha256:df1d754dc7a6046740e50ba20bc90278aa4894d83d01f41d7fdb50455e6d7763 +size 27923 diff --git a/Content/Editor/Terrain/Highlight Terrain Material.flax b/Content/Editor/Terrain/Highlight Terrain Material.flax index 49f76cb07..8e39ed806 100644 --- a/Content/Editor/Terrain/Highlight Terrain Material.flax +++ b/Content/Editor/Terrain/Highlight Terrain Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:820f0265ca7249f84f0c91b402f817dc5ca12a0a5884756b6d0bf712c12a9b06 -size 26815 +oid sha256:db149ec425c1baf245356eff3a819c3c06e857550e86d419c7bd70c55bde4b94 +size 21602 diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax index 0a68763cd..cc3b32eb9 100644 --- a/Content/Editor/TexturePreviewMaterial.flax +++ b/Content/Editor/TexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9029bd6c09c686f551356800b6f16c60f7392fb3a396d2fe583e6cb1f83c46f7 -size 10907 +oid sha256:f92a749ea24fc5d7b45e0e4f2be5619402163f4292d565e5fe014268a46a1275 +size 10653 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index 9dedbfea2..2839044c1 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb08e24a379cd67db22025bf5e3ae84979f0686179db871387d388f11aae20f0 -size 35356 +oid sha256:0ce7aa7e754b27beda160a78e0636a6d6e12f99c59c127ca0557fc3d5a75285e +size 28995 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index cd7cd19e0..2472013e3 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d195b3434472636eff8e34a2052f1ef5d02b1415802414bad6e15f21d0b16de8 -size 39864 +oid sha256:88a4aa4b72322807ce9fcf30faca1d2e20ae78a65bdefd29ae16bf2596d93d8f +size 31898 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index be5145720..9104a5a20 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98a8347591e895fccbe327f393d6376ad1113321daa8a7ad4f47d0753c2e76a1 -size 28681 +oid sha256:c82750f189ac803962c8b6d885b54aa3332af7249d4a53c00a6e530161d157e7 +size 23373 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index d11046007..fe51af1bb 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:502f451e0b232b4288a85a274d81d22a7053c84f174286a05043707dc75fe193 -size 38381 +oid sha256:0569cdb10e6c832ba0d741144e54e92c31e25b9e8555743ae21c6a1bd8cc784e +size 30199 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index 24d56521b..f099cb9e4 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4b60416f9e2deb1f717860ae9a4cc1a74be0517db921526d21a79434b7506e7 -size 39603 +oid sha256:a29b3b287910fecb955b5627bca91a202cb1663f78ea642e4dfde911add86ee6 +size 31420 From 0e57b32082846e52c81bc72047d22c85b9606d34 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Fri, 5 Feb 2021 15:49:01 +0100 Subject: [PATCH 319/419] Fixed issue #210 This will fix the issue described in #210. Co-Authored-By: VNC <52937757+VNNCC@users.noreply.github.com> --- Source/Editor/Content/Tree/ContentTreeNode.cs | 6 ++- Source/Editor/Windows/ContentWindow.cs | 38 +++++-------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 7df407e08..07ce7f803 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -295,7 +295,8 @@ namespace FlaxEditor.Content StartRenaming(); return true; case KeyboardKeys.Delete: - Editor.Instance.Windows.ContentWin.Delete(Folder); + if(Folder.Exists) + Editor.Instance.Windows.ContentWin.Delete(Folder); return true; } if (RootWindow.GetKey(KeyboardKeys.Control)) @@ -303,7 +304,8 @@ namespace FlaxEditor.Content switch (key) { case KeyboardKeys.D: - Editor.Instance.Windows.ContentWin.Duplicate(Folder); + if(Folder.Exists) + Editor.Instance.Windows.ContentWin.Duplicate(Folder); return true; } } diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 66ed50daf..a09bc1c6e 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -396,7 +396,7 @@ namespace FlaxEditor.Windows /// The item to delete. public void Delete(ContentItem item) { - Delete(new List { item }); + Delete(new List(1) { item }); } /// @@ -405,42 +405,24 @@ namespace FlaxEditor.Windows /// The items to delete. public void Delete(List items) { + if (items.Count == 0) return; + // TODO: remove items that depend on different items in the list: use wants to remove `folderA` and `folderA/asset.x`, we should just remove `folderA` var toDelete = new List(items); + string msg = toDelete.Count == 1 ? + string.Format("Are you sure to delete \'{0}\'?\nThis action cannot be undone. Files will be deleted permanently.", items[0].Path) + : string.Format("Are you sure to delete {0} selected items?\nThis action cannot be undone. Files will be deleted permanently.", items.Count); + // Ask user - if (toDelete.Count == 1) - { - // Single item - if (MessageBox.Show(string.Format("Are you sure to delete \'{0}\'?\nThis action cannot be undone. Files will be deleted permanently.", items[0].Path), - "Delete asset(s)", - MessageBoxButtons.OKCancel, - MessageBoxIcon.Question) - != DialogResult.OK) - { - // Break - return; - } - } - else - { - // Many items - if (MessageBox.Show(string.Format("Are you sure to delete {0} selected items?\nThis action cannot be undone. Files will be deleted permanently.", items.Count), - "Delete asset(s)", - MessageBoxButtons.OKCancel, - MessageBoxIcon.Question) - != DialogResult.OK) - { - // Break - return; - } - } + if (MessageBox.Show(msg, "Delete asset(s)", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) + return; // Clear navigation // TODO: just remove invalid locations from the history (those are removed) NavigationClearHistory(); - // Delete items + // Delete for (int i = 0; i < toDelete.Count; i++) Editor.ContentDatabase.Delete(toDelete[i]); From 2fbfe61f2c230884fed0c1042152e08b6caf0a53 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Fri, 5 Feb 2021 15:59:35 +0100 Subject: [PATCH 320/419] Restore comment Co-Authored-By: VNC <52937757+VNNCC@users.noreply.github.com> --- Source/Editor/Windows/ContentWindow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index a09bc1c6e..145c0d972 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -422,7 +422,7 @@ namespace FlaxEditor.Windows // TODO: just remove invalid locations from the history (those are removed) NavigationClearHistory(); - // Delete + // Delete items for (int i = 0; i < toDelete.Count; i++) Editor.ContentDatabase.Delete(toDelete[i]); From 78e4ba2f1711a5bc2f10735e0ca890fd08ee9b53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 21:11:35 +0100 Subject: [PATCH 321/419] Add support for UICanvas preview in Prefab Viewport Fixes #157 --- .../Editor/Viewport/Previews/PrefabPreview.cs | 48 ++++----- Source/Engine/UI/UICanvas.cs | 97 +++++++++++++++++-- 2 files changed, 116 insertions(+), 29 deletions(-) diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index aa04a41fd..066e98456 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -31,44 +31,34 @@ namespace FlaxEditor.Viewport.Previews if (_prefab == value) return; + // Unset and cleanup spawned instance if (_instance) { - // Unlink UI control - if (customControlLinked) - { - if (customControlLinked.Control?.Parent == this) - customControlLinked.Control.Parent = null; - customControlLinked = null; - } - - // Remove for preview - Task.RemoveCustomActor(_instance); - - // Delete - Object.Destroy(_instance); + var instance = _instance; + Instance = null; + Object.Destroy(instance); } _prefab = value; if (_prefab) { + // Load prefab _prefab.WaitForLoaded(); + // Spawn prefab var prevPreview = LoadingPreview; LoadingPreview = this; - - _instance = PrefabManager.SpawnPrefab(_prefab, null); - + var instance = PrefabManager.SpawnPrefab(_prefab, null); LoadingPreview = prevPreview; - - if (_instance == null) + if (instance == null) { _prefab = null; throw new FlaxException("Failed to spawn a prefab for the preview."); } - // Add to preview - Task.AddCustomActor(_instance); + // Set instance + Instance = instance; } } } @@ -94,7 +84,7 @@ namespace FlaxEditor.Viewport.Previews customControlLinked = null; } - // Remove for preview + // Remove for the preview Task.RemoveCustomActor(_instance); } @@ -102,12 +92,26 @@ namespace FlaxEditor.Viewport.Previews if (_instance) { - // Add to preview + // Add to the preview Task.AddCustomActor(_instance); + + // Link UI canvases to the preview + LinkCanvas(_instance); } } } + private void LinkCanvas(Actor actor) + { + if (actor is UICanvas uiCanvas) + uiCanvas.EditorOverride(Task, this); + var children = actor.ChildrenCount; + for (int i = 0; i < children; i++) + { + LinkCanvas(actor.GetChild(i)); + } + } + /// /// Initializes a new instance of the class. /// diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index e0b6425de..65956293b 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; using System.Globalization; using System.IO; using System.Text; @@ -279,16 +280,41 @@ namespace FlaxEngine else if (_renderMode == CanvasRenderMode.CameraSpace) { Matrix tmp1, tmp2; + Vector3 viewPos, viewUp, viewForward, pos; + Quaternion viewRot; // Use default camera is not specified var camera = RenderCamera ?? Camera.MainCamera; +#if FLAX_EDITOR + if (_editorTask) + { + // Use editor viewport task to override Camera Space placement + var view = _editorTask.View; + var frustum = view.Frustum; + if (!frustum.IsOrthographic) + _guiRoot.Size = new Vector2(frustum.GetWidthAtDepth(Distance), frustum.GetHeightAtDepth(Distance)); + else + _guiRoot.Size = _editorTask.Viewport.Size; + Matrix.Translation(_guiRoot.Width / -2.0f, _guiRoot.Height / -2.0f, 0, out world); + Matrix.RotationYawPitchRoll(Mathf.Pi, Mathf.Pi, 0, out tmp2); + Matrix.Multiply(ref world, ref tmp2, out tmp1); + viewPos = view.Position; + viewRot = view.Direction != Vector3.Up ? Quaternion.LookRotation(view.Direction, Vector3.Up) : Quaternion.LookRotation(view.Direction, Vector3.Right); + viewUp = Vector3.Up * viewRot; + viewForward = view.Direction; + pos = view.Position + view.Direction * Distance; + Matrix.Billboard(ref pos, ref viewPos, ref viewUp, ref viewForward, out tmp2); + Matrix.Multiply(ref tmp1, ref tmp2, out world); + return; + } +#endif + // Adjust GUI size to the viewport size at the given distance form the camera var viewport = camera.Viewport; if (camera.UsePerspective) { - Matrix tmp3; - camera.GetMatrices(out tmp1, out tmp3, ref viewport); + camera.GetMatrices(out tmp1, out var tmp3, ref viewport); Matrix.Multiply(ref tmp1, ref tmp3, out tmp2); var frustum = new BoundingFrustum(tmp2); _guiRoot.Size = new Vector2(frustum.GetWidthAtDepth(Distance), frustum.GetHeightAtDepth(Distance)); @@ -304,11 +330,11 @@ namespace FlaxEngine Matrix.Multiply(ref world, ref tmp2, out tmp1); // In front of the camera - var viewPos = camera.Position; - var viewRot = camera.Orientation; - var viewUp = Vector3.Up * viewRot; - var viewForward = Vector3.Forward * viewRot; - var pos = viewPos + viewForward * Distance; + viewPos = camera.Position; + viewRot = camera.Orientation; + viewUp = Vector3.Up * viewRot; + viewForward = Vector3.Forward * viewRot; + pos = viewPos + viewForward * Distance; Matrix.Billboard(ref pos, ref viewPos, ref viewUp, ref viewForward, out tmp2); Matrix.Multiply(ref tmp1, ref tmp2, out world); @@ -334,11 +360,18 @@ namespace FlaxEngine _guiRoot.Offsets = Margin.Zero; if (_renderer) { +#if FLAX_EDITOR + _editorTask?.CustomPostFx.Remove(_renderer); +#endif SceneRenderTask.GlobalCustomPostFx.Remove(_renderer); _renderer.Canvas = null; Destroy(_renderer); _renderer = null; } +#if FLAX_EDITOR + if (_editorRoot) + _guiRoot.Parent = _editorRoot; +#endif break; } case CanvasRenderMode.CameraSpace: @@ -346,12 +379,31 @@ namespace FlaxEngine { // Render canvas manually _guiRoot.AnchorPreset = AnchorPresets.TopLeft; +#if FLAX_EDITOR + if (_editorRoot != null && _guiRoot != null) + _guiRoot.Parent = null; +#endif if (_renderer == null) { _renderer = New(); _renderer.Canvas = this; if (IsActiveInHierarchy && Scene) + { +#if FLAX_EDITOR + if (_editorTask != null) + { + _editorTask.CustomPostFx.Add(_renderer); + break; + } +#endif SceneRenderTask.GlobalCustomPostFx.Add(_renderer); + } +#if FLAX_EDITOR + else if (_editorTask != null && IsActiveInHierarchy) + { + _editorTask.CustomPostFx.Add(_renderer); + } +#endif } break; } @@ -490,10 +542,21 @@ namespace FlaxEngine internal void OnEnable() { +#if FLAX_EDITOR + _guiRoot.Parent = _editorRoot ?? RootControl.CanvasRoot; +#else _guiRoot.Parent = RootControl.CanvasRoot; +#endif if (_renderer) { +#if FLAX_EDITOR + if (_editorTask != null) + { + _editorTask.CustomPostFx.Add(_renderer); + return; + } +#endif SceneRenderTask.GlobalCustomPostFx.Add(_renderer); } } @@ -518,5 +581,25 @@ namespace FlaxEngine _renderer = null; } } + +#if FLAX_EDITOR + private SceneRenderTask _editorTask; + private ContainerControl _editorRoot; + + internal void EditorOverride(SceneRenderTask task, ContainerControl root) + { + if (_editorTask != null && _renderer != null) + _editorTask.CustomPostFx.Remove(_renderer); + if (_editorRoot != null && _guiRoot != null) + _guiRoot.Parent = null; + + _editorTask = task; + _editorRoot = root; + Setup(); + + if (RenderMode == CanvasRenderMode.ScreenSpace && _editorRoot != null && _guiRoot != null) + _guiRoot.Parent = _editorRoot; + } +#endif } } From 43692f514ac0ad4153ab6401728055348f24c75b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 21:11:41 +0100 Subject: [PATCH 322/419] Fix --- Source/Engine/UI/UICanvas.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index 65956293b..426441493 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -369,7 +369,7 @@ namespace FlaxEngine _renderer = null; } #if FLAX_EDITOR - if (_editorRoot) + if (_editorRoot != null) _guiRoot.Parent = _editorRoot; #endif break; From 6b660c846b70c08112523169a518423258333633 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 21:12:14 +0100 Subject: [PATCH 323/419] Fix updating prefab object reference values after apply in prefab editor --- Source/Editor/Windows/Assets/PrefabWindow.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index a8b3e24a4..4cc7898ee 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -284,6 +284,9 @@ namespace FlaxEditor.Windows.Assets { // Simply update changes Editor.Prefabs.ApplyAll(_viewport.Instance); + + // Refresh properties panel to sync new prefab default values + _propertiesEditor.BuildLayout(); } catch (Exception) { From d27edbf5a901c0ddea1458dd0e788c335a9a9dc4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Feb 2021 21:18:21 +0100 Subject: [PATCH 324/419] Format code --- Source/Editor/Content/Tree/ContentTreeNode.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Content/Tree/ContentTreeNode.cs b/Source/Editor/Content/Tree/ContentTreeNode.cs index 07ce7f803..4370bc8e7 100644 --- a/Source/Editor/Content/Tree/ContentTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentTreeNode.cs @@ -295,7 +295,7 @@ namespace FlaxEditor.Content StartRenaming(); return true; case KeyboardKeys.Delete: - if(Folder.Exists) + if (Folder.Exists) Editor.Instance.Windows.ContentWin.Delete(Folder); return true; } @@ -304,7 +304,7 @@ namespace FlaxEditor.Content switch (key) { case KeyboardKeys.D: - if(Folder.Exists) + if (Folder.Exists) Editor.Instance.Windows.ContentWin.Duplicate(Folder); return true; } From 8a567f084950930dffde32fcb8de692471c4961c Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sat, 6 Feb 2021 11:29:48 +0100 Subject: [PATCH 325/419] Fix issue #214 --- Source/Engine/Debug/DebugDraw.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 457c90411..cdf1f412a 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -629,7 +629,11 @@ void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, const Color& color, float duration, bool depthTest) { - ASSERT(lines.Length() % 2 == 0); + if (lines.Length() % 2 == 0) + { + LOG(Error, "Cannot draw debug lines with uneven amount of items in array"); + return; + } // Create draw call entry DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; @@ -637,10 +641,12 @@ void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, c // Add lines const Vector3* p = lines.Get(); Array* list; - if (depthTest) + + if (depthTest) list = duration > 0 ? &DebugDrawDepthTest.DefaultLines : &DebugDrawDepthTest.OneFrameLines; - else + else list = duration > 0 ? &DebugDrawDefault.DefaultLines : &DebugDrawDefault.OneFrameLines; + list->EnsureCapacity(list->Count() + lines.Length()); for (int32 i = 0; i < lines.Length(); i += 2) { From 5b3275653654120f294defeb20f995d7d303f73e Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sat, 6 Feb 2021 11:32:58 +0100 Subject: [PATCH 326/419] Remove empty space Just noticed it, so bye bye. --- Source/Engine/Debug/DebugDraw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index cdf1f412a..f8b4a35ef 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -644,7 +644,7 @@ void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, c if (depthTest) list = duration > 0 ? &DebugDrawDepthTest.DefaultLines : &DebugDrawDepthTest.OneFrameLines; - else + else list = duration > 0 ? &DebugDrawDefault.DefaultLines : &DebugDrawDefault.OneFrameLines; list->EnsureCapacity(list->Count() + lines.Length()); From f5d1ad5a9b2737b6d7412e67c007a2d1d7146715 Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sat, 6 Feb 2021 13:32:04 +0100 Subject: [PATCH 327/419] Use ThrowException instead --- Source/Engine/Debug/DebugDraw.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index f8b4a35ef..c197a0d0d 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -19,6 +19,7 @@ #include "Engine/Graphics/RenderBuffers.h" #include "Engine/Animations/AnimationUtils.h" #include "Engine/Profiler/Profiler.h" +#include "Engine/Debug/DebugLog.h" // Debug draw service configuration #define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024) @@ -631,7 +632,7 @@ void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, c { if (lines.Length() % 2 == 0) { - LOG(Error, "Cannot draw debug lines with uneven amount of items in array"); + DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array"); return; } From 25f35b22be9e757dbf95e653ff76cf5b470ff021 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 6 Feb 2021 15:58:12 +0100 Subject: [PATCH 328/419] Add safety checks for particles data to prevent division by 0 --- ...rticleEmitterGraph.CPU.ParticleModules.cpp | 22 +++++++++---------- .../ParticleEmitterGraph.CPU.Particles.cpp | 2 +- ...rticleEmitterGraph.GPU.ParticleModules.cpp | 22 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp index bebe80479..7aa68e32e 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp @@ -69,7 +69,7 @@ namespace scale *= 1.72531f; } - return noise / weight; + return noise / Math::Max(weight, ZeroTolerance); } VariantType::Types GetVariantType(ParticleAttribute::ValueTypes type) @@ -486,7 +486,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* float particleDrag = drag; \ if (useSpriteSize) \ particleDrag *= ((Vector2*)spriteSizePtr)->MulValues(); \ - *((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * _deltaTime) / *(float*)massPtr); \ + *((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * _deltaTime) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ velocityPtr += stride; \ massPtr += stride; \ spriteSizePtr += stride @@ -545,7 +545,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* Vector3 vectorFieldUVW = Vector3::Transform(*((Vector3*)positionPtr), invFieldTransformMatrix); \ Vector3 force = Noise3D(vectorFieldUVW + 0.5f, octavesCount, roughness); \ force = Vector3::Transform(force, fieldTransformMatrix) * intensity; \ - *((Vector3*)velocityPtr) += force * (_deltaTime / *(float*)massPtr); \ + *((Vector3*)velocityPtr) += force * (_deltaTime / Math::Max(*(float*)massPtr, ZeroTolerance)); \ positionPtr += stride; \ velocityPtr += stride; \ massPtr += stride @@ -1009,7 +1009,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define INPUTS_FETCH() \ const Vector3 center = (Vector3)GetValue(centerBox, 2); \ - const float radius = (float)GetValue(radiusBox, 3); \ + const float radius = Math::Max((float)GetValue(radiusBox, 3), ZeroTolerance); \ const float thickness = (float)GetValue(thicknessBox, 4); \ const float arc = (float)GetValue(arcBox, 5) * DegreesToRadians #define LOGIC() \ @@ -1017,20 +1017,20 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* float sinTheta, cosTheta; \ Math::SinCos(u.X * TWO_PI, sinTheta, cosTheta); \ float r = Math::Saturate(thickness / radius); \ - Vector2 s1_1 = r * Vector2(cosTheta, sinTheta) + Vector2(1, 0); \ - Vector2 s1_2 = r * Vector2(-cosTheta, sinTheta) + Vector2(1, 0); \ - float w = s1_1.X / (s1_1.X + s1_2.X); \ + Vector2 s11 = r * Vector2(cosTheta, sinTheta) + Vector2(1, 0); \ + Vector2 s12 = r * Vector2(-cosTheta, sinTheta) + Vector2(1, 0); \ + float w = s11.X / (s11.X + s12.X); \ Vector3 t; \ float phi; \ if (u.Y < w) \ { \ phi = arc * u.Y / w; \ - t = Vector3(s1_1.X, 0, s1_1.Y); \ + t = Vector3(s11.X, 0, s11.Y); \ } \ else \ { \ phi = arc * (u.Y - w) / (1.0f - w); \ - t = Vector3(s1_2.X, 0, s1_2.Y); \ + t = Vector3(s12.X, 0, s12.Y); \ } \ float s, c; \ Math::SinCos(phi, c, s); \ @@ -1262,7 +1262,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if (sign * sqrLength <= sign * totalRadius * totalRadius) \ { \ float dist = Math::Sqrt(sqrLength); \ - Vector3 n = sign * dir / dist; \ + Vector3 n = sign * dir / Math::Max(dist, ZeroTolerance); \ *(Vector3*)positionPtr = position - n * (dist - totalRadius) * sign; \ COLLISION_LOGIC() @@ -1374,7 +1374,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* collision = Math::Abs(dir.Y) > halfHeight || sqrLength > cylinderRadiusT * cylinderRadiusT; \ if (collision) \ { \ - float dist = Math::Sqrt(sqrLength); \ + float dist = Math::Max(Math::Sqrt(sqrLength), ZeroTolerance); \ float distToCap = sign * (halfHeight - Math::Abs(dir.Y)); \ float distToSide = sign * (cylinderRadiusT - dist); \ Vector3 n = Vector3(dir.X / dist, Math::Sign(dir.Y), dir.Z / dist) * sign; \ diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index 08db1c0d3..d56bf6d5c 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -284,7 +284,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node { const float age = GET_PARTICLE_ATTRIBUTE(0, float); const float lifetime = GET_PARTICLE_ATTRIBUTE(1, float); - value = age / lifetime; + value = age / Math::Max(lifetime, ZeroTolerance); break; } // Effect Position diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp index 23dac1622..57d4ededc 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.ParticleModules.cpp @@ -177,7 +177,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " {{\n" " // Linear Drag\n" " float drag = {2} * {3}.x * {3}.y;\n" - " {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / {1});\n" + " {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / max({1}, PARTICLE_THRESHOLD));\n" " }}\n" ), velocity.Value, mass.Value, drag.Value, spriteSize.Value); } @@ -188,7 +188,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " {{\n" " // Linear Drag\n" " float drag = {2};\n" - " {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / {1});\n" + " {0} *= max(0.0f, 1.0f - (drag * DeltaTime) / max({1}, PARTICLE_THRESHOLD));\n" " }}\n" ), velocity.Value, mass.Value, drag.Value); } @@ -219,7 +219,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " float3 vectorFieldUVW = mul(invFieldTransformMatrix, float4({0}, 1.0f)).xyz;\n" " float3 force = Noise3D(vectorFieldUVW + 0.5f, {8}, {6});\n" " force = mul(fieldTransformMatrix, float4(force, 0.0f)).xyz * {7};\n" - " {1} += force * (DeltaTime / {2});\n" + " {1} += force * (DeltaTime / max({2}, PARTICLE_THRESHOLD));\n" " }}\n" ), position.Value, velocity.Value, mass.Value, fieldPosition.Value, fieldRotation.Value, fieldScale.Value, roughness.Value, intensity.Value, octavesCount.Value); break; @@ -486,21 +486,21 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " float3 u = RAND3;\n" " float sinTheta, cosTheta;\n" " sincos(u.x * PI * 2.0f, sinTheta, cosTheta);\n" - " float r = saturate((float){4} / {3});\n" - " float2 s1_1 = r * float2( cosTheta, sinTheta) + float2(1, 0);\n" - " float2 s1_2 = r * float2(-cosTheta, sinTheta) + float2(1, 0);\n" - " float w = s1_1.x / (s1_1.x + s1_2.x);\n" + " float r = saturate((float){4} / max({3}, PARTICLE_THRESHOLD));\n" + " float2 s11 = r * float2( cosTheta, sinTheta) + float2(1, 0);\n" + " float2 s12 = r * float2(-cosTheta, sinTheta) + float2(1, 0);\n" + " float w = s11.x / (s11.x + s12.x);\n" " float3 t;\n" " float phi;\n" " if (u.y < w)\n" " {{\n" " phi = radians({5}) * u.y / w;\n" - " t = float3(s1_1.x, 0, s1_1.y);\n" + " t = float3(s11.x, 0, s11.y);\n" " }}\n" " else\n" " {{\n" " phi = radians({5}) * (u.y - w) / (1.0f - w);\n" - " t = float3(s1_2.x, 0, s1_2.y);\n" + " t = float3(s12.x, 0, s12.y);\n" " }}\n" " float s, c;\n" " sincos(phi, c, s);\n" @@ -693,7 +693,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " if ({4} * sqrLength <= {4} * totalRadius * totalRadius)\n" " {{\n" " float dist = sqrt(sqrLength);\n" - " float3 n = {4} * dir / dist;\n" + " float3 n = {4} * dir / max(dist, PARTICLE_THRESHOLD);\n" " {0} -= n * (dist - totalRadius) * {4};\n" COLLISION_LOGIC() " }}\n" @@ -787,7 +787,7 @@ void ParticleEmitterGPUGenerator::ProcessModule(Node* node) " collision = abs(dir.y) > halfHeight || sqrLength > cylinderRadiusT * cylinderRadiusT;\n" " if (collision)\n" " {{\n" - " float dist = sqrt(sqrLength);\n" + " float dist = max(sqrt(sqrLength), PARTICLE_THRESHOLD);\n" " float distToCap = {4} * (halfHeight - abs(dir.y));\n" " float distToSide = {4} * (cylinderRadiusT - dist);\n" " float3 n = float3(dir.x / dist, sign(dir.y), dir.z / dist) * {4};\n" From 5cb0da3340ecd119a8a612bec306a35ddb1b039c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 6 Feb 2021 15:58:33 +0100 Subject: [PATCH 329/419] Move GPU particles generator code to be more compact --- .../ParticleEmitterGraph.GPU.Parameters.cpp | 98 -------------- .../ParticleEmitterGraph.GPU.Particles.cpp | 125 ++++++++++++++++++ .../GPU/ParticleEmitterGraph.GPU.Textures.cpp | 2 - .../GPU/ParticleEmitterGraph.GPU.Tools.cpp | 43 ------ 4 files changed, 125 insertions(+), 143 deletions(-) delete mode 100644 Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Parameters.cpp delete mode 100644 Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Tools.cpp diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Parameters.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Parameters.cpp deleted file mode 100644 index bcb672eb3..000000000 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Parameters.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#if COMPILE_WITH_PARTICLE_GPU_GRAPH - -#include "ParticleEmitterGraph.GPU.h" - -void ParticleEmitterGPUGenerator::ProcessGroupParameters(Box* box, Node* node, Value& value) -{ - switch (node->TypeID) - { - // Get - case 1: - case 2: - { - // Get parameter - const auto param = findParam((Guid)node->Values[0]); - if (param) - { - switch (param->Type) - { - case MaterialParameterType::Bool: - value = Value(VariantType::Bool, param->ShaderName); - break; - case MaterialParameterType::Integer: - case MaterialParameterType::SceneTexture: - value = Value(VariantType::Int, param->ShaderName); - break; - case MaterialParameterType::Float: - value = Value(VariantType::Float, param->ShaderName); - break; - case MaterialParameterType::Vector2: - case MaterialParameterType::Vector3: - case MaterialParameterType::Vector4: - case MaterialParameterType::Color: - { - // Set result values based on box ID - const Value sample(box->Type.Type, param->ShaderName); - switch (box->ID) - { - case 0: - value = sample; - break; - case 1: - value.Value = sample.Value + _subs[0]; - break; - case 2: - value.Value = sample.Value + _subs[1]; - break; - case 3: - value.Value = sample.Value + _subs[2]; - break; - case 4: - value.Value = sample.Value + _subs[3]; - break; - default: CRASH; - break; - } - value.Type = box->Type.Type; - break; - } - - case MaterialParameterType::Matrix: - { - value = Value(box->Type.Type, String::Format(TEXT("{0}[{1}]"), param->ShaderName, box->ID)); - break; - } - case MaterialParameterType::ChannelMask: - { - const auto input = tryGetValue(node->GetBox(0), Value::Zero); - value = writeLocal(VariantType::Float, String::Format(TEXT("dot({0}, {1})"), input.Value, param->ShaderName), node); - break; - } - case MaterialParameterType::CubeTexture: - case MaterialParameterType::Texture: - case MaterialParameterType::GPUTextureArray: - case MaterialParameterType::GPUTextureCube: - case MaterialParameterType::GPUTextureVolume: - case MaterialParameterType::GPUTexture: - value = Value(VariantType::Object, param->ShaderName); - break; - default: - CRASH; - break; - } - } - else - { - OnError(node, box, String::Format(TEXT("Missing graph parameter {0}."), node->Values[0])); - value = Value::Zero; - } - break; - } - default: - break; - } -} - -#endif diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp index 4839d0415..08e7b24ca 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp @@ -113,6 +113,131 @@ ParticleEmitterGPUGenerator::Value ParticleEmitterGPUGenerator::AccessParticleAt return value.Variable; } +void ParticleEmitterGPUGenerator::ProcessGroupParameters(Box* box, Node* node, Value& value) +{ + switch (node->TypeID) + { + // Get + case 1: + case 2: + { + // Get parameter + const auto param = findParam((Guid)node->Values[0]); + if (param) + { + switch (param->Type) + { + case MaterialParameterType::Bool: + value = Value(VariantType::Bool, param->ShaderName); + break; + case MaterialParameterType::Integer: + case MaterialParameterType::SceneTexture: + value = Value(VariantType::Int, param->ShaderName); + break; + case MaterialParameterType::Float: + value = Value(VariantType::Float, param->ShaderName); + break; + case MaterialParameterType::Vector2: + case MaterialParameterType::Vector3: + case MaterialParameterType::Vector4: + case MaterialParameterType::Color: + { + // Set result values based on box ID + const Value sample(box->Type.Type, param->ShaderName); + switch (box->ID) + { + case 0: + value = sample; + break; + case 1: + value.Value = sample.Value + _subs[0]; + break; + case 2: + value.Value = sample.Value + _subs[1]; + break; + case 3: + value.Value = sample.Value + _subs[2]; + break; + case 4: + value.Value = sample.Value + _subs[3]; + break; + default: CRASH; + break; + } + value.Type = box->Type.Type; + break; + } + + case MaterialParameterType::Matrix: + { + value = Value(box->Type.Type, String::Format(TEXT("{0}[{1}]"), param->ShaderName, box->ID)); + break; + } + case MaterialParameterType::ChannelMask: + { + const auto input = tryGetValue(node->GetBox(0), Value::Zero); + value = writeLocal(VariantType::Float, String::Format(TEXT("dot({0}, {1})"), input.Value, param->ShaderName), node); + break; + } + case MaterialParameterType::CubeTexture: + case MaterialParameterType::Texture: + case MaterialParameterType::GPUTextureArray: + case MaterialParameterType::GPUTextureCube: + case MaterialParameterType::GPUTextureVolume: + case MaterialParameterType::GPUTexture: + value = Value(VariantType::Object, param->ShaderName); + break; + default: + CRASH; + break; + } + } + else + { + OnError(node, box, String::Format(TEXT("Missing graph parameter {0}."), node->Values[0])); + value = Value::Zero; + } + break; + } + default: + break; + } +} + +void ParticleEmitterGPUGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) +{ + switch (node->TypeID) + { + // Linearize Depth + case 7: + { + // Get input + const Value depth = tryGetValue(node->GetBox(0), Value::Zero).AsFloat(); + + // Linearize raw device depth + linearizeSceneDepth(node, depth, value); + break; + } + // Time + case 8: + value = box->ID == 0 ? Value(VariantType::Float, TEXT("Time")) : Value(VariantType::Float, TEXT("DeltaTime")); + break; + // Transform Position To Screen UV + case 9: + { + const Value position = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); + const Value projPos = writeLocal(VariantType::Vector4, String::Format(TEXT("mul(float4({0}, 1.0f), ViewProjectionMatrix)"), position.Value), node); + _writer.Write(TEXT("\t{0}.xy /= {0}.w;\n"), projPos.Value); + _writer.Write(TEXT("\t{0}.xy = {0}.xy * 0.5f + 0.5f;\n"), projPos.Value); + value = Value(VariantType::Vector2, projPos.Value + TEXT(".xy")); + break; + } + default: + ShaderGenerator::ProcessGroupTools(box, node, value); + break; + } +} + void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Value& value) { switch (node->TypeID) diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Textures.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Textures.cpp index 61273b3a1..e1d864b4e 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Textures.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Textures.cpp @@ -256,10 +256,8 @@ void ParticleEmitterGPUGenerator::ProcessGroupTextures(Box* box, Node* node, Val } // Scene Depth case 8: - { sampleSceneDepth(node, value, box); break; - } // Texture case 11: { diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Tools.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Tools.cpp deleted file mode 100644 index 299e1a422..000000000 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Tools.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#if COMPILE_WITH_PARTICLE_GPU_GRAPH - -#include "ParticleEmitterGraph.GPU.h" - -void ParticleEmitterGPUGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) -{ - switch (node->TypeID) - { - // Linearize Depth - case 7: - { - // Get input - const Value depth = tryGetValue(node->GetBox(0), Value::Zero).AsFloat(); - - // Linearize raw device depth - linearizeSceneDepth(node, depth, value); - break; - } - // Time - case 8: - { - value = box->ID == 0 ? Value(VariantType::Float, TEXT("Time")) : Value(VariantType::Float, TEXT("DeltaTime")); - break; - } - // Transform Position To Screen UV - case 9: - { - const Value position = tryGetValue(node->GetBox(0), Value::Zero).AsVector3(); - const Value projPos = writeLocal(VariantType::Vector4, String::Format(TEXT("mul(float4({0}, 1.0f), ViewProjectionMatrix)"), position.Value), node); - _writer.Write(TEXT("\t{0}.xy /= {0}.w;\n"), projPos.Value); - _writer.Write(TEXT("\t{0}.xy = {0}.xy * 0.5f + 0.5f;\n"), projPos.Value); - value = Value(VariantType::Vector2, projPos.Value + TEXT(".xy")); - break; - } - default: - ShaderGenerator::ProcessGroupTools(box, node, value); - break; - } -} - -#endif From 633357cc9bc493233abdf13a64aef0b5a6b3697a Mon Sep 17 00:00:00 2001 From: intolerantape Date: Sat, 6 Feb 2021 09:52:30 -0800 Subject: [PATCH 330/419] Added FLAXENGINE_API macro to class JsonAsset. --- Source/Engine/Content/JsonAsset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h index ce617938d..c74c4a045 100644 --- a/Source/Engine/Content/JsonAsset.h +++ b/Source/Engine/Content/JsonAsset.h @@ -75,7 +75,7 @@ protected: /// Generic type of Json-format asset. It provides the managed representation of this resource data so it can be accessed via C# API. /// /// -API_CLASS(NoSpawn) class JsonAsset : public JsonAssetBase +API_CLASS(NoSpawn) class FLAXENGINE_API JsonAsset : public JsonAssetBase { DECLARE_ASSET_HEADER(JsonAsset); From 522e1eb76974e3d04d516a68db7ecad6b588dc8a Mon Sep 17 00:00:00 2001 From: "W2.Wizard" Date: Sat, 6 Feb 2021 20:10:22 +0100 Subject: [PATCH 331/419] Inverted condition --- Source/Engine/Debug/DebugDraw.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index c197a0d0d..f954cd7cb 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -630,7 +630,7 @@ void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, const Color& color, float duration, bool depthTest) { - if (lines.Length() % 2 == 0) + if (lines.Length() % 2 != 0) { DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array"); return; From 6e5a13111a0b5c81fba2743e84b0e5f6db07b9b5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 12:49:14 +0100 Subject: [PATCH 332/419] Fix auto-importing materials and textures from model file with invalid path characters used in name Fixes #208 --- Source/Editor/Utilities/EditorUtilities.cpp | 53 +++++++++++++++++++ Source/Editor/Utilities/EditorUtilities.h | 7 +++ .../AssetsImportingManager.cpp | 2 +- Source/Engine/Tools/ModelTool/ModelTool.cpp | 13 ++++- 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Utilities/EditorUtilities.cpp b/Source/Editor/Utilities/EditorUtilities.cpp index fc266ec13..538b78173 100644 --- a/Source/Editor/Utilities/EditorUtilities.cpp +++ b/Source/Editor/Utilities/EditorUtilities.cpp @@ -741,6 +741,59 @@ bool EditorUtilities::GenerateCertificate(const String& name, const String& outp return false; } +bool EditorUtilities::IsInvalidPathChar(Char c) +{ + char illegalChars[] = + { + '?', + '\\', + '/', + '\"', + '<', + '>', + '|', + ':', + '*', + '\u0001', + '\u0002', + '\u0003', + '\u0004', + '\u0005', + '\u0006', + '\a', + '\b', + '\t', + '\n', + '\v', + '\f', + '\r', + '\u000E', + '\u000F', + '\u0010', + '\u0011', + '\u0012', + '\u0013', + '\u0014', + '\u0015', + '\u0016', + '\u0017', + '\u0018', + '\u0019', + '\u001A', + '\u001B', + '\u001C', + '\u001D', + '\u001E', + '\u001F' + }; + for (auto i : illegalChars) + { + if (c == i) + return true; + } + return false; +} + bool EditorUtilities::ReplaceInFiles(const String& folderPath, const Char* searchPattern, DirectorySearchOption searchOption, const String& findWhat, const String& replaceWith) { Array files; diff --git a/Source/Editor/Utilities/EditorUtilities.h b/Source/Editor/Utilities/EditorUtilities.h index 236d10e6f..a5f129c1b 100644 --- a/Source/Editor/Utilities/EditorUtilities.h +++ b/Source/Editor/Utilities/EditorUtilities.h @@ -42,6 +42,13 @@ public: public: + /// + /// Determines whether the specified path character is invalid. + /// + /// The path character. + /// true if the given character cannot be used as a path because it is illegal character; otherwise, false. + static bool IsInvalidPathChar(Char c); + /// /// Replaces the given text with other one in the files. /// diff --git a/Source/Engine/ContentImporters/AssetsImportingManager.cpp b/Source/Engine/ContentImporters/AssetsImportingManager.cpp index 3a39bd64b..f1d048ca5 100644 --- a/Source/Engine/ContentImporters/AssetsImportingManager.cpp +++ b/Source/Engine/ContentImporters/AssetsImportingManager.cpp @@ -178,7 +178,7 @@ void CreateAssetContext::ApplyChanges() // Move file if (FileSystem::MoveFile(TargetAssetPath, OutputPath, true)) { - LOG(Warning, "Cannot move imported file to the destination path."); + LOG(Warning, "Cannot move imported file {0} to the destination path {1}.", OutputPath, TargetAssetPath); _applyChangesResult = CreateAssetResult::CannotSaveFile; return; } diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 35cb3860b..05a69141f 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -15,7 +15,8 @@ #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/ContentImporters/AssetsImportingManager.h" #include "Engine/ContentImporters/CreateMaterial.h" -#include "ThirdParty/meshoptimizer/meshoptimizer.h" +#include "Editor/Utilities/EditorUtilities.h" +#include void RemoveNamespace(String& name) { @@ -486,6 +487,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Textures) == 0 || texture.FilePath.IsEmpty()) continue; auto filename = StringUtils::GetFileNameWithoutExtension(texture.FilePath); + for (int32 j = filename.Length() - 1; j >= 0; j--) + { + if (EditorUtilities::IsInvalidPathChar(filename[j])) + filename[j] = ' '; + } if (importedFileNames.Contains(filename)) { int32 counter = 1; @@ -526,6 +532,11 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options opt if (autoImportOutput.IsEmpty() || (data.Types & ImportDataTypes::Materials) == 0 || !material.UsesProperties()) continue; auto filename = material.Name; + for (int32 j = filename.Length() - 1; j >= 0; j--) + { + if (EditorUtilities::IsInvalidPathChar(filename[j])) + filename[j] = ' '; + } if (importedFileNames.Contains(filename)) { int32 counter = 1; From 0242e29873ee5917c9015bbb9cf5830062cfbeee Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 13:06:33 +0100 Subject: [PATCH 333/419] Fix painting foliage on inactive objects Fixes #209 --- Source/Editor/Tools/Foliage/FoliageTools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Tools/Foliage/FoliageTools.cpp b/Source/Editor/Tools/Foliage/FoliageTools.cpp index a0cbf8e14..3354be0b3 100644 --- a/Source/Editor/Tools/Foliage/FoliageTools.cpp +++ b/Source/Editor/Tools/Foliage/FoliageTools.cpp @@ -67,7 +67,7 @@ struct GeometryLookup static bool Search(Actor* actor, GeometryLookup& lookup) { // Early out if object is not intersecting with the foliage brush bounds - if (!actor->GetBox().Intersects(lookup.Brush)) + if (!actor->GetIsActive() || !actor->GetBox().Intersects(lookup.Brush)) return true; const auto brush = lookup.Brush; From 08abc798cc2339fa7b9e312a55600513a239615f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 16:31:30 +0100 Subject: [PATCH 334/419] Fix FindActor and FindScript in Level --- Source/Engine/Level/Level.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index b1ee090f4..0a10b58d2 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -355,7 +355,7 @@ public: /// /// Actor instance if found, null otherwise. template - FORCE_INLINE T* FindActor() const + FORCE_INLINE static T* FindActor() { return (T*)FindActor(T::GetStaticClass()); } @@ -372,7 +372,7 @@ public: /// /// Script instance if found, null otherwise. template - FORCE_INLINE T* FindScript() const + FORCE_INLINE static T* FindScript() { return (T*)FindScript(T::GetStaticClass()); } From ca6afc0c2d4406c5a0146f0e648c8011b67f89de Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 16:49:44 +0100 Subject: [PATCH 335/419] Fix HorizontalPanel and VerticalPanel auto-sizing if child control is using anchors Fixes #203 --- Source/Engine/UI/GUI/Panels/HorizontalPanel.cs | 2 +- Source/Engine/UI/GUI/Panels/VerticalPanel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs index 53e435f91..d7b54a835 100644 --- a/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs +++ b/Source/Engine/UI/GUI/Panels/HorizontalPanel.cs @@ -42,7 +42,7 @@ namespace FlaxEngine.GUI for (int i = 0; i < _children.Count; i++) { Control c = _children[i]; - if (c.Visible) + if (c.Visible && Mathf.IsZero(c.AnchorMax.X)) { var w = c.Width; c.Bounds = new Rectangle(x + _offset.X, _margin.Top + _offset.Y, w, h); diff --git a/Source/Engine/UI/GUI/Panels/VerticalPanel.cs b/Source/Engine/UI/GUI/Panels/VerticalPanel.cs index f811755ef..2c821bd3f 100644 --- a/Source/Engine/UI/GUI/Panels/VerticalPanel.cs +++ b/Source/Engine/UI/GUI/Panels/VerticalPanel.cs @@ -42,7 +42,7 @@ namespace FlaxEngine.GUI for (int i = 0; i < _children.Count; i++) { Control c = _children[i]; - if (c.Visible) + if (c.Visible && Mathf.IsZero(c.AnchorMax.Y)) { var h = c.Height; c.Bounds = new Rectangle(_margin.Left + _offset.X, y + _offset.Y, w, h); From 6ea897b0f5aa4ebc94d65641a26433fdd6763f18 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 19:27:54 +0100 Subject: [PATCH 336/419] Update Mono for Windows --- Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb | 4 ++-- Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libmono-static.lib | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libmonoutils.pdb | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb index a79f59aac..1cb5d5f43 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ff34e923a27392a70eb465df1ecc03372aadac6f2753832cce3d82cd5ae1b75 -size 159744 +oid sha256:86d71f01f6ba35e763467eb5b2be703831065ae7c9438fde4a32e1cd40d68880 +size 151552 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb index 1a80321ba..2c08c3457 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8bd411ac3aa425db683905a5fa2445d7d770e5d04821757fcdcaa42e0d0ac986 -size 348160 +oid sha256:e7a6731228669866ebfb450bdd14bdb6e54eef8dcc10c36640016630ded6ef73 +size 290816 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb index 7c24a2502..a06e2ddf2 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:184d1b18bb84c35fa43d878cedb60f53f203207208882ce6518a2d7a35308332 -size 864256 +oid sha256:6e12dfaf49963f4b736d9fbd7bbe16ccbf79045d64df71edc5209ffb25e0d1e0 +size 667648 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib index 366a28594..ab2d5015d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f633de4fbae8e4d65aec49eaaa06f37745bfc28328f4193020434dab398235cc -size 28245086 +oid sha256:b90fdbcfab9d89be16789604edbab46ee8ed0159846be60714ef27ac4444baf2 +size 28391654 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb index c60e7dbe4..e2e07cc4a 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:188ebd527b432f33f8bfccb4fbada27b029f362918ab5c4b6f82584f9d30b6f3 -size 1249280 +oid sha256:db47e63ec05d2c50d661f89c062931787be1722e0387f0076b910a9092fbe962 +size 995328 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoutils.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoutils.pdb index 30be1562b..87d823d36 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoutils.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoutils.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:289966b7d9ff6255e38a6e45d2b9a5179c42e0f1add6abb70f7fa5501dfc59bc -size 356352 +oid sha256:3846908f7e00e2ef725c4c770dc69c277491c7e83df6047871fc9cd672d47a6d +size 299008 From 9f3be80f9c10e33ebb96d6575f494d420456ca20 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 19:28:23 +0100 Subject: [PATCH 337/419] Fix VS debugger config for Dictionary and HashSet to show only Occupied buckets --- Source/flax.natvis | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/flax.natvis b/Source/flax.natvis index fb3ef1228..15ccaa7e6 100644 --- a/Source/flax.natvis +++ b/Source/flax.natvis @@ -52,7 +52,7 @@ _elementsCount - + _table[i] i++ @@ -72,7 +72,7 @@ _elementsCount - + _table[i] i++ From 8c1f56b7db86fc7c6aa98cd3b39d1d31cb19f91a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 19:28:57 +0100 Subject: [PATCH 338/419] Fixes for scripting objects --- Source/Engine/Scripting/Scripting.cpp | 95 +++++++++++++++++---- Source/Engine/Scripting/ScriptingObject.cpp | 11 ++- 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 6f32f8fd1..ee2922754 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -56,7 +56,45 @@ namespace MDomain* _monoRootDomain = nullptr; MDomain* _monoScriptsDomain = nullptr; CriticalSection _objectsLocker; +#define USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING 0 +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + struct ScriptingObjectData + { + ScriptingObject* Ptr; + StringAnsi TypeName; + + ScriptingObjectData() + { + Ptr = nullptr; + } + + ScriptingObjectData(ScriptingObject* ptr) + { + Ptr = ptr; + if (ptr && ptr->GetTypeHandle() && ptr->GetTypeHandle().TypeIndex < ptr->GetTypeHandle().Module->Types.Count()) + TypeName = ptr->GetType().Fullname; + } + + ScriptingObject* operator->() const + { + return Ptr; + } + + explicit operator ScriptingObject*() + { + return Ptr; + } + + operator ScriptingObject*() const + { + return Ptr; + } + }; + + Dictionary _objectsDictionary(1024 * 16); +#else Dictionary _objectsDictionary(1024 * 16); +#endif bool _isEngineAssemblyLoaded = false; bool _hasGameModulesLoaded = false; MMethod* _method_Update = nullptr; @@ -456,6 +494,9 @@ void Scripting::Release() for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i) { auto obj = i->Value; +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint32)obj.Ptr, String(obj.TypeName)); +#endif obj->OnScriptingDispose(); } } @@ -498,7 +539,9 @@ void Scripting::Release() } } +#if !USE_SINGLE_DOMAIN MCore::Instance()->UnloadDomain("Scripts Domain"); +#endif } #if USE_EDITOR @@ -683,10 +726,18 @@ ScriptingObject* Scripting::FindObject(Guid id, MClass* type) } // Try to find it +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + ScriptingObjectData data; + _objectsLocker.Lock(); + _objectsDictionary.TryGet(id, data); + _objectsLocker.Unlock(); + auto result = data.Ptr; +#else ScriptingObject* result = nullptr; _objectsLocker.Lock(); _objectsDictionary.TryGet(id, result); _objectsLocker.Unlock(); +#endif if (result) { // Check type @@ -718,11 +769,20 @@ ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type) } // Try to find it +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + ScriptingObjectData data; + _objectsLocker.Lock(); + _objectsDictionary.TryGet(id, data); + _objectsLocker.Unlock(); + auto result = data.Ptr; +#else ScriptingObject* result = nullptr; _objectsLocker.Lock(); _objectsDictionary.TryGet(id, result); _objectsLocker.Unlock(); +#endif + // Check type if (result && !result->Is(type)) { result = nullptr; @@ -753,28 +813,23 @@ ScriptingObject* Scripting::FindObject(const MonoObject* managedInstance) void Scripting::OnManagedInstanceDeleted(ScriptingObject* obj) { + PROFILE_CPU_NAMED("OnManagedInstanceDeleted"); ASSERT(obj); - PROFILE_CPU_NAMED("OnManagedInstanceDeleted"); - - // This is sometimes crashing, probably rawPtr field is not cleared in some cases - // TODO: use faster callback without crashing - //obj->OnManagedInstanceDeleted(); - // Validate if object still exists - bool isValid; - { - ScopeLock lock(_objectsLocker); - isValid = _objectsDictionary.ContainsValue(obj); - } - if (isValid) + _objectsLocker.Lock(); + if (_objectsDictionary.ContainsValue(obj)) { +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[OnManagedInstanceDeleted] obj = 0x{0:x}, {1}", (uint32)obj, String(ScriptingObjectData(obj).TypeName)); +#endif obj->OnManagedInstanceDeleted(); } else { //LOG(Warning, "Object finalization called for already removed object (address={0:x})", (uint64)obj); } + _objectsLocker.Unlock(); } bool Scripting::HasGameModulesLoaded() @@ -805,18 +860,25 @@ void Scripting::RegisterObject(ScriptingObject* obj) //ASSERT(!_objectsDictionary.ContainsValue(obj)); #if ENABLE_ASSERTION - ScriptingObject* other = nullptr; +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + ScriptingObjectData other; if (_objectsDictionary.TryGet(obj->GetID(), other)) +#else + ScriptingObject* other; + if (_objectsDictionary.TryGet(obj->GetID(), other)) +#endif { // Something went wrong... - LOG(Error, "Objects registry already contains object with ID={0}! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName())); - + LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName())); _objectsDictionary.Remove(obj->GetID()); } #else ASSERT(!_objectsDictionary.ContainsKey(obj->_id)); #endif +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[RegisterObject] obj = 0x{0:x}, {1}", (uint32)obj, String(ScriptingObjectData(obj).TypeName)); +#endif _objectsDictionary.Add(obj->GetID(), obj); } @@ -826,6 +888,9 @@ void Scripting::UnregisterObject(ScriptingObject* obj) //ASSERT(!obj->_id.IsValid() || _objectsDictionary.ContainsValue(obj)); +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[UnregisterObject] obj = 0x{0:x}, {1}", (uint32)obj, String(ScriptingObjectData(obj).TypeName)); +#endif _objectsDictionary.Remove(obj->GetID()); } diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 11d6e43e0..9d2320d7f 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -276,6 +276,10 @@ void PersistentScriptingObject::OnManagedInstanceDeleted() _gcHandle = 0; } + // Unregister object + if (IsRegistered()) + UnregisterObject(); + // But do not delete itself } @@ -501,11 +505,6 @@ public: obj->RegisterObject(); } - static void ManagedInstanceDeleted(ScriptingObject* obj) - { - Scripting::OnManagedInstanceDeleted(obj); - } - static void Destroy(ManagedScriptingObject* obj, float timeLeft) { // Use scaled game time for removing actors/scripts by the user (maybe expose it to the api?) @@ -544,7 +543,7 @@ public: ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create1", &Create1); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Create2", &Create2); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceCreated", &ManagedInstanceCreated); - ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &ManagedInstanceDeleted); + ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_ManagedInstanceDeleted", &Scripting::OnManagedInstanceDeleted); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_Destroy", &Destroy); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_GetTypeName", &GetTypeName); ADD_INTERNAL_CALL("FlaxEngine.Object::Internal_FindObject", &FindObject); From 5768eefe49c7332f014749d132d50f7c847be770 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 20:06:44 +0100 Subject: [PATCH 339/419] Optimize `Texture::DownloadData` for staging textures --- .../Engine/Graphics/Textures/GPUTexture.cpp | 97 +++++++++++-------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/Source/Engine/Graphics/Textures/GPUTexture.cpp b/Source/Engine/Graphics/Textures/GPUTexture.cpp index e51d2e510..29268e721 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.cpp +++ b/Source/Engine/Graphics/Textures/GPUTexture.cpp @@ -458,43 +458,13 @@ protected: // [ThreadPoolTask] bool Run() override { - // Check resources auto texture = _texture.Get(); if (texture == nullptr || _staging == nullptr || _data == nullptr) { LOG(Warning, "Cannot download texture data. Missing objects."); return true; } - - const auto arraySize = texture->ArraySize(); - const auto mipLevels = texture->MipLevels(); - - // Get all mip maps for each array slice - auto& rawResultData = _data->Items; - rawResultData.Resize(arraySize, false); - for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) - { - auto& arraySlice = rawResultData[arrayIndex]; - arraySlice.Mips.Resize(mipLevels); - - for (int32 mipMapIndex = 0; mipMapIndex < mipLevels; mipMapIndex++) - { - auto& mip = arraySlice.Mips[mipMapIndex]; - const int32 mipWidth = _data->Width >> mipMapIndex; - const int32 mipHeight = _data->Height >> mipMapIndex; - uint32 mipRowPitch, mipSlicePitch; - RenderTools::ComputePitch(_data->Format, mipWidth, mipHeight, mipRowPitch, mipSlicePitch); - - // Gather data - if (_staging->GetData(arrayIndex, mipMapIndex, mip, mipRowPitch)) - { - LOG(Warning, "Staging resource of \'{0}\' get data failed.", texture->ToString()); - return true; - } - } - } - - return false; + return _staging->DownloadData(*_data); } void OnEnd() override @@ -508,6 +478,57 @@ protected: bool GPUTexture::DownloadData(TextureData& result) { + // Skip for empty ones + if (MipLevels() == 0) + { + LOG(Warning, "Cannot download GPU texture data from an empty texture."); + return true; + } + if (Depth() != 1) + { + MISSING_CODE("support volume texture data downloading."); + } + + // Use faster path for staging resources + if (IsStaging()) + { + const auto arraySize = ArraySize(); + const auto mipLevels = MipLevels(); + + // Set texture info + result.Width = Width(); + result.Height = Height(); + result.Depth = Depth(); + result.Format = Format(); + + // Get all mip maps for each array slice + auto& rawResultData = result.Items; + rawResultData.Resize(arraySize, false); + for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) + { + auto& arraySlice = rawResultData[arrayIndex]; + arraySlice.Mips.Resize(mipLevels); + + for (int32 mipMapIndex = 0; mipMapIndex < mipLevels; mipMapIndex++) + { + auto& mip = arraySlice.Mips[mipMapIndex]; + const int32 mipWidth = result.Width >> mipMapIndex; + const int32 mipHeight = result.Height >> mipMapIndex; + uint32 mipRowPitch, mipSlicePitch; + RenderTools::ComputePitch(result.Format, mipWidth, mipHeight, mipRowPitch, mipSlicePitch); + + // Gather data + if (GetData(arrayIndex, mipMapIndex, mip, mipRowPitch)) + { + LOG(Warning, "Staging resource of \'{0}\' get data failed.", ToString()); + return true; + } + } + } + + return false; + } + const auto name = ToString(); // Ensure not running on main thread - we support DownloadData from textures only on a worker threads (Thread Pool Workers or Content Loaders) @@ -538,7 +559,8 @@ bool GPUTexture::DownloadData(TextureData& result) Task* GPUTexture::DownloadDataAsync(TextureData& result) { - if (!IsAllocated()) + // Skip for empty ones + if (MipLevels() == 0) { LOG(Warning, "Cannot download texture data. It has not ben created yet."); return nullptr; @@ -548,19 +570,12 @@ Task* GPUTexture::DownloadDataAsync(TextureData& result) MISSING_CODE("support volume texture data downloading."); } - // Set texture info - result.Width = Width(); - result.Height = Height(); - result.Depth = Depth(); - result.Format = Format(); - - // Quicker path if texture is already readback - if (_desc.Usage == GPUResourceUsage::StagingReadback) + // Use faster path for staging resources + if (IsStaging()) { // Create task to copy downloaded data to TextureData container auto getDataTask = ::New(this, this, result); ASSERT(getDataTask->HasReference(this)); - return getDataTask; } From 04bb83fe31aa2155c5b26538bac359bca98f7498 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 20:19:10 +0100 Subject: [PATCH 340/419] Fixes --- Source/Editor/Content/Proxy/PrefabProxy.cs | 4 ++-- Source/Engine/UI/UICanvas.cpp | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Source/Editor/Content/Proxy/PrefabProxy.cs b/Source/Editor/Content/Proxy/PrefabProxy.cs index 39d77f25c..276bc0d30 100644 --- a/Source/Editor/Content/Proxy/PrefabProxy.cs +++ b/Source/Editor/Content/Proxy/PrefabProxy.cs @@ -162,8 +162,7 @@ namespace FlaxEditor.Content // Auto fit actor to camera float targetSize = 30.0f; - BoundingBox bounds; - Editor.GetActorEditorBox(_preview.Instance, out bounds); + Editor.GetActorEditorBox(_preview.Instance, out var bounds); float maxSize = Mathf.Max(0.001f, bounds.Size.MaxValue); _preview.Instance.Scale = new Vector3(targetSize / maxSize); _preview.Instance.Position = Vector3.Zero; @@ -175,6 +174,7 @@ namespace FlaxEditor.Content /// public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot) { + _preview.RemoveChildren(); _preview.Prefab = null; _preview.Parent = null; } diff --git a/Source/Engine/UI/UICanvas.cpp b/Source/Engine/UI/UICanvas.cpp index 67dbaaa5e..49d6ac420 100644 --- a/Source/Engine/UI/UICanvas.cpp +++ b/Source/Engine/UI/UICanvas.cpp @@ -17,13 +17,17 @@ MMethod* UICanvas_OnDisable = nullptr; MMethod* UICanvas_EndPlay = nullptr; #define UICANVAS_INVOKE(event) \ - MonoObject* exception = nullptr; \ - UICanvas_##event->Invoke(GetManagedInstance(), nullptr, &exception); \ - if (exception) \ - { \ - MException ex(exception); \ - ex.Log(LogType::Error, TEXT("UICanvas::" #event)); \ - } + auto instance = GetManagedInstance(); \ + if (instance) \ + { \ + MonoObject* exception = nullptr; \ + UICanvas_##event->Invoke(instance, nullptr, &exception); \ + if (exception) \ + { \ + MException ex(exception); \ + ex.Log(LogType::Error, TEXT("UICanvas::" #event)); \ + } \ + } UICanvas::UICanvas(const SpawnParams& params) : Actor(params) From 96f1d9e82061f05028a161592d7231acf0671e59 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 20:19:21 +0100 Subject: [PATCH 341/419] Add UIControl outlines drawing in Prefab window --- Source/Editor/Viewport/PrefabWindowViewport.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 94b994ac8..dd6f8fce3 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -485,6 +485,23 @@ namespace FlaxEditor.Viewport } } + /// + public override void Draw() + { + base.Draw(); + + // Selected UI controls outline + for (var i = 0; i < _window.Selection.Count; i++) + { + if (_window.Selection[i].EditableObject is UIControl controlActor && controlActor.Control != null) + { + var control = controlActor.Control; + var bounds = Rectangle.FromPoints(control.PointToParent(this, Vector2.Zero), control.PointToParent(this, control.Size)); + Render2D.DrawRectangle(bounds, Editor.Instance.Options.Options.Visual.SelectionOutlineColor0, Editor.Instance.Options.Options.Visual.UISelectionOutlineSize); + } + } + } + /// protected override void OnLeftMouseButtonUp() { From 2325de3ddcd18e956ad4ceb0d02d4ccd64c5ac8a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 21:11:04 +0100 Subject: [PATCH 342/419] Fix missing terrain bounds update after modifying terrain Fixes #5 --- Source/Engine/Terrain/Terrain.cpp | 2 ++ Source/Engine/Terrain/TerrainChunk.cpp | 2 +- Source/Engine/Terrain/TerrainPatch.cpp | 7 +++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index 38d1865bb..3b00818c2 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -36,6 +36,7 @@ Terrain::~Terrain() void Terrain::UpdateBounds() { + PROFILE_CPU(); _box = BoundingBox(_transform.Translation, _transform.Translation); for (int32 i = 0; i < _patches.Count(); i++) { @@ -48,6 +49,7 @@ void Terrain::UpdateBounds() void Terrain::CacheNeighbors() { + PROFILE_CPU(); for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++) { const auto patch = _patches[pathIndex]; diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index 1241935e5..fbb80c27d 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -192,7 +192,7 @@ bool TerrainChunk::Intersects(const Ray& ray, float& distance) void TerrainChunk::UpdateBounds() { const Vector3 boundsExtent = _patch->_terrain->_boundsExtent; - const float size = _patch->_terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX; + const float size = (float)_patch->_terrain->_chunkSize * TERRAIN_UNITS_PER_VERTEX; Transform terrainTransform = _patch->_terrain->_transform; Transform localTransform; diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 791e93c7f..552238e9e 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -951,7 +951,7 @@ bool TerrainPatch::SetupHeightMap(int32 heightMapLength, const float* heightMap, chunk._yHeight = chunkHeights[chunkIndex]; chunk.UpdateTransform(); } - UpdateBounds(); + _terrain->UpdateBounds(); UpdateCollision(); #if TERRAIN_UPDATING @@ -1431,7 +1431,7 @@ bool TerrainPatch::ModifyHeightMap(const float* samples, const Int2& modifiedOff chunk._yHeight = chunkHeights[chunkIndex]; chunk.UpdateTransform(); } - UpdateBounds(); + _terrain->UpdateBounds(); return UpdateHeightData(info, modifiedOffset, modifiedSize, wasHeightRangeChanged); } @@ -2108,9 +2108,8 @@ void TerrainPatch::UpdatePostManualDeserialization() { auto& chunk = Chunks[chunkIndex]; chunk.UpdateTransform(); - chunk.UpdateBounds(); } - UpdateBounds(); + _terrain->UpdateBounds(); ScopeLock lock(_collisionLocker); From 103d630d80009bcf59167bcb9163d532032572c7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 7 Feb 2021 21:41:17 +0100 Subject: [PATCH 343/419] Fix tooltips generation for native properties to reflect getter and setter docsa --- Source/Engine/Level/Actor.h | 8 ++++---- .../Bindings/BindingsGenerator.CSharp.cs | 20 +++---------------- .../Bindings/BindingsGenerator.Parsing.cs | 7 +++++++ 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index 334021c28..7621e4a2a 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -437,13 +437,13 @@ public: } /// - /// Sets actor orientation in 3D space + /// Sets actor orientation in 3D space. /// /// The value to set. API_PROPERTY() void SetOrientation(const Quaternion& value); /// - /// Gets actor scale in 3D space + /// Gets actor scale in 3D space. /// API_PROPERTY(Attributes="HideInEditor, NoSerialize") FORCE_INLINE Vector3 GetScale() const @@ -458,13 +458,13 @@ public: API_PROPERTY() void SetScale(const Vector3& value); /// - /// Gets actor rotation matrix + /// Gets actor rotation matrix. /// API_PROPERTY(Attributes="HideInEditor, NoSerialize") Matrix GetRotation() const; /// - /// Sets actor rotation matrix + /// Sets actor rotation matrix. /// /// The value to set. API_PROPERTY() void SetRotation(const Matrix& value); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index d51d7b754..15d8825a8 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -488,10 +488,7 @@ namespace Flax.Build.Bindings { if (comment.Contains("/// ")) continue; - var c = comment.Replace("::", "."); - contents.Append(indent); - contents.Append(c); - contents.AppendLine(); + contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine(); } GenerateCSharpAttributes(buildData, contents, indent, eventInfo, true); @@ -586,11 +583,7 @@ namespace Flax.Build.Bindings { if (comment.Contains("/// ")) continue; - - var c = comment.Replace("::", "."); - contents.Append(indent); - contents.Append(c); - contents.AppendLine(); + contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine(); } GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, true); @@ -636,14 +629,7 @@ namespace Flax.Build.Bindings { if (comment.Contains("/// ") || comment.Contains(" Date: Sun, 7 Feb 2021 21:41:46 +0100 Subject: [PATCH 344/419] Fix Editor timeline editor controls API visible in Visual Scripting --- Source/Editor/GUI/Timeline/Track.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/GUI/Timeline/Track.cs b/Source/Editor/GUI/Timeline/Track.cs index 4f5278251..6ba8191d6 100644 --- a/Source/Editor/GUI/Timeline/Track.cs +++ b/Source/Editor/GUI/Timeline/Track.cs @@ -16,6 +16,7 @@ namespace FlaxEditor.GUI.Timeline /// The Timeline track that contains a header and custom timeline events/media. /// /// + [HideInEditor] public class Track : ContainerControl { /// From da784e98e5913b8bc616d63a1ad722a9e26efd92 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 8 Feb 2021 15:44:38 +0100 Subject: [PATCH 345/419] Add Deformable material domain --- .../MaterialTemplates/Deformable.shader | 375 ++++++++++++++++++ Source/Editor/Surface/Archetypes/Material.cs | 1 + .../Viewport/Previews/MaterialPreview.cs | 3 + Source/Engine/Content/Assets/Material.cpp | 5 +- .../Materials/DeformableMaterialShader.cpp | 195 +++++++++ .../Materials/DeformableMaterialShader.h | 63 +++ Source/Engine/Graphics/Materials/IMaterial.h | 8 + .../Engine/Graphics/Materials/MaterialInfo.h | 5 + .../Graphics/Materials/MaterialShader.cpp | 8 +- Source/Engine/Graphics/Models/Mesh.cpp | 2 +- Source/Engine/Graphics/Models/Mesh.h | 2 +- Source/Engine/Renderer/DrawCall.h | 11 + .../MaterialGenerator/MaterialGenerator.cpp | 16 +- 13 files changed, 685 insertions(+), 9 deletions(-) create mode 100644 Content/Editor/MaterialTemplates/Deformable.shader create mode 100644 Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp create mode 100644 Source/Engine/Graphics/Materials/DeformableMaterialShader.h diff --git a/Content/Editor/MaterialTemplates/Deformable.shader b/Content/Editor/MaterialTemplates/Deformable.shader new file mode 100644 index 000000000..43afa566a --- /dev/null +++ b/Content/Editor/MaterialTemplates/Deformable.shader @@ -0,0 +1,375 @@ +// File generated by Flax Materials Editor +// Version: @0 + +#define MATERIAL 1 +@3 +#include "./Flax/Common.hlsl" +#include "./Flax/MaterialCommon.hlsl" +#include "./Flax/GBufferCommon.hlsl" +@7 +// Primary constant buffer (with additional material parameters) +META_CB_BEGIN(0, Data) +float4x4 ViewProjectionMatrix; +float4x4 WorldMatrix; +float4x4 LocalMatrix; +float4x4 ViewMatrix; +float3 ViewPos; +float ViewFar; +float3 ViewDir; +float TimeParam; +float4 ViewInfo; +float4 ScreenSize; +float3 WorldInvScale; +float WorldDeterminantSign; +float MeshMinZ; +float Segment; +float ChunksPerSegment; +float PerInstanceRandom; +float4 TemporalAAJitter; +float3 GeometrySize; +float MeshMaxZ; +@1META_CB_END + +// Shader resources +@2 +// The spline deformation buffer (stored as 4x3, 3 float4 behind each other) +Buffer SplineDeformation : register(t0); + +// Geometry data passed though the graphics rendering stages up to the pixel shader +struct GeometryData +{ + float3 WorldPosition : TEXCOORD0; + float2 TexCoord : TEXCOORD1; +#if USE_VERTEX_COLOR + half4 VertexColor : COLOR; +#endif + float3 WorldNormal : TEXCOORD2; + float4 WorldTangent : TEXCOORD3; +}; + +// Interpolants passed from the vertex shader +struct VertexOutput +{ + float4 Position : SV_Position; + GeometryData Geometry; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; +#endif +#if USE_TESSELLATION + float TessellationMultiplier : TESS; +#endif +}; + +// Interpolants passed to the pixel shader +struct PixelInput +{ + float4 Position : SV_Position; + GeometryData Geometry; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT] : TEXCOORD9; +#endif + bool IsFrontFace : SV_IsFrontFace; +}; + +// Material properties generation input +struct MaterialInput +{ + float3 WorldPosition; + float TwoSidedSign; + float2 TexCoord; +#if USE_VERTEX_COLOR + half4 VertexColor; +#endif + float3x3 TBN; + float4 SvPosition; + float3 PreSkinnedPosition; + float3 PreSkinnedNormal; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + float4 CustomVSToPS[CUSTOM_VERTEX_INTERPOLATORS_COUNT]; +#endif +}; + +// Extracts geometry data to the material input +MaterialInput GetGeometryMaterialInput(GeometryData geometry) +{ + MaterialInput output = (MaterialInput)0; + output.WorldPosition = geometry.WorldPosition; + output.TexCoord = geometry.TexCoord; +#if USE_VERTEX_COLOR + output.VertexColor = geometry.VertexColor; +#endif + output.TBN = CalcTangentBasis(geometry.WorldNormal, geometry.WorldTangent); + return output; +} + +#if USE_TESSELLATION + +// Interpolates the geometry positions data only (used by the tessallation when generating vertices) +#define InterpolateGeometryPositions(output, p0, w0, p1, w1, p2, w2, offset) output.WorldPosition = p0.WorldPosition * w0 + p1.WorldPosition * w1 + p2.WorldPosition * w2 + offset + +// Offsets the geometry positions data only (used by the tessallation when generating vertices) +#define OffsetGeometryPositions(geometry, offset) geometry.WorldPosition += offset + +// Applies the Phong tessallation to the geometry positions (used by the tessallation when doing Phong tess) +#define ApplyGeometryPositionsPhongTess(geometry, p0, p1, p2, U, V, W) \ + float3 posProjectedU = TessalationProjectOntoPlane(p0.WorldNormal, p0.WorldPosition, geometry.WorldPosition); \ + float3 posProjectedV = TessalationProjectOntoPlane(p1.WorldNormal, p1.WorldPosition, geometry.WorldPosition); \ + float3 posProjectedW = TessalationProjectOntoPlane(p2.WorldNormal, p2.WorldPosition, geometry.WorldPosition); \ + geometry.WorldPosition = U * posProjectedU + V * posProjectedV + W * posProjectedW + +// Interpolates the geometry data except positions (used by the tessallation when generating vertices) +GeometryData InterpolateGeometry(GeometryData p0, float w0, GeometryData p1, float w1, GeometryData p2, float w2) +{ + GeometryData output = (GeometryData)0; + output.TexCoord = p0.TexCoord * w0 + p1.TexCoord * w1 + p2.TexCoord * w2; +#if USE_VERTEX_COLOR + output.VertexColor = p0.VertexColor * w0 + p1.VertexColor * w1 + p2.VertexColor * w2; +#endif + output.WorldNormal = p0.WorldNormal * w0 + p1.WorldNormal * w1 + p2.WorldNormal * w2; + output.WorldNormal = normalize(output.WorldNormal); + output.WorldTangent = p0.WorldTangent * w0 + p1.WorldTangent * w1 + p2.WorldTangent * w2; + output.WorldTangent.xyz = normalize(output.WorldTangent.xyz); + return output; +} + +#endif + +MaterialInput GetMaterialInput(PixelInput input) +{ + MaterialInput output = GetGeometryMaterialInput(input.Geometry); + output.TwoSidedSign = WorldDeterminantSign * (input.IsFrontFace ? 1.0 : -1.0); + output.SvPosition = input.Position; +#if USE_CUSTOM_VERTEX_INTERPOLATORS + output.CustomVSToPS = input.CustomVSToPS; +#endif + return output; +} + +// Removes the scale vector from the local to world transformation matrix +float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) +{ + localToWorld[0] *= WorldInvScale.x; + localToWorld[1] *= WorldInvScale.y; + localToWorld[2] *= WorldInvScale.z; + return localToWorld; +} + +// Transforms a vector from tangent space to world space +float3 TransformTangentVectorToWorld(MaterialInput input, float3 tangentVector) +{ + return mul(tangentVector, input.TBN); +} + +// Transforms a vector from world space to tangent space +float3 TransformWorldVectorToTangent(MaterialInput input, float3 worldVector) +{ + return mul(input.TBN, worldVector); +} + +// Transforms a vector from world space to view space +float3 TransformWorldVectorToView(MaterialInput input, float3 worldVector) +{ + return mul(worldVector, (float3x3)ViewMatrix); +} + +// Transforms a vector from view space to world space +float3 TransformViewVectorToWorld(MaterialInput input, float3 viewVector) +{ + return mul((float3x3)ViewMatrix, viewVector); +} + +// Transforms a vector from local space to world space +float3 TransformLocalVectorToWorld(MaterialInput input, float3 localVector) +{ + float3x3 localToWorld = (float3x3)WorldMatrix; + //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); + return mul(localVector, localToWorld); +} + +// Transforms a vector from local space to world space +float3 TransformWorldVectorToLocal(MaterialInput input, float3 worldVector) +{ + float3x3 localToWorld = (float3x3)WorldMatrix; + //localToWorld = RemoveScaleFromLocalToWorld(localToWorld); + return mul(localToWorld, worldVector); +} + +// Gets the current object position +float3 GetObjectPosition(MaterialInput input) +{ + return WorldMatrix[3].xyz; +} + +// Gets the current object size +float3 GetObjectSize(MaterialInput input) +{ + float4x4 world = WorldMatrix; + return GeometrySize * float3(world._m00, world._m11, world._m22); +} + +// Get the current object random value +float GetPerInstanceRandom(MaterialInput input) +{ + return PerInstanceRandom; +} + +// Get the current object LOD transition dither factor +float GetLODDitherFactor(MaterialInput input) +{ + return 0; +} + +// Gets the interpolated vertex color (in linear space) +float4 GetVertexColor(MaterialInput input) +{ +#if USE_VERTEX_COLOR + return input.VertexColor; +#else + return 1; +#endif +} + +float3 SampleLightmap(Material material, MaterialInput materialInput) +{ + return 0; +} + +@8 + +// Get material properties function (for vertex shader) +Material GetMaterialVS(MaterialInput input) +{ +@5 +} + +// Get material properties function (for domain shader) +Material GetMaterialDS(MaterialInput input) +{ +@6 +} + +// Get material properties function (for pixel shader) +Material GetMaterialPS(MaterialInput input) +{ +@4 +} + +// Calculates the transform matrix from mesh tangent space to local space +float3x3 CalcTangentToLocal(ModelInput input) +{ + float bitangentSign = input.Tangent.w ? -1.0f : +1.0f; + float3 normal = input.Normal.xyz * 2.0 - 1.0; + float3 tangent = input.Tangent.xyz * 2.0 - 1.0; + float3 bitangent = cross(normal, tangent) * bitangentSign; + return float3x3(tangent, bitangent, normal); +} + +// Vertex Shader function for GBuffer Pass and Depth Pass (with full vertex data) +META_VS(true, FEATURE_LEVEL_ES2) +META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(TEXCOORD, 0, R16G16_FLOAT, 1, 0, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(NORMAL, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(TANGENT, 0, R10G10B10A2_UNORM, 1, ALIGN, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(TEXCOORD, 1, R16G16_FLOAT, 1, ALIGN, PER_VERTEX, 0, true) +META_VS_IN_ELEMENT(COLOR, 0, R8G8B8A8_UNORM, 2, 0, PER_VERTEX, 0, USE_VERTEX_COLOR) +VertexOutput VS_SplineModel(ModelInput input) +{ + VertexOutput output; + + // Apply local transformation of the geometry before deformation + float3 position = mul(float4(input.Position, 1), LocalMatrix).xyz; + + // Apply spline curve deformation + float splineAlpha = saturate((position.z - MeshMinZ) / (MeshMaxZ - MeshMinZ)); + int splineIndex = (int)((Segment + splineAlpha) * ChunksPerSegment); + position.z = splineAlpha; + float3x4 splineMatrix = float3x4(SplineDeformation[splineIndex * 3], SplineDeformation[splineIndex * 3 + 1], SplineDeformation[splineIndex * 3 + 2]); + position = mul(splineMatrix, float4(position, 1)); + + // Compute world space vertex position + output.Geometry.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz; + + // Compute clip space position + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); + + // Pass vertex attributes + output.Geometry.TexCoord = input.TexCoord; +#if USE_VERTEX_COLOR + output.Geometry.VertexColor = input.Color; +#endif + + // Calculate tanget space to world space transformation matrix for unit vectors + float3x3 tangentToLocal = CalcTangentToLocal(input); + float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)WorldMatrix); + float3x3 tangentToWorld = mul(tangentToLocal, localToWorld); + output.Geometry.WorldNormal = tangentToWorld[2]; + output.Geometry.WorldTangent.xyz = tangentToWorld[0]; + output.Geometry.WorldTangent.w = input.Tangent.w ? -1.0f : +1.0f; + + // Get material input params if need to evaluate any material property +#if USE_POSITION_OFFSET || USE_TESSELLATION || USE_CUSTOM_VERTEX_INTERPOLATORS + MaterialInput materialInput = GetGeometryMaterialInput(output.Geometry); + materialInput.TwoSidedSign = WorldDeterminantSign; + materialInput.SvPosition = output.Position; + materialInput.PreSkinnedPosition = input.Position.xyz; + materialInput.PreSkinnedNormal = tangentToLocal[2].xyz; + Material material = GetMaterialVS(materialInput); +#endif + + // Apply world position offset per-vertex +#if USE_POSITION_OFFSET + output.Geometry.WorldPosition += material.PositionOffset; + output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); +#endif + + // Get tessalation multiplier (per vertex) +#if USE_TESSELLATION + output.TessellationMultiplier = material.TessellationMultiplier; +#endif + + // Copy interpolants for other shader stages +#if USE_CUSTOM_VERTEX_INTERPOLATORS + output.CustomVSToPS = material.CustomVSToPS; +#endif + + return output; +} + +// Vertex Shader function for Depth Pass +META_VS(true, FEATURE_LEVEL_ES2) +META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) +float4 VS_Depth(ModelInput_PosOnly input) : SV_Position +{ + float3 worldPosition = mul(float4(input.Position.xyz, 1), WorldMatrix).xyz; + float4 position = mul(float4(worldPosition, 1), ViewProjectionMatrix); + return position; +} + +#if USE_DITHERED_LOD_TRANSITION + +void ClipLODTransition(PixelInput input) +{ +} + +#endif + +// Pixel Shader function for Depth Pass +META_PS(true, FEATURE_LEVEL_ES2) +void PS_Depth(PixelInput input) +{ +#if MATERIAL_MASKED || MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE + // Get material parameters + MaterialInput materialInput = GetMaterialInput(input); + Material material = GetMaterialPS(materialInput); + + // Perform per pixel clipping +#if MATERIAL_MASKED + clip(material.Mask - MATERIAL_MASK_THRESHOLD); +#endif +#if MATERIAL_BLEND != MATERIAL_BLEND_OPAQUE + clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD); +#endif +#endif +} + +@9 diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index 6e828fca4..9edbedcfb 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -156,6 +156,7 @@ namespace FlaxEditor.Surface.Archetypes case MaterialDomain.Surface: case MaterialDomain.Terrain: case MaterialDomain.Particle: + case MaterialDomain.Deformable: { bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit; bool isTransparent = info.BlendMode == MaterialBlendMode.Transparent; diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index 6ff695b69..41275ef9a 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -172,6 +172,9 @@ namespace FlaxEditor.Viewport.Previews usePreviewActor = false; particleMaterial = _material; break; + case MaterialDomain.Deformable: + // TODO: preview Deformable material (eg. by using Spline with Spline Model) + break; default: throw new ArgumentOutOfRangeException(); } } diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 966253aba..2930aff24 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -392,12 +392,12 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) auto& info = _shaderHeader.Material.Info; const bool isSurfaceOrTerrain = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain; const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage; - const bool useForward = (info.Domain == MaterialDomain::Surface && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle; + const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle; const bool useTess = info.TessellationMode != TessellationMethod::None && RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrain; const bool useDistortion = - (info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Particle) && + (info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) && info.BlendMode != MaterialBlendMode::Opaque && (info.UsageFlags & MaterialUsageFlags::UseRefraction) != 0 && (info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == 0; @@ -455,6 +455,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) options.Macros.Add({ "IS_DECAL", Numbers[info.Domain == MaterialDomain::Decal ? 1 : 0] }); options.Macros.Add({ "IS_TERRAIN", Numbers[info.Domain == MaterialDomain::Terrain ? 1 : 0] }); options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] }); + options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] }); options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] }); options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrain && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] }); options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] }); diff --git a/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp new file mode 100644 index 000000000..5adb18470 --- /dev/null +++ b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp @@ -0,0 +1,195 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "DeformableMaterialShader.h" +#include "MaterialShaderFeatures.h" +#include "MaterialParams.h" +#include "Engine/Graphics/RenderBuffers.h" +#include "Engine/Graphics/RenderView.h" +#include "Engine/Renderer/DrawCall.h" +#include "Engine/Renderer/RenderList.h" +#include "Engine/Graphics/GPUContext.h" +#include "Engine/Graphics/Shaders/GPUConstantBuffer.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Graphics/Shaders/GPUShader.h" +#include "Engine/Graphics/GPULimits.h" +#include "Engine/Engine/Time.h" +#include "Engine/Graphics/RenderTask.h" + +PACK_STRUCT(struct DeformableMaterialShaderData { + Matrix ViewProjectionMatrix; + Matrix WorldMatrix; + Matrix LocalMatrix; + Matrix ViewMatrix; + Vector3 ViewPos; + float ViewFar; + Vector3 ViewDir; + float TimeParam; + Vector4 ViewInfo; + Vector4 ScreenSize; + Vector3 WorldInvScale; + float WorldDeterminantSign; + float MeshMinZ; + float Segment; + float ChunksPerSegment; + float PerInstanceRandom; + Vector4 TemporalAAJitter; + Vector3 GeometrySize; + float MeshMaxZ; + }); + +DrawPass DeformableMaterialShader::GetDrawModes() const +{ + return _drawModes; +} + +void DeformableMaterialShader::Bind(BindParameters& params) +{ + // Prepare + auto context = params.GPUContext; + auto& view = params.RenderContext.View; + auto& drawCall = *params.FirstDrawCall; + byte* cb = _cbData.Get(); + auto materialData = reinterpret_cast(cb); + cb += sizeof(DeformableMaterialShaderData); + int32 srv = 1; + + // Setup features + if (_info.BlendMode != MaterialBlendMode::Opaque) + ForwardShadingFeature::Bind(params, cb, srv); + + // Setup parameters + MaterialParameter::BindMeta bindMeta; + bindMeta.Context = context; + bindMeta.Constants = cb; + bindMeta.Input = nullptr; + bindMeta.Buffers = nullptr; + bindMeta.CanSampleDepth = false; + bindMeta.CanSampleGBuffer = false; + MaterialParams::Bind(params.ParamsLink, bindMeta); + + // Setup material constants + { + Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); + Matrix::Transpose(drawCall.World, materialData->WorldMatrix); + Matrix::Transpose(drawCall.Deformable.LocalMatrix, materialData->LocalMatrix); + Matrix::Transpose(view.View, materialData->ViewMatrix); + materialData->ViewPos = view.Position; + materialData->ViewFar = view.Far; + materialData->ViewDir = view.Direction; + materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds(); + materialData->ViewInfo = view.ViewInfo; + materialData->ScreenSize = view.ScreenSize; + const float scaleX = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13).Length(); + const float scaleY = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23).Length(); + const float scaleZ = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33).Length(); + materialData->WorldInvScale = Vector3( + scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, + scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, + scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); + materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; + materialData->Segment = drawCall.Deformable.Segment; + materialData->ChunksPerSegment = drawCall.Deformable.ChunksPerSegment; + materialData->MeshMinZ = drawCall.Deformable.MeshMinZ; + materialData->MeshMaxZ = drawCall.Deformable.MeshMaxZ; + materialData->PerInstanceRandom = drawCall.PerInstanceRandom; + materialData->TemporalAAJitter = view.TemporalAAJitter; + materialData->GeometrySize = drawCall.Deformable.GeometrySize; + } + + // Bind spline deformation buffer + context->BindSR(0, drawCall.Deformable.SplineDeformation->View()); + + // Bind constants + if (_cb) + { + context->UpdateCB(_cb, _cbData.Get()); + context->BindCB(0, _cb); + } + + // Select pipeline state based on current pass and render mode + const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != 0 || view.Mode == ViewMode::Wireframe; + CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode; + if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0) + { + // Invert culling when scale is negative + if (cullMode == CullMode::Normal) + cullMode = CullMode::Inverted; + else + cullMode = CullMode::Normal; + } + PipelineStateCache* psCache = _cache.GetPS(view.Pass); + ASSERT(psCache); + GPUPipelineState* state = psCache->GetPS(cullMode, wireframe); + + // Bind pipeline + context->SetState(state); +} + +void DeformableMaterialShader::Unload() +{ + // Base + MaterialShader::Unload(); + + _cache.Release(); +} + +bool DeformableMaterialShader::Load() +{ + _drawModes = DrawPass::Depth; + auto psDesc = GPUPipelineState::Description::Default; + psDesc.DepthTestEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthTest) == 0; + psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == 0; + + // Check if use tessellation (both material and runtime supports it) + const bool useTess = _info.TessellationMode != TessellationMethod::None && GPUDevice::Instance->Limits.HasTessellation; + if (useTess) + { + psDesc.HS = _shader->GetHS("HS"); + psDesc.DS = _shader->GetDS("DS"); + } + + if (_info.BlendMode == MaterialBlendMode::Opaque) + { + _drawModes = DrawPass::GBuffer; + + // GBuffer Pass + psDesc.VS = _shader->GetVS("VS_SplineModel"); + psDesc.PS = _shader->GetPS("PS_GBuffer"); + _cache.Default.Init(psDesc); + } + else + { + _drawModes = DrawPass::Forward; + + // Forward Pass + psDesc.VS = _shader->GetVS("VS_SplineModel"); + psDesc.PS = _shader->GetPS("PS_Forward"); + psDesc.DepthWriteEnable = false; + psDesc.BlendMode = BlendingMode::AlphaBlend; + switch (_info.BlendMode) + { + case MaterialBlendMode::Transparent: + psDesc.BlendMode = BlendingMode::AlphaBlend; + break; + case MaterialBlendMode::Additive: + psDesc.BlendMode = BlendingMode::Additive; + break; + case MaterialBlendMode::Multiply: + psDesc.BlendMode = BlendingMode::Multiply; + break; + } + _cache.Default.Init(psDesc); + } + + // Depth Pass + psDesc.CullMode = CullMode::TwoSided; + psDesc.DepthClipEnable = false; + psDesc.DepthWriteEnable = true; + psDesc.DepthTestEnable = true; + psDesc.DepthFunc = ComparisonFunc::Less; + psDesc.HS = nullptr; + psDesc.DS = nullptr; + _cache.Depth.Init(psDesc); + + return false; +} diff --git a/Source/Engine/Graphics/Materials/DeformableMaterialShader.h b/Source/Engine/Graphics/Materials/DeformableMaterialShader.h new file mode 100644 index 000000000..f2c796a23 --- /dev/null +++ b/Source/Engine/Graphics/Materials/DeformableMaterialShader.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "MaterialShader.h" + +/// +/// Represents material that can be used to render objects that can be deformed. +/// +class DeformableMaterialShader : public MaterialShader +{ +private: + + struct Cache + { + PipelineStateCache Default; + PipelineStateCache Depth; + + FORCE_INLINE PipelineStateCache* GetPS(const DrawPass pass) + { + switch (pass) + { + case DrawPass::Depth: + return &Depth; + case DrawPass::GBuffer: + case DrawPass::Forward: + return &Default; + default: + return nullptr; + } + } + + FORCE_INLINE void Release() + { + Default.Release(); + Depth.Release(); + } + }; + +private: + + Cache _cache; + DrawPass _drawModes = DrawPass::None; + +public: + + DeformableMaterialShader(const String& name) + : MaterialShader(name) + { + } + +public: + + // [MaterialShader] + DrawPass GetDrawModes() const override; + void Bind(BindParameters& params) override; + void Unload() override; + +protected: + + // [MaterialShader] + bool Load() override; +}; diff --git a/Source/Engine/Graphics/Materials/IMaterial.h b/Source/Engine/Graphics/Materials/IMaterial.h index e85328133..4d61656e8 100644 --- a/Source/Engine/Graphics/Materials/IMaterial.h +++ b/Source/Engine/Graphics/Materials/IMaterial.h @@ -74,6 +74,14 @@ public: return GetInfo().Domain == MaterialDomain::Particle; } + /// + /// Determines whether material is a deformable shader. + /// + FORCE_INLINE bool IsDeformable() const + { + return GetInfo().Domain == MaterialDomain::Deformable; + } + /// /// Returns true if material is ready for rendering. /// diff --git a/Source/Engine/Graphics/Materials/MaterialInfo.h b/Source/Engine/Graphics/Materials/MaterialInfo.h index 50b4c1e35..862f8f221 100644 --- a/Source/Engine/Graphics/Materials/MaterialInfo.h +++ b/Source/Engine/Graphics/Materials/MaterialInfo.h @@ -39,6 +39,11 @@ API_ENUM() enum class MaterialDomain : byte /// The particle shader. Can be used only with particles geometry (sprites, trails and ribbons). Supports reading particle data on a GPU. /// Particle = 5, + + /// + /// The deformable shader. Can be used only with objects that can be deformed (spline models). + /// + Deformable = 6, }; /// diff --git a/Source/Engine/Graphics/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp index 2d7998b8a..caac3b5f2 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp @@ -14,7 +14,7 @@ #include "GUIMaterialShader.h" #include "TerrainMaterialShader.h" #include "ParticleMaterialShader.h" -//#include "DeformableMaterialShader.h" +#include "DeformableMaterialShader.h" GPUPipelineState* MaterialShader::PipelineStateCache::InitPS(CullMode mode, bool wireframe) { @@ -62,9 +62,9 @@ MaterialShader* MaterialShader::Create(const String& name, MemoryReadStream& sha case MaterialDomain::Particle: material = New(name); break; - /*case MaterialDomain::Deformable: - material = New(name); - break;*/ + case MaterialDomain::Deformable: + material = New(name); + break; default: LOG(Fatal, "Unknown material type."); return nullptr; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 99262cd51..e7007f4ec 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -350,7 +350,7 @@ bool Mesh::Intersects(const Ray& ray, const Matrix& world, float& distance, Vect #endif } -void Mesh::GetDrawCallGeometry(DrawCall& drawCall) +void Mesh::GetDrawCallGeometry(DrawCall& drawCall) const { drawCall.Geometry.IndexBuffer = _indexBuffer; drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0]; diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 80d566fa3..94d4979a3 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -377,7 +377,7 @@ public: /// Gets the draw call geometry for this mesh. Sets the index and vertex buffers. /// /// The draw call. - void GetDrawCallGeometry(DrawCall& drawCall); + void GetDrawCallGeometry(DrawCall& drawCall) const; /// /// Model instance drawing packed data. diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index 0e91366e2..bb25c0a57 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -211,6 +211,17 @@ struct DrawCall } Ribbon; } Particle; + struct + { + GPUBuffer* SplineDeformation; + Matrix LocalMatrix; // Geometry transformation applied before deformation. + Vector3 GeometrySize; // Object geometry size in the world (unscaled). + float Segment; + float ChunksPerSegment; + float MeshMinZ; + float MeshMaxZ; + } Deformable; + struct { byte Raw[96]; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp index 2f54efe82..a3a599529 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.cpp @@ -206,6 +206,14 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo ADD_FEATURE(DistortionFeature); ADD_FEATURE(ForwardShadingFeature); break; + case MaterialDomain::Deformable: + if (materialInfo.TessellationMode != TessellationMethod::None) + ADD_FEATURE(TessellationFeature); + if (materialInfo.BlendMode == MaterialBlendMode::Opaque) + ADD_FEATURE(DeferredShadingFeature); + if (materialInfo.BlendMode != MaterialBlendMode::Opaque) + ADD_FEATURE(ForwardShadingFeature); + break; default: break; } @@ -228,7 +236,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo { materialVarPS = Value(VariantType::Void, baseLayer->GetVariableName(nullptr)); _writer.Write(TEXT("\tMaterial {0} = (Material)0;\n"), materialVarPS.Value); - if (baseLayer->Domain == MaterialDomain::Surface || baseLayer->Domain == MaterialDomain::Terrain || baseLayer->Domain == MaterialDomain::Particle) + if (baseLayer->Domain == MaterialDomain::Surface || baseLayer->Domain == MaterialDomain::Terrain || baseLayer->Domain == MaterialDomain::Particle || baseLayer->Domain == MaterialDomain::Deformable) { eatMaterialGraphBox(baseLayer, MaterialGraphBoxes::Emissive); eatMaterialGraphBox(baseLayer, MaterialGraphBoxes::Normal); @@ -410,6 +418,9 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo case MaterialDomain::Particle: srv = 2; // Particles data + Sorted indices/Ribbon segments break; + case MaterialDomain::Deformable: + srv = 1; // Mesh deformation buffer + break; } for (auto f : features) { @@ -489,6 +500,9 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo case MaterialDomain::Particle: path /= TEXT("Particle.shader"); break; + case MaterialDomain::Deformable: + path /= TEXT("Deformable.shader"); + break; default: LOG(Warning, "Unknown material domain."); return true; From 8e87c986446095837f7e11996729964b87fb7873 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 8 Feb 2021 15:45:22 +0100 Subject: [PATCH 346/419] Cleanup material shaders code --- .../Editor/MaterialTemplates/Particle.shader | 10 +--------- .../Editor/MaterialTemplates/Surface.shader | 19 +++++-------------- .../Editor/MaterialTemplates/Terrain.shader | 10 +--------- .../Materials/DecalMaterialShader.cpp | 2 +- .../Materials/DeferredMaterialShader.cpp | 8 ++------ .../Materials/ForwardMaterialShader.cpp | 9 ++------- .../Graphics/Materials/GUIMaterialShader.cpp | 2 +- .../Materials/ParticleMaterialShader.cpp | 2 +- .../Materials/PostFxMaterialShader.cpp | 2 +- .../Materials/TerrainMaterialShader.cpp | 3 +-- 10 files changed, 16 insertions(+), 51 deletions(-) diff --git a/Content/Editor/MaterialTemplates/Particle.shader b/Content/Editor/MaterialTemplates/Particle.shader index 109fe668b..726ad225a 100644 --- a/Content/Editor/MaterialTemplates/Particle.shader +++ b/Content/Editor/MaterialTemplates/Particle.shader @@ -679,11 +679,7 @@ VertexOutput VS_Ribbon(uint vertexIndex : SV_VertexID) // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) -void PS_Depth(PixelInput input -#if GLSL - , out float4 OutColor : SV_Target0 -#endif - ) +void PS_Depth(PixelInput input) { // Get material parameters MaterialInput materialInput = GetMaterialInput(input); @@ -696,10 +692,6 @@ void PS_Depth(PixelInput input #if MATERIAL_BLEND == MATERIAL_BLEND_TRANSPARENT clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD); #endif - -#if GLSL - OutColor = 0; -#endif } @9 diff --git a/Content/Editor/MaterialTemplates/Surface.shader b/Content/Editor/MaterialTemplates/Surface.shader index f3cc8499e..1038a73a3 100644 --- a/Content/Editor/MaterialTemplates/Surface.shader +++ b/Content/Editor/MaterialTemplates/Surface.shader @@ -43,9 +43,9 @@ struct GeometryData #endif float3 WorldNormal : TEXCOORD3; float4 WorldTangent : TEXCOORD4; - float3 InstanceOrigin : TEXCOORD6; - float2 InstanceParams : TEXCOORD7; // x-PerInstanceRandom, y-LODDitherFactor - float3 PrevWorldPosition : TEXCOORD8; + float3 InstanceOrigin : TEXCOORD5; + float2 InstanceParams : TEXCOORD6; // x-PerInstanceRandom, y-LODDitherFactor + float3 PrevWorldPosition : TEXCOORD7; }; // Interpolants passed from the vertex shader @@ -475,8 +475,7 @@ float3x4 GetBoneMatrix(ModelInput_Skinned input) // Transforms the vertex position by weighted sum of the skinning matrices float3 SkinPosition(ModelInput_Skinned input, SkinningData data) { - float4 position = float4(input.Position.xyz, 1); - return mul(data.BlendMatrix, position); + return mul(data.BlendMatrix, float4(input.Position.xyz, 1)); } // Transforms the vertex position by weighted sum of the skinning matrices @@ -596,11 +595,7 @@ void ClipLODTransition(PixelInput input) // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) -void PS_Depth(PixelInput input -#if GLSL - , out float4 OutColor : SV_Target0 -#endif - ) +void PS_Depth(PixelInput input) { #if USE_DITHERED_LOD_TRANSITION // LOD masking @@ -620,10 +615,6 @@ void PS_Depth(PixelInput input clip(material.Opacity - MATERIAL_OPACITY_THRESHOLD); #endif #endif - -#if GLSL - OutColor = 0; -#endif } @9 diff --git a/Content/Editor/MaterialTemplates/Terrain.shader b/Content/Editor/MaterialTemplates/Terrain.shader index d7624dfd6..b1c3a2910 100644 --- a/Content/Editor/MaterialTemplates/Terrain.shader +++ b/Content/Editor/MaterialTemplates/Terrain.shader @@ -448,11 +448,7 @@ VertexOutput VS(TerrainVertexInput input) // Pixel Shader function for Depth Pass META_PS(true, FEATURE_LEVEL_ES2) -void PS_Depth(PixelInput input -#if GLSL - , out float4 OutColor : SV_Target0 -#endif - ) +void PS_Depth(PixelInput input) { #if MATERIAL_MASKED // Perform per pixel clipping if material requries it @@ -460,10 +456,6 @@ void PS_Depth(PixelInput input Material material = GetMaterialPS(materialInput); clip(material.Mask - MATERIAL_MASK_THRESHOLD); #endif - -#if GLSL - OutColor = 0; -#endif } @9 diff --git a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp index e141996dc..d8d68e912 100644 --- a/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DecalMaterialShader.cpp @@ -56,7 +56,7 @@ void DecalMaterialShader::Bind(BindParameters& params) // Decals use depth buffer to draw on top of the objects context->BindSR(0, GET_TEXTURE_VIEW_SAFE(params.RenderContext.Buffers->DepthBuffer)); - // Setup material constants data + // Setup material constants { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index d8b40c3a6..babfaf830 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -79,30 +79,26 @@ void DeferredMaterialShader::Bind(BindParameters& params) bindMeta.CanSampleGBuffer = false; MaterialParams::Bind(params.ParamsLink, bindMeta); + // Setup material constants { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); Matrix::Transpose(view.View, materialData->ViewMatrix); Matrix::Transpose(drawCall.Surface.PrevWorld, materialData->PrevWorldMatrix); Matrix::Transpose(view.PrevViewProjection, materialData->PrevViewProjectionMatrix); - materialData->ViewPos = view.Position; materialData->ViewFar = view.Far; materialData->ViewDir = view.Direction; materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds(); materialData->ViewInfo = view.ViewInfo; materialData->ScreenSize = view.ScreenSize; - - // Extract per axis scales from LocalToWorld transform const float scaleX = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13).Length(); const float scaleY = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23).Length(); const float scaleZ = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33).Length(); - const Vector3 worldInvScale = Vector3( + materialData->WorldInvScale = Vector3( scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); - - materialData->WorldInvScale = worldInvScale; materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; materialData->LODDitherFactor = drawCall.Surface.LODDitherFactor; materialData->PerInstanceRandom = drawCall.PerInstanceRandom; diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index addd7e96a..2ea17c9cd 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -85,31 +85,26 @@ void ForwardMaterialShader::Bind(BindParameters& params) context->BindSR(0, drawCall.Surface.Skinning->BoneMatrices->View()); } - // Setup material constants data + // Setup material constants { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); Matrix::Transpose(view.View, materialData->ViewMatrix); Matrix::Transpose(drawCall.Surface.PrevWorld, materialData->PrevWorldMatrix); Matrix::Transpose(view.PrevViewProjection, materialData->PrevViewProjectionMatrix); - materialData->ViewPos = view.Position; materialData->ViewFar = view.Far; materialData->ViewDir = view.Direction; materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds(); materialData->ViewInfo = view.ViewInfo; materialData->ScreenSize = view.ScreenSize; - - // Extract per axis scales from LocalToWorld transform const float scaleX = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13).Length(); const float scaleY = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23).Length(); const float scaleZ = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33).Length(); - const Vector3 worldInvScale = Vector3( + materialData->WorldInvScale = Vector3( scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); - - materialData->WorldInvScale = worldInvScale; materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; materialData->LODDitherFactor = drawCall.Surface.LODDitherFactor; materialData->PerInstanceRandom = drawCall.PerInstanceRandom; diff --git a/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp b/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp index 9e1d9ea21..769d36557 100644 --- a/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/GUIMaterialShader.cpp @@ -44,7 +44,7 @@ void GUIMaterialShader::Bind(BindParameters& params) bindMeta.CanSampleGBuffer = false; MaterialParams::Bind(params.ParamsLink, bindMeta); - // Setup material constants data + // Setup material constants { const auto viewProjectionMatrix = (Matrix*)params.CustomData; Matrix::Transpose(*viewProjectionMatrix, materialData->ViewProjectionMatrix); diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index eda7fff22..7a718ceb7 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -145,7 +145,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) ASSERT(psCache); GPUPipelineState* state = psCache->GetPS(cullMode, wireframe); - // Setup material constants data + // Setup material constants { static StringView ParticlePosition(TEXT("Position")); static StringView ParticleSpriteSize(TEXT("SpriteSize")); diff --git a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp index 890275f68..a55455173 100644 --- a/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/PostFxMaterialShader.cpp @@ -41,7 +41,7 @@ void PostFxMaterialShader::Bind(BindParameters& params) bindMeta.CanSampleGBuffer = true; MaterialParams::Bind(params.ParamsLink, bindMeta); - // Setup material constants data + // Setup material constants { Matrix::Transpose(view.View, materialData->ViewMatrix); materialData->ViewPos = view.Position; diff --git a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp index 80988e405..ec3c038a5 100644 --- a/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/TerrainMaterialShader.cpp @@ -72,12 +72,11 @@ void TerrainMaterialShader::Bind(BindParameters& params) bindMeta.CanSampleGBuffer = false; MaterialParams::Bind(params.ParamsLink, bindMeta); - // Setup material constants data + // Setup material constants { Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix); Matrix::Transpose(drawCall.World, materialData->WorldMatrix); Matrix::Transpose(view.View, materialData->ViewMatrix); - materialData->ViewPos = view.Position; materialData->ViewFar = view.Far; materialData->ViewDir = view.Direction; From 85167b2d4b8d7229f2d829b32a683f9212cde772 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 8 Feb 2021 15:45:39 +0100 Subject: [PATCH 347/419] Fix using Bezier curve for Transform --- Source/Engine/Animations/AnimationUtils.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Animations/AnimationUtils.h b/Source/Engine/Animations/AnimationUtils.h index c90afcea3..5df8cc2c0 100644 --- a/Source/Engine/Animations/AnimationUtils.h +++ b/Source/Engine/Animations/AnimationUtils.h @@ -70,7 +70,7 @@ namespace AnimationUtils FORCE_INLINE void GetTangent(const Quaternion& a, const Quaternion& b, float length, Quaternion& result) { const float oneThird = 1.0f / 3.0f; - Quaternion::Slerp(a, b, oneThird, result); + Quaternion::Slerp(a, b, length * oneThird, result); } template<> @@ -79,8 +79,8 @@ namespace AnimationUtils const float oneThird = 1.0f / 3.0f; const float oneThirdLength = length * oneThird; result.Translation = a.Translation + b.Translation * oneThirdLength; - Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation); - result.Scale = a.Scale + b.Scale * oneThirdLength; + Quaternion::Slerp(a.Orientation, b.Orientation, oneThirdLength, result.Orientation); + result.Scale = a.Scale + (b.Scale - a.Scale) * oneThirdLength; } template @@ -101,6 +101,14 @@ namespace AnimationUtils Quaternion::Slerp(a, b, t, result); } + template<> + FORCE_INLINE void Interpolate(const Transform& a, const Transform& b, float t, Transform& result) + { + Vector3::Lerp(a.Translation, b.Translation, t, result.Translation); + Quaternion::Slerp(a.Orientation, b.Orientation, t, result.Orientation); + Vector3::Lerp(a.Scale, b.Scale, t, result.Scale); + } + static void WrapTime(float& time, float start, float end, bool loop) { const float length = end - start; From c9860f21ec3bb4a25dfa35483533da496a3f8fb8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 8 Feb 2021 19:42:04 +0100 Subject: [PATCH 348/419] Fix scripting object issue --- Source/Engine/Scripting/Scripting.cpp | 26 +++++++++++++++++---- Source/Engine/Scripting/ScriptingObject.cpp | 4 ---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index ee2922754..c8c8184bd 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -495,7 +495,7 @@ void Scripting::Release() { auto obj = i->Value; #if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING - LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint32)obj.Ptr, String(obj.TypeName)); + LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName)); #endif obj->OnScriptingDispose(); } @@ -589,6 +589,24 @@ void Scripting::Reload(bool canTriggerSceneReload) MCore::GC::Collect(); MCore::GC::WaitForPendingFinalizers(); + // Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload) + _objectsLocker.Lock(); + { + const auto flaxModule = GetBinaryModuleFlaxEngine(); + for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i) + { + auto obj = i->Value; + if (obj->GetTypeHandle().Module == flaxModule) + continue; + +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName)); +#endif + obj->OnScriptingDispose(); + } + } + _objectsLocker.Unlock(); + // Unload all game modules LOG(Info, "Unloading game binary modules"); auto modules = BinaryModule::GetModules(); @@ -821,7 +839,7 @@ void Scripting::OnManagedInstanceDeleted(ScriptingObject* obj) if (_objectsDictionary.ContainsValue(obj)) { #if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING - LOG(Info, "[OnManagedInstanceDeleted] obj = 0x{0:x}, {1}", (uint32)obj, String(ScriptingObjectData(obj).TypeName)); + LOG(Info, "[OnManagedInstanceDeleted] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName)); #endif obj->OnManagedInstanceDeleted(); } @@ -877,7 +895,7 @@ void Scripting::RegisterObject(ScriptingObject* obj) #endif #if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING - LOG(Info, "[RegisterObject] obj = 0x{0:x}, {1}", (uint32)obj, String(ScriptingObjectData(obj).TypeName)); + LOG(Info, "[RegisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName)); #endif _objectsDictionary.Add(obj->GetID(), obj); } @@ -889,7 +907,7 @@ void Scripting::UnregisterObject(ScriptingObject* obj) //ASSERT(!obj->_id.IsValid() || _objectsDictionary.ContainsValue(obj)); #if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING - LOG(Info, "[UnregisterObject] obj = 0x{0:x}, {1}", (uint32)obj, String(ScriptingObjectData(obj).TypeName)); + LOG(Info, "[UnregisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName)); #endif _objectsDictionary.Remove(obj->GetID()); } diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 9d2320d7f..dd9cf57a8 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -276,10 +276,6 @@ void PersistentScriptingObject::OnManagedInstanceDeleted() _gcHandle = 0; } - // Unregister object - if (IsRegistered()) - UnregisterObject(); - // But do not delete itself } From 58350c87e252502f8191589659374dcfbdb11daa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 8 Feb 2021 20:20:39 +0100 Subject: [PATCH 349/419] Fix missing CharacterController bounds if controller is missing --- Source/Engine/Graphics/RenderTask.cpp | 3 +-- Source/Engine/Physics/Colliders/CharacterController.cpp | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index d30e8e37c..405e0fe45 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -283,11 +283,10 @@ Viewport SceneRenderTask::GetViewport() const GPUTextureView* SceneRenderTask::GetOutputView() const { - if (Output) + if (Output && Output->IsAllocated()) return Output->View(); if (SwapChain) return SwapChain->GetBackBufferView(); - CRASH; return nullptr; } diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index e0630573e..79410bdf4 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -361,6 +361,11 @@ void CharacterController::OnTransformChanged() UpdateScale(); UpdateBounds(); } + else if (!_controller) + { + _box = BoundingBox(_transform.Translation, _transform.Translation); + BoundingSphere::FromBox(_box, _sphere); + } } void CharacterController::Serialize(SerializeStream& stream, const void* otherObj) From 2b962e43893519e223547873cccdeb644a418d53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 9 Feb 2021 10:58:13 +0100 Subject: [PATCH 350/419] Fix invalid comment --- Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs index 3415501bd..32a408828 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs @@ -312,7 +312,7 @@ namespace Flax.Deps.Dependencies } // Get the source - //CloneGitRepoSingleBranch(root, "https://github.com/NVIDIAGameWorks/PhysX.git", "4.1"); + CloneGitRepoSingleBranch(root, "https://github.com/NVIDIAGameWorks/PhysX.git", "4.1"); foreach (var platform in options.Platforms) { From 311dad7b824d9b9c539aeb0bfd3c068e31a6ea20 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 9 Feb 2021 16:04:47 +0100 Subject: [PATCH 351/419] Add Spline Model --- .../MaterialTemplates/Deformable.shader | 32 +- Source/Editor/SceneGraph/Actors/SplineNode.cs | 29 ++ Source/Engine/Content/Assets/Material.cpp | 6 +- .../Materials/DeformableMaterialShader.cpp | 9 +- Source/Engine/Level/Actors/SplineModel.cpp | 473 ++++++++++++++++++ Source/Engine/Level/Actors/SplineModel.h | 116 +++++ 6 files changed, 639 insertions(+), 26 deletions(-) create mode 100644 Source/Engine/Level/Actors/SplineModel.cpp create mode 100644 Source/Engine/Level/Actors/SplineModel.h diff --git a/Content/Editor/MaterialTemplates/Deformable.shader b/Content/Editor/MaterialTemplates/Deformable.shader index 43afa566a..044f7184c 100644 --- a/Content/Editor/MaterialTemplates/Deformable.shader +++ b/Content/Editor/MaterialTemplates/Deformable.shader @@ -19,7 +19,7 @@ float3 ViewDir; float TimeParam; float4 ViewInfo; float4 ScreenSize; -float3 WorldInvScale; +float3 Dummy0; float WorldDeterminantSign; float MeshMinZ; float Segment; @@ -148,9 +148,17 @@ MaterialInput GetMaterialInput(PixelInput input) // Removes the scale vector from the local to world transformation matrix float3x3 RemoveScaleFromLocalToWorld(float3x3 localToWorld) { - localToWorld[0] *= WorldInvScale.x; - localToWorld[1] *= WorldInvScale.y; - localToWorld[2] *= WorldInvScale.z; + // Extract per axis scales from localToWorld transform + float scaleX = length(localToWorld[0]); + float scaleY = length(localToWorld[1]); + float scaleZ = length(localToWorld[2]); + float3 invScale = float3( + scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, + scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, + scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); + localToWorld[0] *= invScale.x; + localToWorld[1] *= invScale.y; + localToWorld[2] *= invScale.z; return localToWorld; } @@ -278,6 +286,7 @@ VertexOutput VS_SplineModel(ModelInput input) // Apply local transformation of the geometry before deformation float3 position = mul(float4(input.Position, 1), LocalMatrix).xyz; + float4x4 world = LocalMatrix; // Apply spline curve deformation float splineAlpha = saturate((position.z - MeshMinZ) / (MeshMaxZ - MeshMinZ)); @@ -285,9 +294,12 @@ VertexOutput VS_SplineModel(ModelInput input) position.z = splineAlpha; float3x4 splineMatrix = float3x4(SplineDeformation[splineIndex * 3], SplineDeformation[splineIndex * 3 + 1], SplineDeformation[splineIndex * 3 + 2]); position = mul(splineMatrix, float4(position, 1)); + float4x3 splineMatrixT = transpose(splineMatrix); + world = mul(world, float4x4(float4(splineMatrixT[0], 0), float4(splineMatrixT[1], 0), float4(splineMatrixT[2], 0), float4(splineMatrixT[3], 1))); // Compute world space vertex position output.Geometry.WorldPosition = mul(float4(position, 1), WorldMatrix).xyz; + world = mul(world, WorldMatrix); // Compute clip space position output.Position = mul(float4(output.Geometry.WorldPosition, 1), ViewProjectionMatrix); @@ -300,7 +312,7 @@ VertexOutput VS_SplineModel(ModelInput input) // Calculate tanget space to world space transformation matrix for unit vectors float3x3 tangentToLocal = CalcTangentToLocal(input); - float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)WorldMatrix); + float3x3 localToWorld = RemoveScaleFromLocalToWorld((float3x3)world); float3x3 tangentToWorld = mul(tangentToLocal, localToWorld); output.Geometry.WorldNormal = tangentToWorld[2]; output.Geometry.WorldTangent.xyz = tangentToWorld[0]; @@ -335,16 +347,6 @@ VertexOutput VS_SplineModel(ModelInput input) return output; } -// Vertex Shader function for Depth Pass -META_VS(true, FEATURE_LEVEL_ES2) -META_VS_IN_ELEMENT(POSITION, 0, R32G32B32_FLOAT, 0, 0, PER_VERTEX, 0, true) -float4 VS_Depth(ModelInput_PosOnly input) : SV_Position -{ - float3 worldPosition = mul(float4(input.Position.xyz, 1), WorldMatrix).xyz; - float4 position = mul(float4(worldPosition, 1), ViewProjectionMatrix); - return position; -} - #if USE_DITHERED_LOD_TRANSITION void ClipLODTransition(PixelInput input) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 179f3267e..1eeb55511 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Modules; using FlaxEngine; using FlaxEngine.Json; @@ -178,6 +179,11 @@ namespace FlaxEditor.SceneGraph.Actors } } + public override void OnContextMenu(ContextMenu contextMenu) + { + ParentNode.OnContextMenu(contextMenu); + } + public static SceneGraphNode Create(StateData state) { var data = JsonSerializer.Deserialize(state.State); @@ -239,6 +245,11 @@ namespace FlaxEditor.SceneGraph.Actors DebugDraw.DrawSphere(new BoundingSphere(pos, 5.0f), Color.YellowGreen, 0, false); } + public override void OnContextMenu(ContextMenu contextMenu) + { + ParentNode.OnContextMenu(contextMenu); + } + public override void OnDispose() { _node = null; @@ -292,6 +303,24 @@ namespace FlaxEditor.SceneGraph.Actors spline.AddSplineLocalPoint(new Vector3(0, 0, 100.0f)); } + /// + public override void OnContextMenu(ContextMenu contextMenu) + { + base.OnContextMenu(contextMenu); + + contextMenu.AddButton("Add spline model", OnAddSplineMode); + } + + private void OnAddSplineMode() + { + var actor = new SplineModel + { + StaticFlags = Actor.StaticFlags, + Transform = Actor.Transform, + }; + Editor.Instance.SceneEditing.Spawn(actor, Actor); + } + /// public override void OnDispose() { diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 2930aff24..4d1e5d283 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -390,12 +390,12 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) // Prepare auto& info = _shaderHeader.Material.Info; - const bool isSurfaceOrTerrain = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain; + const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable; const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage; const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle; const bool useTess = info.TessellationMode != TessellationMethod::None && - RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrain; + RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable; const bool useDistortion = (info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) && info.BlendMode != MaterialBlendMode::Opaque && @@ -457,7 +457,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options) options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] }); options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] }); options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] }); - options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrain && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] }); + options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] }); options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] }); #endif } diff --git a/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp index 5adb18470..dafe13d46 100644 --- a/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeformableMaterialShader.cpp @@ -26,7 +26,7 @@ PACK_STRUCT(struct DeformableMaterialShaderData { float TimeParam; Vector4 ViewInfo; Vector4 ScreenSize; - Vector3 WorldInvScale; + Vector3 Dummy0; float WorldDeterminantSign; float MeshMinZ; float Segment; @@ -79,13 +79,6 @@ void DeformableMaterialShader::Bind(BindParameters& params) materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds(); materialData->ViewInfo = view.ViewInfo; materialData->ScreenSize = view.ScreenSize; - const float scaleX = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13).Length(); - const float scaleY = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23).Length(); - const float scaleZ = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33).Length(); - materialData->WorldInvScale = Vector3( - scaleX > 0.00001f ? 1.0f / scaleX : 0.0f, - scaleY > 0.00001f ? 1.0f / scaleY : 0.0f, - scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f); materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign; materialData->Segment = drawCall.Deformable.Segment; materialData->ChunksPerSegment = drawCall.Deformable.ChunksPerSegment; diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp new file mode 100644 index 000000000..7380bf920 --- /dev/null +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -0,0 +1,473 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "SplineModel.h" +#include "Spline.h" +#include "Engine/Engine/Engine.h" +#include "Engine/Core/Math/Matrix3x4.h" +#include "Engine/Serialization/Serialization.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Graphics/RenderTools.h" +#include "Engine/Profiler/ProfilerCPU.h" +#if USE_EDITOR +#include "Editor/Editor.h" +#endif + +#define SPLINE_RESOLUTION 32.0f + +SplineModel::SplineModel(const SpawnParams& params) + : ModelInstanceActor(params) +{ + Model.Changed.Bind(this); + Model.Loaded.Bind(this); +} + +SplineModel::~SplineModel() +{ + SAFE_DELETE_GPU_RESOURCE(_deformationBuffer); + if (_deformationBufferData) + Allocator::Free(_deformationBufferData); +} + +float SplineModel::GetQuality() const +{ + return _quality; +} + +void SplineModel::SetQuality(float value) +{ + value = Math::Clamp(value, 0.0f, 100.0f); + if (Math::NearEqual(value, _quality)) + return; + _quality = value; + OnSplineUpdated(); +} + +float SplineModel::GetBoundsScale() const +{ + return _boundsScale; +} + +void SplineModel::SetBoundsScale(float value) +{ + if (Math::NearEqual(_boundsScale, value)) + return; + _boundsScale = value; + OnSplineUpdated(); +} + +int32 SplineModel::GetLODBias() const +{ + return static_cast(_lodBias); +} + +void SplineModel::SetLODBias(int32 value) +{ + _lodBias = static_cast(Math::Clamp(value, -100, 100)); +} + +int32 SplineModel::GetForcedLOD() const +{ + return static_cast(_forcedLod); +} + +void SplineModel::SetForcedLOD(int32 value) +{ + _forcedLod = static_cast(Math::Clamp(value, -1, 100)); +} + +void SplineModel::OnModelChanged() +{ + Entries.Release(); + + if (Model && !Model->IsLoaded()) + { + OnSplineUpdated(); + } +} + +void SplineModel::OnModelLoaded() +{ + Entries.SetupIfInvalid(Model); + + OnSplineUpdated(); +} + +void SplineModel::OnSplineUpdated() +{ + // Skip updates when actor is disabled or something is missing + if (!_spline || !Model || !Model->IsLoaded() || !IsActiveInHierarchy() || !IsDuringPlay() || _spline->GetSplinePointsCount() < 2) + { + _box = BoundingBox(_transform.Translation, _transform.Translation); + BoundingSphere::FromBox(_box, _sphere); + return; + } + PROFILE_CPU(); + + // Setup model instances over the spline segments + const auto& keyframes = _spline->Curve.GetKeyframes(); + const int32 segments = keyframes.Count() - 1; + const int32 chunksPerSegment = Math::Clamp(Math::CeilToInt(SPLINE_RESOLUTION * _quality), 2, 1024); + const float chunksPerSegmentInv = 1.0f / (float)chunksPerSegment; + const Transform splineTransform = _spline->GetTransform(); + _instances.Resize(segments, false); + BoundingBox localModelBounds; + { + auto& meshes = Model->LODs[0].Meshes; + Vector3 tmp, min = Vector3::Maximum, max = Vector3::Minimum; + Vector3 corners[8]; + for (int32 j = 0; j < meshes.Count(); j++) + { + const auto& mesh = meshes[j]; + mesh.GetCorners(corners); + + for (int32 i = 0; i < 8; i++) + { + //Vector3::Transform(corners[i], localTransform, tmp); + //_localTransform.LocalToWorld(corners[i], tmp); + + // Transform mesh corner using local spline model transformation but use double-precision to prevent issues when rotating model + tmp = corners[i] * _localTransform.Scale; + double rotation[4] = { (double)_localTransform.Orientation.X, (double)_localTransform.Orientation.Y, (double)_localTransform.Orientation.Z, (double)_localTransform.Orientation.W }; + const double length = sqrt(rotation[0] * rotation[0] + rotation[1] * rotation[1] + rotation[2] * rotation[2] + rotation[3] * rotation[3]); + const double inv = 1.0 / length; + rotation[0] *= inv; + rotation[1] *= inv; + rotation[2] *= inv; + rotation[3] *= inv; + double pos[3] = { (double)tmp.X, (double)tmp.Y, (double)tmp.Z }; + const double x = rotation[0] + rotation[0]; + const double y = rotation[1] + rotation[1]; + const double z = rotation[2] + rotation[2]; + const double wx = rotation[3] * x; + const double wy = rotation[3] * y; + const double wz = rotation[3] * z; + const double xx = rotation[0] * x; + const double xy = rotation[0] * y; + const double xz = rotation[0] * z; + const double yy = rotation[1] * y; + const double yz = rotation[1] * z; + const double zz = rotation[2] * z; + tmp = Vector3( + (float)(pos[0] * (1.0 - yy - zz) + pos[1] * (xy - wz) + pos[2] * (xz + wy)), + (float)(pos[0] * (xy + wz) + pos[1] * (1.0 - xx - zz) + pos[2] * (yz - wx)), + (float)(pos[0] * (xz - wy) + pos[1] * (yz + wx) + pos[2] * (1.0 - xx - yy))); + Vector3::Add(tmp, _localTransform.Translation, tmp); + + min = Vector3::Min(min, tmp); + max = Vector3::Max(max, tmp); + } + } + localModelBounds = BoundingBox(min, max); + } + _meshMinZ = localModelBounds.Minimum.Z; + _meshMaxZ = localModelBounds.Maximum.Z; + Transform chunkLocal, chunkWorld, leftTangent, rightTangent; + Array segmentPoints; + segmentPoints.Resize(chunksPerSegment); + for (int32 segment = 0; segment < segments; segment++) + { + auto& instance = _instances[segment]; + const auto& start = keyframes[segment]; + const auto& end = keyframes[segment + 1]; + const float length = end.Time - start.Time; + AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent); + AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent); + + // Find maximum scale over the segment spline and collect the segment positions for bounds + segmentPoints.Clear(); + segmentPoints.Add(end.Value.Translation); + float maxScale = end.Value.Scale.GetAbsolute().MaxValue(); + for (int32 chunk = 0; chunk < chunksPerSegment; chunk++) + { + const float alpha = (float)chunk * chunksPerSegmentInv; + AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, chunkLocal); + splineTransform.LocalToWorld(chunkLocal, chunkWorld); + segmentPoints.Add(chunkWorld.Translation); + maxScale = Math::Max(maxScale, chunkWorld.Scale.GetAbsolute().MaxValue()); + } + maxScale = Math::Max(maxScale, _localTransform.Scale.GetAbsolute().MaxValue()); + BoundingSphere::FromPoints(segmentPoints.Get(), segmentPoints.Count(), instance.Sphere); + instance.Sphere.Center += _localTransform.Translation; + instance.Sphere.Radius *= maxScale * _boundsScale; + } + + // Update deformation buffer during next drawing + _deformationDirty = true; + + // Update bounds + _sphere = _instances.First().Sphere; + for (int32 i = 1; i < _instances.Count(); i++) + BoundingSphere::Merge(_sphere, _instances[i].Sphere, _sphere); + BoundingBox::FromSphere(_sphere, _box); +} + +void SplineModel::UpdateDeformationBuffer() +{ + PROFILE_CPU(); + + // Deformation buffer contains precomputed matrices for each chunk of the spline segment (packed with transposed float3x4 matrix) + _deformationDirty = false; + if (!_deformationBuffer) + _deformationBuffer = GPUDevice::Instance->CreateBuffer(GetName()); + const auto& keyframes = _spline->Curve.GetKeyframes(); + const int32 segments = keyframes.Count() - 1; + const int32 chunksPerSegment = Math::Clamp(Math::CeilToInt(SPLINE_RESOLUTION * _quality), 2, 1024); + const int32 count = (chunksPerSegment * segments + 1) * 3; + const uint32 size = count * sizeof(Vector4); + if (_deformationBuffer->GetSize() != size) + { + if (_deformationBufferData) + { + Allocator::Free(_deformationBufferData); + _deformationBufferData = nullptr; + } + if (_deformationBuffer->Init(GPUBufferDescription::Typed(count, PixelFormat::R32G32B32A32_Float, false, IsTransformStatic() ? GPUResourceUsage::Default : GPUResourceUsage::Dynamic))) + { + LOG(Error, "Failed to initialize the spline model {0} deformation buffer.", ToString()); + return; + } + } + if (!_deformationBufferData) + _deformationBufferData = Allocator::Allocate(size); + _chunksPerSegment = (float)chunksPerSegment; + + // Update pre-calculated matrices for spline chunks + auto ptr = (Matrix3x4*)_deformationBufferData; + const float chunksPerSegmentInv = 1.0f / (float)chunksPerSegment; + Matrix m; + Transform transform, leftTangent, rightTangent; + for (int32 segment = 0; segment < segments; segment++) + { + auto& instance = _instances[segment]; + const auto& start = keyframes[segment]; + const auto& end = keyframes[segment + 1]; + const float length = end.Time - start.Time; + AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent); + AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent); + for (int32 chunk = 0; chunk < chunksPerSegment; chunk++) + { + const float alpha = (float)chunk * chunksPerSegmentInv; + + // Evaluate transformation at the curve + AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform); + + // Apply spline direction (from position 1st derivative) + Vector3 direction; + AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction); + direction.Normalize(); + Quaternion orientation; + if (direction.IsZero()) + orientation = Quaternion::Identity; + else if (Vector3::Dot(direction, Vector3::Up) >= 0.999f) + Quaternion::RotationAxis(Vector3::Left, PI_HALF, orientation); + else + Quaternion::LookRotation(direction, Vector3::Cross(Vector3::Cross(direction, Vector3::Up), direction), orientation); + transform.Orientation = orientation * transform.Orientation; + + // Write transform into deformation buffer + transform.GetWorld(m); + ptr->SetMatrixTranspose(m); + ptr++; + } + instance.RotDeterminant = m.RotDeterminant(); + } + + // Add last transformation to prevent issues when sampling spline deformation buffer with alpha=1 + { + const auto& start = keyframes[segments - 1]; + const auto& end = keyframes[segments]; + const float length = end.Time - start.Time; + const float alpha = 1.0f - ZeroTolerance; // Offset to prevent zero derivative at the end of the curve + AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent); + AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent); + AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform); + Vector3 direction; + AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction); + direction.Normalize(); + Quaternion orientation; + if (direction.IsZero()) + orientation = Quaternion::Identity; + else if (Vector3::Dot(direction, Vector3::Up) >= 0.999f) + Quaternion::RotationAxis(Vector3::Left, PI_HALF, orientation); + else + Quaternion::LookRotation(direction, Vector3::Cross(Vector3::Cross(direction, Vector3::Up), direction), orientation); + transform.Orientation = orientation * transform.Orientation; + transform.GetWorld(m); + ptr->SetMatrixTranspose(m); + } + + // Flush data with GPU + auto context = GPUDevice::Instance->GetMainContext(); + context->UpdateBuffer(_deformationBuffer, _deformationBufferData, size); + + // Static splines are rarely updated so release scratch memory + if (IsTransformStatic()) + { + Allocator::Free(_deformationBufferData); + _deformationBufferData = nullptr; + } +} + +void SplineModel::OnParentChanged() +{ + if (_spline) + { + _spline->SplineUpdated.Unbind(this); + } + + // Base + Actor::OnParentChanged(); + + _spline = Cast(_parent); + if (_spline) + { + _spline->SplineUpdated.Bind(this); + } + + OnSplineUpdated(); +} + +bool SplineModel::HasContentLoaded() const +{ + return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded(); +} + +void SplineModel::Draw(RenderContext& renderContext) +{ + const DrawPass actorDrawModes = (DrawPass)(DrawModes & renderContext.View.Pass); + if (!_spline || !Model || !Model->IsLoaded() || !Model->CanBeRendered() || actorDrawModes == DrawPass::None) + return; + auto model = Model.Get(); + if (!Entries.IsValidFor(model)) + Entries.Setup(model); + + // Build mesh deformation buffer for the whole spline + if (_deformationDirty) + UpdateDeformationBuffer(); + + // Draw all segments + DrawCall drawCall; + drawCall.InstanceCount = 1; + drawCall.IndirectArgsBuffer = nullptr; + drawCall.IndirectArgsOffset = 0; + drawCall.Deformable.SplineDeformation = _deformationBuffer; + drawCall.Deformable.ChunksPerSegment = _chunksPerSegment; + drawCall.Deformable.MeshMinZ = _meshMinZ; + drawCall.Deformable.MeshMaxZ = _meshMaxZ; + drawCall.Deformable.GeometrySize = _box.GetSize(); + drawCall.PerInstanceRandom = GetPerInstanceRandom(); + _localTransform.GetWorld(drawCall.Deformable.LocalMatrix); + const Transform splineTransform = _spline->GetTransform(); + splineTransform.GetWorld(drawCall.World); + drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation(); + const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant(); + for (int32 segment = 0; segment < _instances.Count(); segment++) + { + auto& instance = _instances[segment]; + if (!renderContext.View.CullingFrustum.Intersects(instance.Sphere)) + continue; + drawCall.Deformable.Segment = (float)segment; + + // Select a proper LOD index (model may be culled) + int32 lodIndex; + if (_forcedLod != -1) + { + lodIndex = _forcedLod; + } + else + { + lodIndex = RenderTools::ComputeModelLOD(model, instance.Sphere.Center, instance.Sphere.Radius, renderContext); + if (lodIndex == -1) + continue; + } + lodIndex += _lodBias + renderContext.View.ModelLODBias; + lodIndex = model->ClampLODIndex(lodIndex); + + // Draw + const auto& lod = model->LODs[lodIndex]; + for (int32 i = 0; i < lod.Meshes.Count(); i++) + { + const auto mesh = &lod.Meshes[i]; + + // Cache data + const auto& entry = Entries[mesh->GetMaterialSlotIndex()]; + if (!entry.Visible || !mesh->IsInitialized()) + continue; + const MaterialSlot& slot = model->MaterialSlots[mesh->GetMaterialSlotIndex()]; + + // Check if skip rendering + const auto shadowsMode = static_cast(entry.ShadowsMode & slot.ShadowsMode); + const auto drawModes = static_cast(actorDrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode)); + if (drawModes == DrawPass::None) + continue; + + // Select material + MaterialBase* material; + if (entry.Material && entry.Material->IsLoaded()) + material = entry.Material; + else if (slot.Material && slot.Material->IsLoaded()) + material = slot.Material; + else + material = nullptr; + if (!material || !material->IsDeformable()) + continue; + + // Submit draw call + mesh->GetDrawCallGeometry(drawCall); + drawCall.Material = material; + drawCall.WorldDeterminantSign = Math::FloatSelect(worldDeterminantSign * instance.RotDeterminant, 1, -1); + renderContext.List->AddDrawCall(drawModes, _staticFlags, drawCall, entry.ReceiveDecals); + } + } +} + +void SplineModel::DrawGeneric(RenderContext& renderContext) +{ + Draw(renderContext); +} + +bool SplineModel::IntersectsItself(const Ray& ray, float& distance, Vector3& normal) +{ + return false; +} + +void SplineModel::Serialize(SerializeStream& stream, const void* otherObj) +{ + // Base + ModelInstanceActor::Serialize(stream, otherObj); + + SERIALIZE_GET_OTHER_OBJ(SplineModel); + + SERIALIZE_MEMBER(Quality, _quality); + SERIALIZE_MEMBER(BoundsScale, _boundsScale); + SERIALIZE_MEMBER(LODBias, _lodBias); + SERIALIZE_MEMBER(ForcedLOD, _forcedLod); + SERIALIZE(Model); + SERIALIZE(DrawModes); + + stream.JKEY("Buffer"); + stream.Object(&Entries, other ? &other->Entries : nullptr); +} + +void SplineModel::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + // Base + ModelInstanceActor::Deserialize(stream, modifier); + + DESERIALIZE_MEMBER(Quality, _quality); + DESERIALIZE_MEMBER(BoundsScale, _boundsScale); + DESERIALIZE_MEMBER(LODBias, _lodBias); + DESERIALIZE_MEMBER(ForcedLOD, _forcedLod); + DESERIALIZE(Model); + DESERIALIZE(DrawModes); + + Entries.DeserializeIfExists(stream, "Buffer", modifier); +} + +void SplineModel::OnTransformChanged() +{ + // Base + ModelInstanceActor::OnTransformChanged(); + + OnSplineUpdated(); +} diff --git a/Source/Engine/Level/Actors/SplineModel.h b/Source/Engine/Level/Actors/SplineModel.h new file mode 100644 index 000000000..a7d53af49 --- /dev/null +++ b/Source/Engine/Level/Actors/SplineModel.h @@ -0,0 +1,116 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "ModelInstanceActor.h" +#include "Engine/Content/Assets/Model.h" + +class Spline; + +/// +/// Renders model over the spline segments. +/// +API_CLASS() class FLAXENGINE_API SplineModel : public ModelInstanceActor +{ +DECLARE_SCENE_OBJECT(SplineModel); +private: + + struct Instance + { + BoundingSphere Sphere; + float RotDeterminant; + }; + + float _boundsScale = 1.0f, _quality = 1.0f; + char _lodBias = 0; + char _forcedLod = -1; + bool _deformationDirty = false; + Array _instances; + Spline* _spline = nullptr; + GPUBuffer* _deformationBuffer = nullptr; + void* _deformationBufferData = nullptr; + float _chunksPerSegment, _meshMinZ, _meshMaxZ; + +public: + + ~SplineModel(); + + /// + /// The model asset to draw. + /// + API_FIELD(Attributes="EditorOrder(20), DefaultValue(null), EditorDisplay(\"Model\")") + AssetReference Model; + + /// + /// The draw passes to use for rendering this object. + /// + API_FIELD(Attributes="EditorOrder(15), DefaultValue(DrawPass.Default), EditorDisplay(\"Model\")") + DrawPass DrawModes = DrawPass::Default; + + /// + /// Gets the spline model quality scale. Higher values improve the spline representation (better tessellation) but reduce performance. + /// + API_PROPERTY(Attributes="EditorOrder(11), DefaultValue(1.0f), EditorDisplay(\"Model\"), Limit(0.1f, 100.0f, 0.1f)") + float GetQuality() const; + + /// + /// Sets the spline model quality scale. Higher values improve the spline representation (better tessellation) but reduce performance. + /// + API_PROPERTY() void SetQuality(float value); + + /// + /// Gets the model bounds scale. It is useful when using Position Offset to animate the vertices of the object outside of its bounds. + /// + API_PROPERTY(Attributes="EditorOrder(12), DefaultValue(1.0f), EditorDisplay(\"Model\"), Limit(0, 10.0f, 0.1f)") + float GetBoundsScale() const; + + /// + /// Sets the model bounds scale. It is useful when using Position Offset to animate the vertices of the object outside of its bounds. + /// + API_PROPERTY() void SetBoundsScale(float value); + + /// + /// Gets the model Level Of Detail bias value. Allows to increase or decrease rendered model quality. + /// + API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Model\", \"LOD Bias\")") + int32 GetLODBias() const; + + /// + /// Sets the model Level Of Detail bias value. Allows to increase or decrease rendered model quality. + /// + API_PROPERTY() void SetLODBias(int32 value); + + /// + /// Gets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature. + /// + API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Model\", \"Forced LOD\")") + int32 GetForcedLOD() const; + + /// + /// Sets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature. + /// + API_PROPERTY() void SetForcedLOD(int32 value); + +private: + + void OnModelChanged(); + void OnModelLoaded(); + void OnSplineUpdated(); + void UpdateDeformationBuffer(); + +public: + + // [ModelInstanceActor] + bool HasContentLoaded() const override; + void Draw(RenderContext& renderContext) override; + void DrawGeneric(RenderContext& renderContext) override; + bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override; + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void OnParentChanged() override; + +protected: + + // [ModelInstanceActor] + void OnTransformChanged() override; +}; From 15c4b7df54ae72c1fe59b9f02df73a49b56d7e82 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 9 Feb 2021 16:08:51 +0100 Subject: [PATCH 352/419] Update materials --- Content/Editor/Camera/M_Camera.flax | 4 ++-- Content/Editor/CubeTexturePreviewMaterial.flax | 4 ++-- Content/Editor/DefaultFontMaterial.flax | 4 ++-- Content/Editor/Gizmo/FoliageBrushMaterial.flax | 4 ++-- Content/Editor/Gizmo/Material.flax | 4 ++-- Content/Editor/Gizmo/MaterialWire.flax | 4 ++-- Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax | 4 ++-- Content/Editor/Highlight Material.flax | 4 ++-- Content/Editor/Icons/IconsMaterial.flax | 4 ++-- Content/Editor/Particles/Particle Material Color.flax | 4 ++-- Content/Editor/Particles/Smoke Material.flax | 4 ++-- Content/Editor/Terrain/Circle Brush Material.flax | 4 ++-- Content/Editor/Terrain/Highlight Terrain Material.flax | 4 ++-- Content/Editor/Wires Debug Material.flax | 4 ++-- Content/Engine/DefaultMaterial.flax | 4 ++-- Content/Engine/DefaultTerrainMaterial.flax | 4 ++-- Content/Engine/SingleColorMaterial.flax | 4 ++-- Content/Engine/SkyboxMaterial.flax | 4 ++-- 18 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index 9e2154ed9..768cf1986 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:919822dc6234c58fe9efa48676845cb3f780cc2ad3a8b8edcb2badb6cfb8183c -size 30119 +oid sha256:2d62539b39925798a3d526d20b3fc4e30c268629427c3920e1ece9f4c446ec45 +size 29995 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index c16307620..f4047b652 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16b75ef1da889ff19d3f7cbd52073358cd0a433cd84912bd9802e3c21fcf90f2 -size 31676 +oid sha256:33afbfa5c2b3e24499de14fdabf87bdc809ce703f859b718241e203115db3961 +size 31552 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index 9fc057b01..3cb4ff8dd 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7addee999e9325dac84f0ed70a817dbbdd549b84dfa7d0260403cca89abb399a -size 30200 +oid sha256:bdca03c927766e9d4bb5409e2507b3a9d22f13342fad159182fdd8da122312cb +size 30076 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index 8b33f3404..8c87ae3b3 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:792fd8ca00b3ad5b06a43a66cbca624e96af845c157519bf6038c08ae3d6dcd6 -size 34661 +oid sha256:5129d65fa3a24af617b25b9058294b89e65290581b2992e77ba46a12ade7a8ee +size 34537 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index fb9729627..6bc4e8f93 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28de48669af8729327e4d3797076c1c28674ec86edf90d365d5f7426d7f23f01 -size 31187 +oid sha256:1e470f838ed5ff78b311169f974a3832b387730be2030dc67e3ddf0d601dc3c0 +size 31063 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index d0a37c641..2b85a3941 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e00d2943edb8ab1a1cdd2c7adfabc5e418fd9c0f414ed54a97ba33a3c1b23aac -size 30400 +oid sha256:083633783ea5733720803b285a2378a6130fec506b9a2abe93b146cdf736d0ed +size 30276 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index 5dbab8006..1314e5596 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5c14383f163282e144d9af8d89a817ea4f450ef200164dcd6f4af1406936d4a -size 31134 +oid sha256:770a2498f0a712ae1705fd74204c62ded862b73613ed8cec70974bee0802350a +size 31010 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index 48ec19fdb..824fbf090 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bf05e8133b46a764b6bda2a0b80a9a7dd44fcf3a9169e4da7a99d72fade6c67 -size 28995 +oid sha256:50fcefc75caa48fe73ed23bf3fe2c023f9a6ac146d3a3bc16b4386a39aaa56f7 +size 28871 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index 30c96a8ef..e7b91ff38 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6820dc041a4dc8fa66ac4dbacaf28efffb9a7083ac16b53850d8bfdb3b4fe80a -size 28927 +oid sha256:aae6e991a3623c9c84165c30fceab674d4173bb27303a164bd3dcef61f30654d +size 28803 diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index 577b1689e..e2fdb046b 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15b814eb7cf2a263562004f3d440835ef50e6cf11be211c8b0171040ce87972d -size 29326 +oid sha256:14d75ef13449f9ed9a6e18ee82fe59fe4e83ac519b62b1e87aaadabd89673995 +size 29232 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index a29ea1649..66c3466e8 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f0df0dea34c8105df1f26a60f5ba013e28d125297b1d630b62d6333ce3ec966 -size 35425 +oid sha256:586e1e4620df43c638ca5a059dc1b705a82b47f398c973dd526e3f58570cf645 +size 35331 diff --git a/Content/Editor/Terrain/Circle Brush Material.flax b/Content/Editor/Terrain/Circle Brush Material.flax index 08aed3e07..30093ee5e 100644 --- a/Content/Editor/Terrain/Circle Brush Material.flax +++ b/Content/Editor/Terrain/Circle Brush Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df1d754dc7a6046740e50ba20bc90278aa4894d83d01f41d7fdb50455e6d7763 -size 27923 +oid sha256:2ce01e5aeaf46ed0ed2285b2b6c5b0d8f0bebdb0a7f5fbd246eef22a8ead21fa +size 27520 diff --git a/Content/Editor/Terrain/Highlight Terrain Material.flax b/Content/Editor/Terrain/Highlight Terrain Material.flax index 8e39ed806..a942d7848 100644 --- a/Content/Editor/Terrain/Highlight Terrain Material.flax +++ b/Content/Editor/Terrain/Highlight Terrain Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db149ec425c1baf245356eff3a819c3c06e857550e86d419c7bd70c55bde4b94 -size 21602 +oid sha256:3f8b40c2622b1422ae062bafec002cd120e160f2ed56aab10e3390ba3dbd3a55 +size 21199 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index 2839044c1..0b90a7474 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ce7aa7e754b27beda160a78e0636a6d6e12f99c59c127ca0557fc3d5a75285e -size 28995 +oid sha256:9563e2a8c60bac96093c614dde7a581bc5eb1ce6f7da00339f4b8f2f1607c2a4 +size 28871 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index 2472013e3..a746a0e4e 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88a4aa4b72322807ce9fcf30faca1d2e20ae78a65bdefd29ae16bf2596d93d8f -size 31898 +oid sha256:77076bc1e501bc2f630f559dfb699c965d2d95c5a611b4619a2819bc1f6eb8ca +size 31774 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index 9104a5a20..c7fbfec0f 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c82750f189ac803962c8b6d885b54aa3332af7249d4a53c00a6e530161d157e7 -size 23373 +oid sha256:909a9b45c735f3356d68885f6d294cb0627f5107316edef32b4b56328e8a0353 +size 23279 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index fe51af1bb..2aceb44be 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0569cdb10e6c832ba0d741144e54e92c31e25b9e8555743ae21c6a1bd8cc784e -size 30199 +oid sha256:075200d4b5a1353b463aff9c019212bea19aaee6ff38e34a3ddefceb816c00ec +size 30075 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index f099cb9e4..7a7822ac7 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a29b3b287910fecb955b5627bca91a202cb1663f78ea642e4dfde911add86ee6 -size 31420 +oid sha256:b58ddef0b73414d33f88a4e15df151b0a95a1093116b3d1e194d9b781ff09051 +size 31296 From d4f959b68132c2cd7df98f2f3cd90ea5a43c5c46 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 9 Feb 2021 16:46:10 +0100 Subject: [PATCH 353/419] Add support for Deformable material preview --- .../Viewport/Previews/MaterialPreview.cs | 27 ++++++++++++++++++- Source/Engine/Level/Actors/SplineModel.cpp | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index 41275ef9a..4f7d71b18 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -35,6 +35,8 @@ namespace FlaxEditor.Viewport.Previews private StaticModel _previewModel; private Decal _decal; private Terrain _terrain; + private Spline _spline; + private SplineModel _splineModel; private ParticleEffect _particleEffect; private MaterialBase _particleEffectMaterial; private ParticleEmitter _particleEffectEmitter; @@ -140,6 +142,7 @@ namespace FlaxEditor.Viewport.Previews MaterialBase guiMaterial = null; MaterialBase terrainMaterial = null; MaterialBase particleMaterial = null; + MaterialBase deformableMaterial = null; bool usePreviewActor = true; if (_material != null) { @@ -173,7 +176,8 @@ namespace FlaxEditor.Viewport.Previews particleMaterial = _material; break; case MaterialDomain.Deformable: - // TODO: preview Deformable material (eg. by using Spline with Spline Model) + usePreviewActor = false; + deformableMaterial = _material; break; default: throw new ArgumentOutOfRangeException(); } @@ -282,6 +286,25 @@ namespace FlaxEditor.Viewport.Previews } } } + + // Deformable + if (deformableMaterial && _spline == null) + { + _spline = new Spline(); + _spline.AddSplineLocalPoint(new Vector3(0, 0, -50.0f), false); + _spline.AddSplineLocalPoint(new Vector3(0, 0, 50.0f)); + _splineModel = new SplineModel + { + Scale = new Vector3(0.45f), + Parent = _spline, + }; + Task.AddCustomActor(_spline); + } + if (_splineModel != null) + { + _splineModel.Model = _previewModel.Model; + _splineModel.SetMaterial(0, deformableMaterial); + } } /// @@ -298,6 +321,8 @@ namespace FlaxEditor.Viewport.Previews Object.Destroy(ref _previewModel); Object.Destroy(ref _decal); Object.Destroy(ref _terrain); + Object.Destroy(ref _spline); + Object.Destroy(ref _splineModel); Object.Destroy(ref _particleEffect); Object.Destroy(ref _particleEffectEmitter); Object.Destroy(ref _particleEffectSystem); diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 7380bf920..69fcf90bc 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -95,7 +95,7 @@ void SplineModel::OnModelLoaded() void SplineModel::OnSplineUpdated() { // Skip updates when actor is disabled or something is missing - if (!_spline || !Model || !Model->IsLoaded() || !IsActiveInHierarchy() || !IsDuringPlay() || _spline->GetSplinePointsCount() < 2) + if (!_spline || !Model || !Model->IsLoaded() || !IsActiveInHierarchy() || _spline->GetSplinePointsCount() < 2) { _box = BoundingBox(_transform.Translation, _transform.Translation); BoundingSphere::FromBox(_box, _sphere); From cb6b96dea6ef5bdd65f04e8a5f54cb785544a11d Mon Sep 17 00:00:00 2001 From: Vizepi Date: Tue, 9 Feb 2021 18:08:50 +0100 Subject: [PATCH 354/419] [C++] Added FLAXENGINE_API macro on SettingsBase class so static symbol are retrieved by linker --- Source/Engine/Core/Config/Settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Config/Settings.h b/Source/Engine/Core/Config/Settings.h index 0380c96b3..e21e558fc 100644 --- a/Source/Engine/Core/Config/Settings.h +++ b/Source/Engine/Core/Config/Settings.h @@ -9,7 +9,7 @@ /// /// Base class for all global settings containers for the engine. Helps to apply, store and expose properties to c#. /// -class SettingsBase +class FLAXENGINE_API SettingsBase { public: From 6673d70d0acbcd0519005a766ae842fd14b3576c Mon Sep 17 00:00:00 2001 From: Vizepi Date: Tue, 9 Feb 2021 18:20:20 +0100 Subject: [PATCH 355/419] [C++] Add missing header forcing client code to include two headers instead of one --- Source/Engine/Core/Config/LayersTagsSettings.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Core/Config/LayersTagsSettings.h b/Source/Engine/Core/Config/LayersTagsSettings.h index 4ed9394fb..c01f915aa 100644 --- a/Source/Engine/Core/Config/LayersTagsSettings.h +++ b/Source/Engine/Core/Config/LayersTagsSettings.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Core/Config/Settings.h" +#include "Engine/Serialization/Json.h" /// /// Layers and objects tags settings. From 73efa35008e8aa2dbca96963b4d09360d844d3fc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 9 Feb 2021 23:31:23 +0100 Subject: [PATCH 356/419] Add `Level.GetActors` and `Level.GetScripts` --- Source/Engine/Level/Level.cpp | 48 +++++++++++++++++++++++++++-------- Source/Engine/Level/Level.cs | 28 ++++++++++++++++++++ Source/Engine/Level/Level.h | 14 ++++++++++ 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 04918f24b..219893012 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -1326,12 +1326,8 @@ Actor* Level::FindActor(const MClass* type) CHECK_RETURN(type, nullptr); Actor* result = nullptr; ScopeLock lock(ScenesLock); - for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++) - { result = Scenes[i]->FindActor(type); - } - return result; } @@ -1340,25 +1336,57 @@ Script* Level::FindScript(const MClass* type) CHECK_RETURN(type, nullptr); Script* result = nullptr; ScopeLock lock(ScenesLock); - for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++) - { result = Scenes[i]->FindScript(type); + return result; +} + +namespace +{ + void GetActors(const MClass* type, Actor* actor, Array& result) + { + if (actor->GetClass()->IsSubClassOf(type)) + result.Add(actor); + for (auto child : actor->Children) + GetActors(type, child, result); } + void GetScripts(const MClass* type, Actor* actor, Array& result) + { + for (auto script : actor->Scripts) + if (script->GetClass()->IsSubClassOf(type)) + result.Add(script); + for (auto child : actor->Children) + GetScripts(type, child, result); + } +} + +Array Level::GetActors(const MClass* type) +{ + Array result; + CHECK_RETURN(type, result); + ScopeLock lock(ScenesLock); + for (int32 i = 0; i < Scenes.Count(); i++) + ::GetActors(type, Scenes[i], result); + return result; +} + +Array Level::GetScripts(const MClass* type) +{ + Array result; + CHECK_RETURN(type, result); + ScopeLock lock(ScenesLock); + for (int32 i = 0; i < Scenes.Count(); i++) + ::GetScripts(type, Scenes[i], result); return result; } Scene* Level::FindScene(const Guid& id) { ScopeLock lock(ScenesLock); - for (int32 i = 0; i < Scenes.Count(); i++) - { if (Scenes[i]->GetID() == id) return Scenes[i]; - } - return nullptr; } diff --git a/Source/Engine/Level/Level.cs b/Source/Engine/Level/Level.cs index 6232d2e54..1eafe08d5 100644 --- a/Source/Engine/Level/Level.cs +++ b/Source/Engine/Level/Level.cs @@ -77,5 +77,33 @@ namespace FlaxEngine { return FindActor(id) as T; } + + /// + /// Finds all the scripts of the given type in all the loaded scenes. + /// + /// Type of the object. + /// Found scripts list. + public static T[] GetScripts() where T : Script + { + var scripts = GetScripts(typeof(T)); + var result = new T[scripts.Length]; + for (int i = 0; i < scripts.Length; i++) + result[i] = scripts[i] as T; + return result; + } + + /// + /// Finds all the actors of the given type in all the loaded scenes. + /// + /// Type of the object. + /// Found actors list. + public static T[] GetActors() where T : Actor + { + var actors = GetActors(typeof(T)); + var result = new T[actors.Length]; + for (int i = 0; i < actors.Length; i++) + result[i] = actors[i] as T; + return result; + } } } diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h index 0a10b58d2..5e274978a 100644 --- a/Source/Engine/Level/Level.h +++ b/Source/Engine/Level/Level.h @@ -377,6 +377,20 @@ public: return (T*)FindScript(T::GetStaticClass()); } + /// + /// Finds all the actors of the given type in all the loaded scenes. + /// + /// Type of the actor to search for. Includes any actors derived from the type. + /// Found actors list. + API_FUNCTION() static Array GetActors(const MClass* type); + + /// + /// Finds all the scripts of the given type in all the loaded scenes. + /// + /// Type of the script to search for. Includes any scripts derived from the type. + /// Found scripts list. + API_FUNCTION() static Array GetScripts(const MClass* type); + /// /// Tries to find scene with given ID. /// From 191694725ad1cca2a0dd046eba8dfa6659aceafb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 11:12:05 +0100 Subject: [PATCH 357/419] Add `SoftObjectReference` for lazy references to assets and objects --- Source/Engine/Scripting/Object.cs | 5 + .../Engine/Scripting/SoftObjectReference.cs | 80 +++++ Source/Engine/Scripting/SoftObjectReference.h | 327 ++++++++++++++++++ Source/Engine/Serialization/JsonSerializer.cs | 33 ++ Source/Engine/Serialization/Serialization.h | 22 ++ .../Bindings/BindingsGenerator.CSharp.cs | 12 +- .../Bindings/BindingsGenerator.Cpp.cs | 12 +- 7 files changed, 479 insertions(+), 12 deletions(-) create mode 100644 Source/Engine/Scripting/SoftObjectReference.cs create mode 100644 Source/Engine/Scripting/SoftObjectReference.h diff --git a/Source/Engine/Scripting/Object.cs b/Source/Engine/Scripting/Object.cs index 1c649ba9b..a4e2424e9 100644 --- a/Source/Engine/Scripting/Object.cs +++ b/Source/Engine/Scripting/Object.cs @@ -205,6 +205,11 @@ namespace FlaxEngine return obj?.__unmanagedPtr ?? IntPtr.Zero; } + internal static IntPtr GetUnmanagedPtr(SoftObjectReference reference) + { + return GetUnmanagedPtr(reference.Get()); + } + /// public override int GetHashCode() { diff --git a/Source/Engine/Scripting/SoftObjectReference.cs b/Source/Engine/Scripting/SoftObjectReference.cs new file mode 100644 index 000000000..df098d02a --- /dev/null +++ b/Source/Engine/Scripting/SoftObjectReference.cs @@ -0,0 +1,80 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// The scripting object soft reference. Objects gets referenced on use (ID reference is resolving it). + /// + public struct SoftObjectReference : IComparable, IComparable + { + private Guid _id; + private Object _object; + + /// + /// Gets or sets the object identifier. + /// + public Guid ID + { + get => _id; + set + { + if (_id == value) + return; + _id = value; + _object = null; + } + } + + /// + /// Gets the object reference. + /// + /// The object type. + /// The resolved object or null. + public T Get() where T : Object + { + if (!_object) + _object = Object.Find(ref _id, typeof(T)); + return _object as T; + } + + /// + /// Sets the object reference. + /// + /// The object. + public void Set(Object obj) + { + _object = obj; + _id = obj?.ID ?? Guid.Empty; + } + + /// + public override string ToString() + { + if (_object) + return _object.ToString(); + return _id.ToString(); + } + + /// + public override int GetHashCode() + { + return _id.GetHashCode(); + } + + /// + public int CompareTo(object obj) + { + if (obj is SoftObjectReference other) + return CompareTo(other); + return 0; + } + + /// + public int CompareTo(SoftObjectReference other) + { + return _id.CompareTo(other._id); + } + } +} diff --git a/Source/Engine/Scripting/SoftObjectReference.h b/Source/Engine/Scripting/SoftObjectReference.h new file mode 100644 index 000000000..1befaa0f7 --- /dev/null +++ b/Source/Engine/Scripting/SoftObjectReference.h @@ -0,0 +1,327 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Scripting/ScriptingObject.h" + +// Don't include Scripting.h but just FindObject method +extern FLAXENGINE_API ScriptingObject* FindObject(const Guid& id, MClass* type); + +/// +/// The scripting object soft reference. Objects gets referenced on use (ID reference is resolving it). +/// +class FLAXENGINE_API SoftObjectReferenceBase +{ +public: + + typedef Delegate<> EventType; + +protected: + + ScriptingObject* _object = nullptr; + Guid _id = Guid::Empty; + +public: + + /// + /// Action fired when reference gets changed. + /// + EventType Changed; + +public: + + /// + /// Initializes a new instance of the class. + /// + SoftObjectReferenceBase() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The object to link. + SoftObjectReferenceBase(ScriptingObject* obj) + { + OnSet(obj); + } + + /// + /// Finalizes an instance of the class. + /// + ~SoftObjectReferenceBase() + { + if (_object) + _object->Deleted.Unbind(this); + } + +public: + + /// + /// Gets the object ID. + /// + /// The object ID or Guid::Empty if nothing assigned. + Guid GetID() const + { + return _object ? _object->GetID() : _id; + } + +protected: + + /// + /// Sets the object. + /// + /// The object. + void OnSet(ScriptingObject* object) + { + auto e = _object; + if (e != object) + { + if (e) + e->Deleted.Unbind(this); + _object = e = object; + _id = e ? e->GetID() : Guid::Empty; + if (e) + e->Deleted.Bind(this); + Changed(); + } + } + + void OnDeleted(ScriptingObject* obj) + { + ASSERT(_object == obj); + _object->Deleted.Unbind(this); + _object = nullptr; + Changed(); + } +}; + +/// +/// The scripting object soft reference. Objects gets referenced on use (ID reference is resolving it). +/// +template +API_CLASS(InBuild) class SoftObjectReference : public SoftObjectReferenceBase +{ +public: + + typedef SoftObjectReference Type; + +public: + + /// + /// Initializes a new instance of the class. + /// + SoftObjectReference() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The object to link. + SoftObjectReference(T* obj) + : SoftObjectReferenceBase(obj) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The other property. + SoftObjectReference(const SoftObjectReference& other) + : SoftObjectReferenceBase(other.Get()) + { + } + + /// + /// Finalizes an instance of the class. + /// + ~SoftObjectReference() + { + } + +public: + + /// + /// Compares the property value with the given object. + /// + /// The other. + /// True if property object equals the given value. + FORCE_INLINE bool operator==(T* other) + { + return Get() == other; + } + + /// + /// Compares the property value with the other property value. + /// + /// The other property. + /// True if properties are equal. + FORCE_INLINE bool operator==(const SoftObjectReference& other) + { + return _id == other._id; + } + + /// + /// Compares the property value with the given object. + /// + /// The other. + /// True if property object not equals the given value. + FORCE_INLINE bool operator!=(T* other) + { + return Get() != other; + } + + /// + /// Compares the property value with the other property value. + /// + /// The other property. + /// True if properties are not equal. + FORCE_INLINE bool operator!=(const SoftObjectReference& other) + { + return _id != other._id; + } + + /// + /// Sets the property to the given property value. + /// + /// The other property. + /// The reference to this property. + SoftObjectReference& operator=(const SoftObjectReference& other) + { + if (this != &other) + OnSet(other.Get()); + return *this; + } + + /// + /// Sets the property to the given value. + /// + /// The object. + /// The reference to this property. + FORCE_INLINE SoftObjectReference& operator=(const T& other) + { + OnSet(&other); + return *this; + } + + /// + /// Sets the property to the given value. + /// + /// The object. + /// The reference to this property. + FORCE_INLINE SoftObjectReference& operator=(T* other) + { + OnSet(other); + return *this; + } + + /// + /// Sets the property to the object of the given ID. + /// + /// The object ID. + /// The reference to this property. + FORCE_INLINE SoftObjectReference& operator=(const Guid& id) + { + Set(id); + return *this; + } + + /// + /// Implicit conversion to the object. + /// + /// The object reference. + FORCE_INLINE operator T*() const + { + return (T*)Get(); + } + + /// + /// Implicit conversion to boolean value. + /// + /// True if object has been assigned, otherwise false + FORCE_INLINE operator bool() const + { + return _object != nullptr || _id.IsValid(); + } + + /// + /// Object accessor. + /// + /// The object reference. + FORCE_INLINE T* operator->() const + { + return (T*)Get(); + } + + /// + /// Gets the object pointer. + /// + /// The object reference. + T* Get() const + { + if (!_object) + const_cast(this)->OnSet(FindObject(_id, T::GetStaticClass())); + return (T*)_object; + } + + /// + /// Gets the object as a given type (static cast). + /// + /// Asset + template + FORCE_INLINE U* As() const + { + return static_cast(Get()); + } + +public: + + /// + /// Gets managed instance object (or null if no object linked). + /// + /// The managed object instance. + MonoObject* GetManagedInstance() const + { + auto object = Get(); + return object ? object->GetOrCreateManagedInstance() : nullptr; + } + + /// + /// Determines whether object is assigned and managed instance of the object is alive. + /// + /// True if managed object has been created and exists, otherwise false. + bool HasManagedInstance() const + { + auto object = Get(); + return object && object->HasManagedInstance(); + } + + /// + /// Gets the managed instance object or creates it if missing or null if not assigned. + /// + /// The Mono managed object. + MonoObject* GetOrCreateManagedInstance() const + { + auto object = Get(); + return object ? object->GetOrCreateManagedInstance() : nullptr; + } + + /// + /// Sets the object. + /// + /// The object ID. Uses Scripting to find the registered object of the given ID. + FORCE_INLINE void Set(const Guid& id) + { + Set(static_cast(FindObject(id, T::GetStaticClass()))); + } + + /// + /// Sets the object. + /// + /// The object. + FORCE_INLINE void Set(T* object) + { + OnSet(object); + } +}; diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index 535499442..d3300dd23 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -87,6 +87,38 @@ namespace FlaxEngine.Json } } + /// + /// Serialize SoftObjectReference as Guid in internal format. + /// + /// + internal class SoftObjectReferenceConverter : JsonConverter + { + /// + public override unsafe void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) + { + var id = ((SoftObjectReference)value).ID; + writer.WriteValue(JsonSerializer.GetStringID(&id)); + } + + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) + { + var result = new SoftObjectReference(); + if (reader.TokenType == JsonToken.String) + { + JsonSerializer.ParseID((string)reader.Value, out var id); + result.ID = id; + } + return result; + } + + /// + public override bool CanConvert(Type objectType) + { + return objectType == typeof(SoftObjectReference); + } + } + /* /// /// Serialize Guid values using `N` format @@ -184,6 +216,7 @@ namespace FlaxEngine.Json ObjectConverter = new FlaxObjectConverter(); settings.Converters.Add(ObjectConverter); settings.Converters.Add(new SceneReferenceConverter()); + settings.Converters.Add(new SoftObjectReferenceConverter()); settings.Converters.Add(new VersionConverter()); //settings.Converters.Add(new GuidConverter()); return settings; diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h index b760e9689..039d9df3e 100644 --- a/Source/Engine/Serialization/Serialization.h +++ b/Source/Engine/Serialization/Serialization.h @@ -9,6 +9,7 @@ #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Scripting/ScriptingObjectReference.h" +#include "Engine/Scripting/SoftObjectReference.h" #include "Engine/Content/AssetReference.h" #include "Engine/Content/WeakAssetReference.h" @@ -457,6 +458,27 @@ namespace Serialization v = id; } + // Soft Object Reference + + template + inline bool ShouldSerialize(const SoftObjectReference& v, const void* otherObj) + { + return !otherObj || v.Get() != ((SoftObjectReference*)otherObj)->Get(); + } + template + inline void Serialize(ISerializable::SerializeStream& stream, const SoftObjectReference& v, const void* otherObj) + { + stream.Guid(v.GetID()); + } + template + inline void Deserialize(ISerializable::DeserializeStream& stream, SoftObjectReference& v, ISerializeModifier* modifier) + { + Guid id; + Deserialize(stream, id, modifier); + modifier->IdsMapping.TryGet(id, id); + v = id; + } + // Asset Reference template diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 8b679fb7a..861d96a1a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -132,8 +132,8 @@ namespace Flax.Build.Bindings return result; } - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return typeInfo.GenericArgs[0].Type.Replace("::", "."); // Array or Span @@ -188,8 +188,8 @@ namespace Flax.Build.Bindings return "IntPtr"; } - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "IntPtr"; return GenerateCSharpNativeToManaged(buildData, typeInfo, caller); @@ -231,8 +231,8 @@ namespace Flax.Build.Bindings return "FlaxEngine.Object.GetUnmanagedPtr"; } - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "FlaxEngine.Object.GetUnmanagedPtr"; // Default diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 11fe6a414..ff2a2fadd 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -100,7 +100,7 @@ namespace Flax.Build.Bindings return value; if (typeInfo.Type == "String") return $"Variant(StringView({value}))"; - if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "ScriptingObjectReference") + if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference") return $"Variant({value}.Get())"; if (typeInfo.IsArray) { @@ -149,7 +149,7 @@ namespace Flax.Build.Bindings return $"((StringView){value}).GetText()"; if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})"; - if (typeInfo.Type == "ScriptingObjectReference") + if (typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference") return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((ScriptingObject*){value})"; if (typeInfo.IsArray) throw new Exception($"Not supported type to convert from the Variant to fixed-size array '{typeInfo}[{typeInfo.ArraySize}]'."); @@ -340,8 +340,8 @@ namespace Flax.Build.Bindings } } - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) { type = "MonoObject*"; return "{0}.GetManagedInstance()"; @@ -448,8 +448,8 @@ namespace Flax.Build.Bindings type = "MonoReflectionType*"; return "MUtils::UnboxVariantType({0})"; default: - // ScriptingObjectReference or AssetReference or WeakAssetReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null) + // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference + if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) { // For non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code) if (CppNonPodTypesConvertingGeneration) From adbb467206b2ff24c5fa69bd32b37291052a6cc8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 11:29:07 +0100 Subject: [PATCH 358/419] Add support for `mutable` keyword on `API_FIELD` --- .../Bindings/BindingsGenerator.Parsing.cs | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 1c4cadb4a..fb6ce7d52 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -1022,11 +1022,30 @@ namespace Flax.Build.Bindings // Read parameters from the tag var tagParams = ParseTagParameters(ref context); + context.Tokenizer.SkipUntil(TokenType.Identifier); - // Read 'static' keyword - desc.IsStatic = context.Tokenizer.NextToken().Value == "static"; - if (!desc.IsStatic) - context.Tokenizer.PreviousToken(); + // Read 'static' or 'mutable' + Token token; + var isMutable = false; + while (true) + { + token = context.Tokenizer.CurrentToken; + if (!desc.IsStatic && token.Value == "static") + { + desc.IsStatic = true; + context.Tokenizer.NextToken(); + } + else if (!isMutable && token.Value == "mutable") + { + isMutable = true; + context.Tokenizer.NextToken(); + } + else + { + context.Tokenizer.PreviousToken(); + break; + } + } // Read type desc.Type = ParseType(ref context); @@ -1035,7 +1054,7 @@ namespace Flax.Build.Bindings desc.Name = ParseName(ref context); // Read ';' or default value or array size or bit-field size - var token = context.Tokenizer.ExpectAnyTokens(new[] { TokenType.SemiColon, TokenType.Equal, TokenType.LeftBracket, TokenType.Colon }); + token = context.Tokenizer.ExpectAnyTokens(new[] { TokenType.SemiColon, TokenType.Equal, TokenType.LeftBracket, TokenType.Colon }); if (token.Type == TokenType.Equal) { context.Tokenizer.SkipUntil(TokenType.SemiColon, out desc.DefaultValue, false); From 81e41d51c88848abdfacfdb89f1ce404bce83cc9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 11:37:14 +0100 Subject: [PATCH 359/419] Remove Unlink from asset reference --- .../Editor/Utilities/ViewportIconsRenderer.cpp | 2 +- Source/Engine/Content/AssetReference.h | 10 +--------- Source/Engine/Content/Assets/SkeletonMask.cpp | 2 +- Source/Engine/Debug/DebugDraw.cpp | 2 +- Source/Engine/Foliage/Foliage.cpp | 2 +- Source/Engine/Graphics/GPUDevice.cpp | 10 +++++----- Source/Engine/Level/Scene/Lightmap.cpp | 4 ++-- Source/Engine/Level/Scene/Scene.cpp | 4 ++-- Source/Engine/Navigation/NavMesh.cpp | 3 +-- Source/Engine/Particles/ParticleManager.cpp | 2 +- Source/Engine/Render2D/Render2D.cpp | 2 +- Source/Engine/Renderer/AmbientOcclusionPass.cpp | 2 +- Source/Engine/Renderer/AntiAliasing/FXAA.cpp | 6 ++---- Source/Engine/Renderer/AntiAliasing/SMAA.cpp | 10 ++++------ Source/Engine/Renderer/AntiAliasing/TAA.cpp | 3 ++- Source/Engine/Renderer/AtmospherePreCompute.cpp | 2 +- Source/Engine/Renderer/ColorGradingPass.cpp | 6 ++---- Source/Engine/Renderer/DepthOfFieldPass.cpp | 16 ++++++---------- Source/Engine/Renderer/EyeAdaptationPass.cpp | 6 ++---- Source/Engine/Renderer/ForwardPass.cpp | 6 ++---- Source/Engine/Renderer/GBufferPass.cpp | 11 ++++------- Source/Engine/Renderer/HistogramPass.cpp | 3 ++- Source/Engine/Renderer/LightPass.cpp | 6 ++---- Source/Engine/Renderer/MotionBlurPass.cpp | 6 ++---- Source/Engine/Renderer/PostProcessingPass.cpp | 12 +++++------- Source/Engine/Renderer/ProbesRenderer.cpp | 2 +- Source/Engine/Renderer/ReflectionsPass.cpp | 10 ++++------ .../Renderer/ScreenSpaceReflectionsPass.cpp | 8 +++----- Source/Engine/Renderer/ShadowsPass.cpp | 5 +++-- Source/Engine/Renderer/Utils/BitonicSort.cpp | 6 ++---- Source/Engine/Renderer/Utils/MultiScaler.cpp | 10 ++++------ Source/Engine/Renderer/VolumetricFogPass.cpp | 15 ++++----------- Source/Engine/ShadowsOfMordor/Builder.cpp | 2 +- Source/Engine/Terrain/TerrainChunk.cpp | 2 +- Source/Engine/Terrain/TerrainPatch.cpp | 6 +++--- 35 files changed, 80 insertions(+), 124 deletions(-) diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index 631e8b56f..889d94052 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -138,7 +138,7 @@ bool ViewportIconsRendererService::Init() void ViewportIconsRendererService::Dispose() { - QuadModel.Unlink(); + QuadModel = nullptr; for (int32 i = 0; i < ARRAY_COUNT(InstanceBuffers); i++) InstanceBuffers[i].Release(); ActorTypeToIconType.Clear(); diff --git a/Source/Engine/Content/AssetReference.h b/Source/Engine/Content/AssetReference.h index c78aa5aef..658545d1a 100644 --- a/Source/Engine/Content/AssetReference.h +++ b/Source/Engine/Content/AssetReference.h @@ -78,14 +78,6 @@ public: return _asset ? _asset->GetOrCreateManagedInstance() : nullptr; } - /// - /// Clears the asset reference. - /// - FORCE_INLINE void Unlink() - { - OnSet(nullptr); - } - /// /// Gets the asset property value as string. /// @@ -135,7 +127,7 @@ protected: if (_asset == asset) { Unload(); - Unlink(); + OnSet(nullptr); } } }; diff --git a/Source/Engine/Content/Assets/SkeletonMask.cpp b/Source/Engine/Content/Assets/SkeletonMask.cpp index 4747e0e44..0742e2951 100644 --- a/Source/Engine/Content/Assets/SkeletonMask.cpp +++ b/Source/Engine/Content/Assets/SkeletonMask.cpp @@ -38,7 +38,7 @@ Asset::LoadResult SkeletonMask::load() void SkeletonMask::unload(bool isReloading) { - Skeleton.Unlink(); + Skeleton = nullptr; _maskedNodes.Resize(0); _mask.Resize(0); } diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 320a991a3..1f7e0c596 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -487,7 +487,7 @@ void DebugDrawService::Dispose() DebugDrawPsDepthTest.Release(); DebugDrawPsDepthTest.Release(); SAFE_DELETE(DebugDrawVB); - DebugDrawShader.Unlink(); + DebugDrawShader = nullptr; } void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTextureView* depthBuffer, bool enableDepthTest) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index dcfd50212..c38191adf 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -201,7 +201,7 @@ void Foliage::RemoveFoliageType(int32 index) FoliageTypes[i].Index--; } auto& item = FoliageTypes[index]; - item.Model.Unlink(); + item.Model = nullptr; item.Entries.Release(); FoliageTypes.RemoveAtKeepOrder(index); diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index 47ec6d3a6..821031454 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -13,10 +13,10 @@ #include "Engine/Platform/Windows/WindowsWindow.h" #include "Engine/Render2D/Render2D.h" #include "Engine/Engine/CommandLine.h" +#include "Engine/Engine/Engine.h" #include "Engine/Engine/EngineService.h" #include "Engine/Profiler/Profiler.h" #include "Engine/Renderer/RenderList.h" -#include "Engine/Engine/Engine.h" #include "Engine/Core/Utilities.h" GPUPipelineState* GPUPipelineState::Spawn(const SpawnParams& params) @@ -230,10 +230,10 @@ void GPUDevice::preDispose() RenderTargetPool::Flush(); // Release resources - _res->DefaultMaterial.Unlink(); - _res->DefaultNormalMap.Unlink(); - _res->DefaultWhiteTexture.Unlink(); - _res->DefaultBlackTexture.Unlink(); + _res->DefaultMaterial = nullptr; + _res->DefaultNormalMap = nullptr; + _res->DefaultWhiteTexture = nullptr; + _res->DefaultBlackTexture = nullptr; SAFE_DELETE_GPU_RESOURCE(_res->PS_CopyLinear); SAFE_DELETE_GPU_RESOURCE(_res->PS_Clear); SAFE_DELETE_GPU_RESOURCE(_res->FullscreenTriangleVB); diff --git a/Source/Engine/Level/Scene/Lightmap.cpp b/Source/Engine/Level/Scene/Lightmap.cpp index b55b603f5..4a2eec796 100644 --- a/Source/Engine/Level/Scene/Lightmap.cpp +++ b/Source/Engine/Level/Scene/Lightmap.cpp @@ -79,7 +79,7 @@ void Lightmap::EnsureSize(int32 size) { // Unlink texture that cannot be loaded LOG(Warning, "Lightmap::EnsureSize failed to load texture"); - texture.Unlink(); + texture = nullptr; } else { @@ -88,7 +88,7 @@ void Lightmap::EnsureSize(int32 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(); + texture = nullptr; } } } diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp index b86610ab9..d38bd4225 100644 --- a/Source/Engine/Level/Scene/Scene.cpp +++ b/Source/Engine/Level/Scene/Scene.cpp @@ -298,8 +298,8 @@ void Scene::OnDeleteObject() { // Cleanup LightmapsData.UnloadLightmaps(); - CSGData.Model.Unlink(); - CSGData.CollisionData.Unlink(); + CSGData.Model = nullptr; + CSGData.CollisionData = nullptr; // Base Actor::OnDeleteObject(); diff --git a/Source/Engine/Navigation/NavMesh.cpp b/Source/Engine/Navigation/NavMesh.cpp index ef90e2989..fe7756049 100644 --- a/Source/Engine/Navigation/NavMesh.cpp +++ b/Source/Engine/Navigation/NavMesh.cpp @@ -40,8 +40,7 @@ void NavMesh::SaveNavMesh() // Check if has no navmesh data generated (someone could just remove navmesh volumes or generate for empty scene) if (Data.Tiles.IsEmpty()) { - // Keep asset reference valid - DataAsset.Unlink(); + DataAsset = nullptr; return; } diff --git a/Source/Engine/Particles/ParticleManager.cpp b/Source/Engine/Particles/ParticleManager.cpp index c83aacbbf..429420950 100644 --- a/Source/Engine/Particles/ParticleManager.cpp +++ b/Source/Engine/Particles/ParticleManager.cpp @@ -557,7 +557,7 @@ void OnShaderReloading(Asset* obj) void CleanupGPUParticlesSorting() { - GPUParticlesSorting.Unlink(); + GPUParticlesSorting = nullptr; } void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices) diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 2413686aa..1aed94a8e 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -522,7 +522,7 @@ void Render2DService::Dispose() Lines.Resize(0); Lines2.Resize(0); - GUIShader.Unlink(); + GUIShader = nullptr; PsoDepth.Dispose(); PsoNoDepth.Dispose(); diff --git a/Source/Engine/Renderer/AmbientOcclusionPass.cpp b/Source/Engine/Renderer/AmbientOcclusionPass.cpp index dfae20792..e72de509b 100644 --- a/Source/Engine/Renderer/AmbientOcclusionPass.cpp +++ b/Source/Engine/Renderer/AmbientOcclusionPass.cpp @@ -196,7 +196,7 @@ void AmbientOcclusionPass::Dispose() SAFE_DELETE_GPU_RESOURCE(_psApplyHalf); // Release asset - _shader.Unlink(); + _shader = nullptr; } void AmbientOcclusionPass::Render(RenderContext& renderContext) diff --git a/Source/Engine/Renderer/AntiAliasing/FXAA.cpp b/Source/Engine/Renderer/AntiAliasing/FXAA.cpp index 159e481a6..3dcb6ae71 100644 --- a/Source/Engine/Renderer/AntiAliasing/FXAA.cpp +++ b/Source/Engine/Renderer/AntiAliasing/FXAA.cpp @@ -55,11 +55,9 @@ void FXAA::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline state + // Cleanup _psFXAA.Delete(); - - // Release asset - _shader.Unlink(); + _shader = nullptr; } void FXAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output) diff --git a/Source/Engine/Renderer/AntiAliasing/SMAA.cpp b/Source/Engine/Renderer/AntiAliasing/SMAA.cpp index cf7e53f40..7c1ee0c16 100644 --- a/Source/Engine/Renderer/AntiAliasing/SMAA.cpp +++ b/Source/Engine/Renderer/AntiAliasing/SMAA.cpp @@ -82,15 +82,13 @@ void SMAA::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup _psEdge.Delete(); _psBlend.Delete(); SAFE_DELETE_GPU_RESOURCE(_psNeighbor); - - // Release assets - _shader.Unlink(); - _areaTex.Unlink(); - _searchTex.Unlink(); + _shader = nullptr; + _areaTex = nullptr; + _searchTex = nullptr; } void SMAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output) diff --git a/Source/Engine/Renderer/AntiAliasing/TAA.cpp b/Source/Engine/Renderer/AntiAliasing/TAA.cpp index 7b8dfe17f..a1fee93ad 100644 --- a/Source/Engine/Renderer/AntiAliasing/TAA.cpp +++ b/Source/Engine/Renderer/AntiAliasing/TAA.cpp @@ -42,8 +42,9 @@ void TAA::Dispose() // Base RendererPass::Dispose(); + // Cleanup _psTAA = nullptr; - _shader.Unlink(); + _shader = nullptr; } bool TAA::NeedMotionVectors(RenderContext& renderContext) diff --git a/Source/Engine/Renderer/AtmospherePreCompute.cpp b/Source/Engine/Renderer/AtmospherePreCompute.cpp index 66576e55f..cef9964ca 100644 --- a/Source/Engine/Renderer/AtmospherePreCompute.cpp +++ b/Source/Engine/Renderer/AtmospherePreCompute.cpp @@ -305,7 +305,7 @@ void release() SAFE_DELETE_GPU_RESOURCE(_psInscatterS); SAFE_DELETE_GPU_RESOURCE(_psInscatterN); SAFE_DELETE_GPU_RESOURCE(_psCopyInscatterNAdd); - _shader.Unlink(); + _shader = nullptr; SAFE_DELETE(_task); SAFE_DELETE_GPU_RESOURCE(AtmosphereTransmittance); SAFE_DELETE_GPU_RESOURCE(AtmosphereIrradiance); diff --git a/Source/Engine/Renderer/ColorGradingPass.cpp b/Source/Engine/Renderer/ColorGradingPass.cpp index 94caf2b46..c40248caa 100644 --- a/Source/Engine/Renderer/ColorGradingPass.cpp +++ b/Source/Engine/Renderer/ColorGradingPass.cpp @@ -121,11 +121,9 @@ void ColorGradingPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline state + // Cleanup _psLut.Delete(); - - // Release assets - _shader.Unlink(); + _shader = nullptr; } GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) diff --git a/Source/Engine/Renderer/DepthOfFieldPass.cpp b/Source/Engine/Renderer/DepthOfFieldPass.cpp index b5382ec72..4e4ee7ed1 100644 --- a/Source/Engine/Renderer/DepthOfFieldPass.cpp +++ b/Source/Engine/Renderer/DepthOfFieldPass.cpp @@ -66,21 +66,17 @@ void DepthOfFieldPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psDofDepthBlurGeneration); SAFE_DELETE_GPU_RESOURCE(_psBokehGeneration); SAFE_DELETE_GPU_RESOURCE(_psDoNotGenerateBokeh); SAFE_DELETE_GPU_RESOURCE(_psBokeh); SAFE_DELETE_GPU_RESOURCE(_psBokehComposite); - - // Release assets - _shader.Unlink(); - _defaultBokehHexagon.Unlink(); - _defaultBokehOctagon.Unlink(); - _defaultBokehCircle.Unlink(); - _defaultBokehCross.Unlink(); - - // Release resources + _shader = nullptr; + _defaultBokehHexagon = nullptr; + _defaultBokehOctagon = nullptr; + _defaultBokehCircle = nullptr; + _defaultBokehCross = nullptr; SAFE_DELETE_GPU_RESOURCE(_bokehBuffer); SAFE_DELETE_GPU_RESOURCE(_bokehIndirectArgsBuffer); } diff --git a/Source/Engine/Renderer/EyeAdaptationPass.cpp b/Source/Engine/Renderer/EyeAdaptationPass.cpp index a85e0a72b..3bfbb9daa 100644 --- a/Source/Engine/Renderer/EyeAdaptationPass.cpp +++ b/Source/Engine/Renderer/EyeAdaptationPass.cpp @@ -244,15 +244,13 @@ void EyeAdaptationPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psManual); SAFE_DELETE_GPU_RESOURCE(_psLuminanceMap); SAFE_DELETE_GPU_RESOURCE(_psBlendLuminance); SAFE_DELETE_GPU_RESOURCE(_psApplyLuminance); SAFE_DELETE_GPU_RESOURCE(_psHistogram); - - // Release asset - _shader.Unlink(); + _shader = nullptr; } bool EyeAdaptationPass::setupResources() diff --git a/Source/Engine/Renderer/ForwardPass.cpp b/Source/Engine/Renderer/ForwardPass.cpp index ad178fe67..93ccf0750 100644 --- a/Source/Engine/Renderer/ForwardPass.cpp +++ b/Source/Engine/Renderer/ForwardPass.cpp @@ -62,11 +62,9 @@ void ForwardPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psApplyDistortion); - - // Release assets - _shader.Unlink(); + _shader = nullptr; } void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output) diff --git a/Source/Engine/Renderer/GBufferPass.cpp b/Source/Engine/Renderer/GBufferPass.cpp index b9e91ee01..2165e25dc 100644 --- a/Source/Engine/Renderer/GBufferPass.cpp +++ b/Source/Engine/Renderer/GBufferPass.cpp @@ -77,14 +77,11 @@ void GBufferPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline state + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psDebug); - - // Release assets - _gBufferShader.Unlink(); - _skyModel.Unlink(); - _boxModel.Unlink(); - + _gBufferShader = nullptr; + _skyModel = nullptr; + _boxModel = nullptr; #if USE_EDITOR SAFE_DELETE(_lightmapUVsDensityMaterialShader); SAFE_DELETE(_vertexColorsMaterialShader); diff --git a/Source/Engine/Renderer/HistogramPass.cpp b/Source/Engine/Renderer/HistogramPass.cpp index fe16dbd19..e8bfc9fad 100644 --- a/Source/Engine/Renderer/HistogramPass.cpp +++ b/Source/Engine/Renderer/HistogramPass.cpp @@ -102,8 +102,9 @@ void HistogramPass::Dispose() // Base RendererPass::Dispose(); + // Cleanup SAFE_DELETE_GPU_RESOURCE(_histogramBuffer); - _shader.Unlink(); + _shader = nullptr; } bool HistogramPass::setupResources() diff --git a/Source/Engine/Renderer/LightPass.cpp b/Source/Engine/Renderer/LightPass.cpp index d021ede01..c79033c37 100644 --- a/Source/Engine/Renderer/LightPass.cpp +++ b/Source/Engine/Renderer/LightPass.cpp @@ -164,7 +164,7 @@ void LightPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup _psLightDir.Delete(); _psLightPointNormal.Delete(); _psLightPointInverted.Delete(); @@ -172,9 +172,7 @@ void LightPass::Dispose() _psLightSpotInverted.Delete(); SAFE_DELETE_GPU_RESOURCE(_psLightSkyNormal); SAFE_DELETE_GPU_RESOURCE(_psLightSkyInverted); - - // Release assets - _sphereModel.Unlink(); + _sphereModel = nullptr; } void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightBuffer) diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index f10ef442b..689916708 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -134,16 +134,14 @@ void MotionBlurPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline state + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psCameraMotionVectors); SAFE_DELETE_GPU_RESOURCE(_psMotionVectorsDebug); SAFE_DELETE_GPU_RESOURCE(_psTileMax); SAFE_DELETE_GPU_RESOURCE(_psTileMaxVariable); SAFE_DELETE_GPU_RESOURCE(_psNeighborMax); SAFE_DELETE_GPU_RESOURCE(_psMotionBlur); - - // Release asset - _shader.Unlink(); + _shader = nullptr; } void MotionBlurPass::RenderMotionVectors(RenderContext& renderContext) diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp index 02469aa81..4e5b4d4fb 100644 --- a/Source/Engine/Renderer/PostProcessingPass.cpp +++ b/Source/Engine/Renderer/PostProcessingPass.cpp @@ -167,19 +167,17 @@ void PostProcessingPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psThreshold); SAFE_DELETE_GPU_RESOURCE(_psScale); SAFE_DELETE_GPU_RESOURCE(_psBlurH); SAFE_DELETE_GPU_RESOURCE(_psBlurV); SAFE_DELETE_GPU_RESOURCE(_psGenGhosts); _psComposite.Delete(); - - // Release assets - _shader.Unlink(); - _defaultLensColor.Unlink(); - _defaultLensDirt.Unlink(); - _defaultLensStar.Unlink(); + _shader = nullptr; + _defaultLensColor = nullptr; + _defaultLensDirt = nullptr; + _defaultLensStar = nullptr; } void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output, GPUTexture* colorGradingLUT) diff --git a/Source/Engine/Renderer/ProbesRenderer.cpp b/Source/Engine/Renderer/ProbesRenderer.cpp index 6688c4716..3f48f8c6e 100644 --- a/Source/Engine/Renderer/ProbesRenderer.cpp +++ b/Source/Engine/Renderer/ProbesRenderer.cpp @@ -333,7 +333,7 @@ void ProbesRenderer::Release() SAFE_DELETE_GPU_RESOURCE(_psAccDiffuseIrradiance); SAFE_DELETE_GPU_RESOURCE(_psAccumulateCubeFaces); SAFE_DELETE_GPU_RESOURCE(_psCopyFrameLHB); - _shader.Unlink(); + _shader = nullptr; SAFE_DELETE_GPU_RESOURCE(_output); SAFE_DELETE(_task); SAFE_DELETE_GPU_RESOURCE(_probe); diff --git a/Source/Engine/Renderer/ReflectionsPass.cpp b/Source/Engine/Renderer/ReflectionsPass.cpp index 40f3aeb30..c3108cf0d 100644 --- a/Source/Engine/Renderer/ReflectionsPass.cpp +++ b/Source/Engine/Renderer/ReflectionsPass.cpp @@ -324,15 +324,13 @@ void ReflectionsPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psProbeNormal); SAFE_DELETE_GPU_RESOURCE(_psProbeInverted); SAFE_DELETE_GPU_RESOURCE(_psCombinePass); - - // Release assets - _shader.Unlink(); - _sphereModel.Unlink(); - _preIntegratedGF.Unlink(); + _shader = nullptr; + _sphereModel = nullptr; + _preIntegratedGF = nullptr; } bool sortProbes(EnvironmentProbe* const& p1, EnvironmentProbe* const& p2) diff --git a/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp b/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp index 491f53262..5e81db45c 100644 --- a/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp +++ b/Source/Engine/Renderer/ScreenSpaceReflectionsPass.cpp @@ -152,16 +152,14 @@ void ScreenSpaceReflectionsPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psRayTracePass); SAFE_DELETE_GPU_RESOURCE(_psCombinePass); SAFE_DELETE_GPU_RESOURCE(_psTemporalPass); SAFE_DELETE_GPU_RESOURCE(_psMixPass); _psResolvePass.Delete(); - - // Release assets - _shader.Unlink(); - _preIntegratedGF.Unlink(); + _shader = nullptr; + _preIntegratedGF = nullptr; } void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTextureView* reflectionsRT, GPUTextureView* lightBuffer) diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 11812a45c..4c3260045 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -193,11 +193,12 @@ void ShadowsPass::Dispose() // Base RendererPass::Dispose(); + // Cleanup _psShadowDir.Delete(); _psShadowPoint.Delete(); _psShadowSpot.Delete(); - _shader.Unlink(); - _sphereModel.Unlink(); + _shader = nullptr; + _sphereModel = nullptr; SAFE_DELETE_GPU_RESOURCE(_shadowMapCSM); SAFE_DELETE_GPU_RESOURCE(_shadowMapCube); } diff --git a/Source/Engine/Renderer/Utils/BitonicSort.cpp b/Source/Engine/Renderer/Utils/BitonicSort.cpp index 5dc4fffee..89313a95d 100644 --- a/Source/Engine/Renderer/Utils/BitonicSort.cpp +++ b/Source/Engine/Renderer/Utils/BitonicSort.cpp @@ -69,7 +69,7 @@ void BitonicSort::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup SAFE_DELETE_GPU_RESOURCE(_dispatchArgsBuffer); _cb = nullptr; _indirectArgsCS = nullptr; @@ -77,9 +77,7 @@ void BitonicSort::Dispose() _innerSortCS = nullptr; _outerSortCS = nullptr; _copyIndicesCS = nullptr; - - // Release asset - _shader.Unlink(); + _shader = nullptr; } void BitonicSort::Sort(GPUContext* context, GPUBuffer* sortingKeysBuffer, GPUBuffer* countBuffer, uint32 counterOffset, bool sortAscending, GPUBuffer* sortedIndicesBuffer) diff --git a/Source/Engine/Renderer/Utils/MultiScaler.cpp b/Source/Engine/Renderer/Utils/MultiScaler.cpp index c415c1900..2c6e8ba14 100644 --- a/Source/Engine/Renderer/Utils/MultiScaler.cpp +++ b/Source/Engine/Renderer/Utils/MultiScaler.cpp @@ -81,14 +81,12 @@ void MultiScaler::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup SAFE_DELETE_GPU_RESOURCE(_psHalfDepth); _psBlur5.Delete(); _psBlur9.Delete(); _psBlur13.Delete(); - - // Release asset - _shader.Unlink(); + _shader = nullptr; } void MultiScaler::Filter(const FilterMode mode, GPUContext* context, const int32 width, const int32 height, GPUTextureView* src, GPUTextureView* dst, GPUTextureView* tmp) @@ -121,7 +119,7 @@ void MultiScaler::Filter(const FilterMode mode, GPUContext* context, const int32 ps = &_psBlur13; break; default: - CRASH; + CRASH; return; } @@ -177,7 +175,7 @@ void MultiScaler::Filter(const FilterMode mode, GPUContext* context, const int32 ps = &_psBlur13; break; default: - CRASH; + CRASH; return; } diff --git a/Source/Engine/Renderer/VolumetricFogPass.cpp b/Source/Engine/Renderer/VolumetricFogPass.cpp index 0bf8d838e..ff8f469ae 100644 --- a/Source/Engine/Renderer/VolumetricFogPass.cpp +++ b/Source/Engine/Renderer/VolumetricFogPass.cpp @@ -89,26 +89,19 @@ void VolumetricFogPass::Dispose() // Base RendererPass::Dispose(); - // Delete pipeline states + // Cleanup _psInjectLight.Delete(); - _csInitialize = nullptr; _csLightScattering.Clear(); _csFinalIntegration = nullptr; - SAFE_DELETE_GPU_RESOURCE(_vbCircleRasterize); SAFE_DELETE_GPU_RESOURCE(_ibCircleRasterize); - - // Release assets - _shader.Unlink(); + _shader = nullptr; } -float ComputeZSliceFromDepth(float SceneDepth, const VolumetricFogOptions& options, int32 GridSizeZ) +float ComputeZSliceFromDepth(float sceneDepth, const VolumetricFogOptions& options, int32 gridSizeZ) { - // This must match frustum voxels depth distribution - // See ComputeNormalizedZSliceFromDepth() in VolumetricFog.shader - - return SceneDepth / options.Distance * GridSizeZ; + return sceneDepth / options.Distance * (float)gridSizeZ; } bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options) diff --git a/Source/Engine/ShadowsOfMordor/Builder.cpp b/Source/Engine/ShadowsOfMordor/Builder.cpp index 715803839..b9cb57376 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.cpp @@ -499,7 +499,7 @@ void ShadowsOfMordor::Builder::releaseResources() SAFE_DELETE_GPU_RESOURCE(_psRenderCacheModel); SAFE_DELETE_GPU_RESOURCE(_psRenderCacheTerrain); SAFE_DELETE_GPU_RESOURCE(_psBlurCache); - _shader.Unlink(); + _shader = nullptr; SAFE_DELETE_GPU_RESOURCE(_irradianceReduction); diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index 6cff541f3..9dab924c2 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -21,7 +21,7 @@ void TerrainChunk::Init(TerrainPatch* patch, uint16 x, uint16 z) _yHeight = 1; _heightmapUVScaleBias = Vector4(1.0f, 1.0f, _x, _z) * (1.0f / TerrainPatch::CHUNKS_COUNT_EDGE); _perInstanceRandom = (_patch->_terrain->_id.C ^ _x ^ _z) * (1.0f / MAX_uint32); - OverrideMaterial.Unlink(); + OverrideMaterial = nullptr; } bool TerrainChunk::PrepareDraw(const RenderContext& renderContext) diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 791e93c7f..61e6f456e 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -64,12 +64,12 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z) { Chunks[i].Init(this, i % CHUNKS_COUNT_EDGE, i / CHUNKS_COUNT_EDGE); } - Heightmap.Unlink(); + Heightmap = nullptr; for (int32 i = 0; i < TERRAIN_MAX_SPLATMAPS_COUNT; i++) { - Splatmap[i].Unlink(); + Splatmap[i] = nullptr; } - _heightfield.Unlink(); + _heightfield = nullptr; #if TERRAIN_UPDATING _cachedHeightMap.Resize(0); _cachedHolesMask.Resize(0); From 5cb232227063c33af48dd3ddece7ac475380a5dc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 11:37:34 +0100 Subject: [PATCH 360/419] Add default Deformable material for splines --- Content/Editor/Particles/Particle Material Color.flax | 2 +- Content/Engine/DefaultDeformableMaterial.flax | 3 +++ Source/Editor/Cooker/Steps/DeployDataStep.cpp | 1 + Source/Engine/Graphics/GPUDevice.cpp | 9 +++++++++ Source/Engine/Graphics/GPUDevice.h | 5 +++++ Source/Engine/Level/Actors/SplineModel.cpp | 2 +- Source/Engine/Scripting/SoftObjectReference.h | 5 +++-- 7 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 Content/Engine/DefaultDeformableMaterial.flax diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index e2fdb046b..81fcbea72 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14d75ef13449f9ed9a6e18ee82fe59fe4e83ac519b62b1e87aaadabd89673995 +oid sha256:e44da38b26f222f7081278dc64b7fa932c6f9bfd0b79694da784bd4d944e838a size 29232 diff --git a/Content/Engine/DefaultDeformableMaterial.flax b/Content/Engine/DefaultDeformableMaterial.flax new file mode 100644 index 000000000..3b2e45a87 --- /dev/null +++ b/Content/Engine/DefaultDeformableMaterial.flax @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dab7eb4f71a407eae7d6b051cbad701f234742b0d96646811c8a615de80e94eb +size 18800 diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp index 9ce3a8a5d..96f8bfad2 100644 --- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp +++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp @@ -74,6 +74,7 @@ bool DeployDataStep::Perform(CookingData& data) data.AddRootEngineAsset(TEXT("Shaders/SSR")); data.AddRootEngineAsset(TEXT("Shaders/VolumetricFog")); data.AddRootEngineAsset(TEXT("Engine/DefaultMaterial")); + data.AddRootEngineAsset(TEXT("Engine/DefaultDeformableMaterial")); data.AddRootEngineAsset(TEXT("Engine/DefaultTerrainMaterial")); if (!gameSettings->NoSplashScreen && !gameSettings->SplashScreen.IsValid()) data.AddRootEngineAsset(TEXT("Engine/Textures/Logo")); diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index 821031454..51d92cd3f 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -18,6 +18,7 @@ #include "Engine/Profiler/Profiler.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Core/Utilities.h" +#include "Engine/Scripting/SoftObjectReference.h" GPUPipelineState* GPUPipelineState::Spawn(const SpawnParams& params) { @@ -127,6 +128,7 @@ struct GPUDevice::PrivateData GPUPipelineState* PS_Clear = nullptr; GPUBuffer* FullscreenTriangleVB = nullptr; AssetReference DefaultMaterial; + SoftObjectReference DefaultDeformableMaterial; AssetReference DefaultNormalMap; AssetReference DefaultWhiteTexture; AssetReference DefaultBlackTexture; @@ -206,6 +208,7 @@ bool GPUDevice::LoadContent() _res->DefaultMaterial = Content::LoadAsyncInternal(TEXT("Engine/DefaultMaterial")); if (_res->DefaultMaterial == nullptr) return true; + _res->DefaultDeformableMaterial = Guid(0x639e12c0, 0x42d34bae, 0x89dd8b81, 0x7e1efc2d); // Load default normal map _res->DefaultNormalMap = Content::LoadAsyncInternal(TEXT("Engine/Textures/NormalTexture")); @@ -231,6 +234,7 @@ void GPUDevice::preDispose() // Release resources _res->DefaultMaterial = nullptr; + _res->DefaultDeformableMaterial = nullptr; _res->DefaultNormalMap = nullptr; _res->DefaultWhiteTexture = nullptr; _res->DefaultBlackTexture = nullptr; @@ -380,6 +384,11 @@ MaterialBase* GPUDevice::GetDefaultMaterial() const return _res->DefaultMaterial; } +MaterialBase* GPUDevice::GetDefaultDeformableMaterial() const +{ + return _res->DefaultDeformableMaterial.Get(); +} + GPUTexture* GPUDevice::GetDefaultNormalMap() const { return _res->DefaultNormalMap ? _res->DefaultNormalMap->GetTexture() : nullptr; diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h index 0b5fb46b9..e1deb16cc 100644 --- a/Source/Engine/Graphics/GPUDevice.h +++ b/Source/Engine/Graphics/GPUDevice.h @@ -234,6 +234,11 @@ public: /// MaterialBase* GetDefaultMaterial() const; + /// + /// Gets the default material (Deformable domain). + /// + MaterialBase* GetDefaultDeformableMaterial() const; + /// /// Gets the default normal map texture. /// diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 69fcf90bc..c8a7b7b1f 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -408,7 +408,7 @@ void SplineModel::Draw(RenderContext& renderContext) else if (slot.Material && slot.Material->IsLoaded()) material = slot.Material; else - material = nullptr; + material = GPUDevice::Instance->GetDefaultDeformableMaterial(); if (!material || !material->IsDeformable()) continue; diff --git a/Source/Engine/Scripting/SoftObjectReference.h b/Source/Engine/Scripting/SoftObjectReference.h index 1befaa0f7..b6f08db4a 100644 --- a/Source/Engine/Scripting/SoftObjectReference.h +++ b/Source/Engine/Scripting/SoftObjectReference.h @@ -311,9 +311,10 @@ public: /// Sets the object. /// /// The object ID. Uses Scripting to find the registered object of the given ID. - FORCE_INLINE void Set(const Guid& id) + void Set(const Guid& id) { - Set(static_cast(FindObject(id, T::GetStaticClass()))); + _id = id; + _object = nullptr; } /// From 8bce445ab01176eaf16bd828ea06a15a6ebafc5a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 14:33:16 +0100 Subject: [PATCH 361/419] Refactor Collider base class to improve code sharing across collider shape types --- .../Engine/Physics/Colliders/BoxCollider.cpp | 50 +------ Source/Engine/Physics/Colliders/BoxCollider.h | 16 +-- .../Physics/Colliders/CapsuleCollider.cpp | 51 +------ .../Physics/Colliders/CapsuleCollider.h | 16 +-- .../Physics/Colliders/CharacterController.cpp | 28 +--- .../Physics/Colliders/CharacterController.h | 10 +- Source/Engine/Physics/Colliders/Collider.cpp | 105 +++++++++++++-- Source/Engine/Physics/Colliders/Collider.h | 30 +++-- .../Engine/Physics/Colliders/MeshCollider.cpp | 127 ++---------------- .../Engine/Physics/Colliders/MeshCollider.h | 9 +- .../Physics/Colliders/SphereCollider.cpp | 50 +------ .../Engine/Physics/Colliders/SphereCollider.h | 12 +- Source/Engine/Physics/Types.h | 1 + 13 files changed, 154 insertions(+), 351 deletions(-) diff --git a/Source/Engine/Physics/Colliders/BoxCollider.cpp b/Source/Engine/Physics/Colliders/BoxCollider.cpp index da78e6a2f..487059148 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.cpp +++ b/Source/Engine/Physics/Colliders/BoxCollider.cpp @@ -4,9 +4,6 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Physics/Utilities.h" #include -#if USE_EDITOR -#include "Engine/Level/Scene/SceneRendering.h" -#endif BoxCollider::BoxCollider(const SpawnParams& params) : Collider(params) @@ -123,26 +120,6 @@ void BoxCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* mod DESERIALIZE_MEMBER(Size, _size); } -#if USE_EDITOR - -void BoxCollider::OnEnable() -{ - GetSceneRendering()->AddPhysicsDebug(this); - - // Base - Collider::OnEnable(); -} - -void BoxCollider::OnDisable() -{ - GetSceneRendering()->RemovePhysicsDebug(this); - - // Base - Collider::OnDisable(); -} - -#endif - void BoxCollider::UpdateBounds() { // Cache bounds @@ -152,32 +129,11 @@ void BoxCollider::UpdateBounds() BoundingSphere::FromBox(_box, _sphere); } -void BoxCollider::CreateShape() +void BoxCollider::GetGeometry(PxGeometryHolder& geometry) { - // Setup shape geometry - _cachedScale = GetScale(); Vector3 size = _size * _cachedScale; size.Absolute(); const float minSize = 0.001f; - const PxBoxGeometry geometry(Math::Max(size.X * 0.5f, minSize), Math::Max(size.Y * 0.5f, minSize), Math::Max(size.Z * 0.5f, minSize)); - - // Setup shape - CreateShapeBase(geometry); -} - -void BoxCollider::UpdateGeometry() -{ - // Check if has no shape created - if (_shape == nullptr) - return; - - // Setup shape geometry - _cachedScale = GetScale(); - Vector3 size = _size * _cachedScale; - size.Absolute(); - const float minSize = 0.001f; - const PxBoxGeometry geometry(Math::Max(size.X * 0.5f, minSize), Math::Max(size.Y * 0.5f, minSize), Math::Max(size.Z * 0.5f, minSize)); - - // Setup shape - _shape->setGeometry(geometry); + const PxBoxGeometry box(Math::Max(size.X * 0.5f, minSize), Math::Max(size.Y * 0.5f, minSize), Math::Max(size.Z * 0.5f, minSize)); + geometry.storeAny(box); } diff --git a/Source/Engine/Physics/Colliders/BoxCollider.h b/Source/Engine/Physics/Colliders/BoxCollider.h index ac381e878..89850b101 100644 --- a/Source/Engine/Physics/Colliders/BoxCollider.h +++ b/Source/Engine/Physics/Colliders/BoxCollider.h @@ -47,12 +47,6 @@ public: return _bounds; } -private: - -#if USE_EDITOR - void DrawPhysicsDebug(RenderView& view); -#endif - public: // [Collider] @@ -67,11 +61,9 @@ public: protected: // [Collider] -#if USE_EDITOR - void OnEnable() override; - void OnDisable() override; -#endif void UpdateBounds() override; - void CreateShape() override; - void UpdateGeometry() override; + void GetGeometry(PxGeometryHolder& geometry) override; +#if USE_EDITOR + void DrawPhysicsDebug(RenderView& view) override; +#endif }; diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.cpp b/Source/Engine/Physics/Colliders/CapsuleCollider.cpp index e3e717839..d3bc4dc30 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.cpp +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.cpp @@ -4,9 +4,6 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Physics/Utilities.h" #include -#if USE_EDITOR -#include "Engine/Level/Scene/SceneRendering.h" -#endif CapsuleCollider::CapsuleCollider(const SpawnParams& params) : Collider(params) @@ -93,26 +90,6 @@ void CapsuleCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* DESERIALIZE_MEMBER(Height, _height); } -#if USE_EDITOR - -void CapsuleCollider::OnEnable() -{ - GetSceneRendering()->AddPhysicsDebug(this); - - // Base - Collider::OnEnable(); -} - -void CapsuleCollider::OnDisable() -{ - GetSceneRendering()->RemovePhysicsDebug(this); - - // Base - Collider::OnDisable(); -} - -#endif - void CapsuleCollider::UpdateBounds() { // Cache bounds @@ -123,34 +100,12 @@ void CapsuleCollider::UpdateBounds() BoundingSphere::FromBox(_box, _sphere); } -void CapsuleCollider::CreateShape() +void CapsuleCollider::GetGeometry(PxGeometryHolder& geometry) { - // Setup shape geometry - _cachedScale = GetScale(); const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float minSize = 0.001f; const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); const float height = Math::Max(Math::Abs(_height) * scaling, minSize); - const PxCapsuleGeometry geometry(radius, height * 0.5f); - - // Setup shape - CreateShapeBase(geometry); -} - -void CapsuleCollider::UpdateGeometry() -{ - // Check if has no shape created - if (_shape == nullptr) - return; - - // Setup shape geometry - _cachedScale = GetScale(); - const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float minSize = 0.001f; - const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize); - const float height = Math::Max(Math::Abs(_height) * scaling, minSize); - const PxCapsuleGeometry geometry(radius, height * 0.5f); - - // Setup shape - _shape->setGeometry(geometry); + const PxCapsuleGeometry capsule(radius, height * 0.5f); + geometry.storeAny(capsule); } diff --git a/Source/Engine/Physics/Colliders/CapsuleCollider.h b/Source/Engine/Physics/Colliders/CapsuleCollider.h index 5efe18c13..4a66ca877 100644 --- a/Source/Engine/Physics/Colliders/CapsuleCollider.h +++ b/Source/Engine/Physics/Colliders/CapsuleCollider.h @@ -63,12 +63,6 @@ public: /// API_PROPERTY() void SetHeight(float value); -private: - -#if USE_EDITOR - void DrawPhysicsDebug(RenderView& view); -#endif - public: // [Collider] @@ -82,11 +76,9 @@ public: protected: // [Collider] -#if USE_EDITOR - void OnEnable() override; - void OnDisable() override; -#endif void UpdateBounds() override; - void CreateShape() override; - void UpdateGeometry() override; + void GetGeometry(PxGeometryHolder& geometry) override; +#if USE_EDITOR + void DrawPhysicsDebug(RenderView& view) override; +#endif }; diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index e0630573e..c7809dd78 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -7,9 +7,6 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Engine/Time.h" #include "Engine/Physics/PhysicalMaterial.h" -#if USE_EDITOR -#include "Engine/Level/Scene/SceneRendering.h" -#endif #include #include #include @@ -276,6 +273,11 @@ void CharacterController::UpdateGeometry() UpdateSize(); } +void CharacterController::GetGeometry(PxGeometryHolder& geometry) +{ + // Unused +} + void CharacterController::UpdateLayerBits() { // Base @@ -309,26 +311,6 @@ void CharacterController::EndPlay() _shape = nullptr; } -#if USE_EDITOR - -void CharacterController::OnEnable() -{ - GetSceneRendering()->AddPhysicsDebug(this); - - // Base - Collider::OnEnable(); -} - -void CharacterController::OnDisable() -{ - GetSceneRendering()->RemovePhysicsDebug(this); - - // Base - Collider::OnDisable(); -} - -#endif - void CharacterController::OnActiveInTreeChanged() { // Skip collider base diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 15ca75a7e..f850b9124 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -212,12 +212,6 @@ protected: /// void UpdateSize() const; -private: - -#if USE_EDITOR - void DrawPhysicsDebug(RenderView& view); -#endif - public: // [Collider] @@ -240,12 +234,12 @@ protected: // [PhysicsActor] void UpdateGeometry() override; + void GetGeometry(PxGeometryHolder& geometry) override; void UpdateLayerBits() override; void BeginPlay(SceneBeginData* data) override; void EndPlay() override; #if USE_EDITOR - void OnEnable() override; - void OnDisable() override; + void DrawPhysicsDebug(RenderView& view) override; #endif void OnActiveInTreeChanged() override; void OnParentChanged() override; diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 2eeb266aa..a1a84a523 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -2,6 +2,9 @@ #include "Collider.h" #include "Engine/Core/Log.h" +#if USE_EDITOR +#include "Engine/Level/Scene/SceneRendering.h" +#endif #include "Engine/Serialization/Serialization.h" #include "Engine/Physics/Utilities.h" #include "Engine/Physics/PhysicsSettings.h" @@ -182,6 +185,16 @@ bool Collider::IsAttached() const return _shape && _shape->getActor() != nullptr; } +bool Collider::CanAttach(RigidBody* rigidBody) const +{ + return true; +} + +bool Collider::CanBeTrigger() const +{ + return true; +} + RigidBody* Collider::GetAttachedRigidBody() const { if (_shape && _staticActor == nullptr) @@ -193,6 +206,26 @@ RigidBody* Collider::GetAttachedRigidBody() const return nullptr; } +#if USE_EDITOR + +void Collider::OnEnable() +{ + GetSceneRendering()->AddPhysicsDebug(this); + + // Base + Actor::OnEnable(); +} + +void Collider::OnDisable() +{ + // Base + Actor::OnDisable(); + + GetSceneRendering()->RemovePhysicsDebug(this); +} + +#endif + void Collider::Attach(RigidBody* rigidBody) { ASSERT(CanAttach(rigidBody)); @@ -238,11 +271,14 @@ void Collider::UpdateLayerBits() _shape->setQueryFilterData(filterData); } -void Collider::CreateShapeBase(const PxGeometry& geometry) +void Collider::CreateShape() { - ASSERT(_shape == nullptr); + // Setup shape geometry + _cachedScale = GetScale(); + PxGeometryHolder geometry; + GetGeometry(geometry); - // Prepare + // Create shape const bool isTrigger = _isTrigger && CanBeTrigger(); const PxShapeFlags shapeFlags = GetShapeFlags(isTrigger, IsActiveInHierarchy()); PxMaterial* material = Physics::GetDefaultMaterial(); @@ -250,17 +286,62 @@ void Collider::CreateShapeBase(const PxGeometry& geometry) { material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial(); } - - // Create shape - _shape = CPhysX->createShape(geometry, *material, true, shapeFlags); + ASSERT(_shape == nullptr); + _shape = CPhysX->createShape(geometry.any(), *material, true, shapeFlags); ASSERT(_shape); _shape->userData = this; - - // Setup properties _shape->setContactOffset(Math::Max(_shape->getRestOffset() + ZeroTolerance, _contactOffset)); UpdateLayerBits(); } +void Collider::UpdateGeometry() +{ + if (_shape == nullptr) + return; + + // Setup shape geometry + _cachedScale = GetScale(); + PxGeometryHolder geometry; + GetGeometry(geometry); + + // Recreate shape if geometry has different type + if (_shape->getGeometryType() != geometry.getType()) + { + // Detach from the actor + auto actor = _shape->getActor(); + if (actor) + actor->detachShape(*_shape); + + // Release shape + Physics::RemoveCollider(this); + _shape->release(); + _shape = nullptr; + + // Recreate shape + CreateShape(); + + // Reattach again (only if can, see CanAttach function) + if (actor) + { + const auto rigidBody = dynamic_cast(GetParent()); + if (_staticActor != nullptr || (rigidBody && CanAttach(rigidBody))) + { + actor->attachShape(*_shape); + } + else + { + // Be static triangle mesh + CreateStaticActor(); + } + } + + return; + } + + // Update shape + _shape->setGeometry(geometry.any()); +} + void Collider::CreateStaticActor() { ASSERT(_staticActor == nullptr); @@ -285,6 +366,14 @@ void Collider::RemoveStaticActor() _staticActor = nullptr; } +#if USE_EDITOR + +void Collider::DrawPhysicsDebug(RenderView& view) +{ +} + +#endif + void Collider::OnMaterialChanged() { // Update the shape material diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index c836a7886..3e7b5ecfd 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -160,19 +160,13 @@ public: /// /// The rigid body. /// true if this collider can be attached the specified rigid body; otherwise, false. - virtual bool CanAttach(RigidBody* rigidBody) const - { - return true; - } + virtual bool CanAttach(RigidBody* rigidBody) const; /// /// Determines whether this collider can be a trigger shape. /// /// true if this collider can be trigger; otherwise, false. - virtual bool CanBeTrigger() const - { - return true; - } + virtual bool CanBeTrigger() const; /// /// Attaches collider to the specified rigid body. @@ -198,20 +192,20 @@ protected: virtual void UpdateBounds() = 0; /// - /// Creates the collider shape. + /// Gets the collider shape geometry. /// - virtual void CreateShape() = 0; + /// The output geometry. + virtual void GetGeometry(PxGeometryHolder& geometry) = 0; /// - /// Creates the collider shape from the given geometry. + /// Creates the collider shape. /// - /// The geometry. - void CreateShapeBase(const PxGeometry& geometry); + virtual void CreateShape(); /// /// Updates the shape geometry. /// - virtual void UpdateGeometry() = 0; + virtual void UpdateGeometry(); /// /// Creates the static actor. @@ -223,6 +217,10 @@ protected: /// void RemoveStaticActor(); +#if USE_EDITOR + virtual void DrawPhysicsDebug(RenderView& view); +#endif + private: void OnMaterialChanged(); @@ -237,6 +235,10 @@ public: protected: // [PhysicsColliderActor] +#if USE_EDITOR + void OnEnable() override; + void OnDisable() override; +#endif void BeginPlay(SceneBeginData* data) override; void EndPlay() override; void OnActiveInTreeChanged() override; diff --git a/Source/Engine/Physics/Colliders/MeshCollider.cpp b/Source/Engine/Physics/Colliders/MeshCollider.cpp index 1b72d83eb..7ba76f963 100644 --- a/Source/Engine/Physics/Colliders/MeshCollider.cpp +++ b/Source/Engine/Physics/Colliders/MeshCollider.cpp @@ -5,11 +5,6 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Physics/Utilities.h" #include "Engine/Physics/Physics.h" -#include -#include -#if USE_EDITOR -#include "Engine/Level/Scene/SceneRendering.h" -#endif MeshCollider::MeshCollider(const SpawnParams& params) : Collider(params) @@ -117,26 +112,6 @@ void MeshCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* mo DESERIALIZE(CollisionData); } -#if USE_EDITOR - -void MeshCollider::OnEnable() -{ - GetSceneRendering()->AddPhysicsDebug(this); - - // Base - Collider::OnEnable(); -} - -void MeshCollider::OnDisable() -{ - GetSceneRendering()->RemovePhysicsDebug(this); - - // Base - Collider::OnDisable(); -} - -#endif - void MeshCollider::UpdateBounds() { // Cache bounds @@ -149,11 +124,10 @@ void MeshCollider::UpdateBounds() BoundingSphere::FromBox(_box, _sphere); } -void MeshCollider::CreateShape() +void MeshCollider::GetGeometry(PxGeometryHolder& geometry) { // Prepare scale - Vector3 scale = GetScale(); - _cachedScale = scale; + Vector3 scale = _cachedScale; scale.Absolute(); const float minSize = 0.001f; scale = Vector3::Max(scale, minSize); @@ -165,100 +139,23 @@ void MeshCollider::CreateShape() if (type == CollisionDataType::ConvexMesh) { // Convex mesh - PxConvexMeshGeometry geometry; - geometry.scale.scale = C2P(scale); - geometry.convexMesh = CollisionData->GetConvex(); - CreateShapeBase(geometry); + PxConvexMeshGeometry convexMesh; + convexMesh.scale.scale = C2P(scale); + convexMesh.convexMesh = CollisionData->GetConvex(); + geometry.storeAny(convexMesh); } else if (type == CollisionDataType::TriangleMesh) { // Triangle mesh - PxTriangleMeshGeometry geometry; - geometry.scale.scale = C2P(scale); - geometry.triangleMesh = CollisionData->GetTriangle(); - CreateShapeBase(geometry); + PxTriangleMeshGeometry triangleMesh; + triangleMesh.scale.scale = C2P(scale); + triangleMesh.triangleMesh = CollisionData->GetTriangle(); + geometry.storeAny(triangleMesh); } else { // Dummy geometry - const PxSphereGeometry geometry(0.01f); - CreateShapeBase(geometry); - } -} - -void MeshCollider::UpdateGeometry() -{ - // Check if has no shape created - if (_shape == nullptr) - return; - - // Recreate shape if geometry has different type - CollisionDataType type = CollisionDataType::None; - if (CollisionData && CollisionData->IsLoaded()) - type = CollisionData->GetOptions().Type; - if ((type == CollisionDataType::ConvexMesh && _shape->getGeometryType() != PxGeometryType::eCONVEXMESH) - || (type == CollisionDataType::TriangleMesh && _shape->getGeometryType() != PxGeometryType::eTRIANGLEMESH) - || (type == CollisionDataType::None && _shape->getGeometryType() != PxGeometryType::eSPHERE) - ) - { - // Detach from the actor - auto actor = _shape->getActor(); - if (actor) - actor->detachShape(*_shape); - - // Release shape - Physics::RemoveCollider(this); - _shape->release(); - _shape = nullptr; - - // Recreate shape - CreateShape(); - - // Reattach again (only if can, see CanAttach function) - if (actor) - { - if (_staticActor != nullptr || type != CollisionDataType::TriangleMesh) - { - actor->attachShape(*_shape); - } - else - { - // Be static triangle mesh - CreateStaticActor(); - } - } - - return; - } - - // Prepare scale - Vector3 scale = GetScale(); - _cachedScale = scale; - scale.Absolute(); - const float minSize = 0.001f; - scale = Vector3::Max(scale, minSize); - - // Setup shape (based on type) - if (type == CollisionDataType::ConvexMesh) - { - // Convex mesh - PxConvexMeshGeometry geometry; - geometry.scale.scale = C2P(scale); - geometry.convexMesh = CollisionData->GetConvex(); - _shape->setGeometry(geometry); - } - else if (type == CollisionDataType::TriangleMesh) - { - // Triangle mesh - PxTriangleMeshGeometry geometry; - geometry.scale.scale = C2P(scale); - geometry.triangleMesh = CollisionData->GetTriangle(); - _shape->setGeometry(geometry); - } - else - { - // Dummy geometry - const PxSphereGeometry geometry(0.01f); - _shape->setGeometry(geometry); + const PxSphereGeometry sphere(minSize); + geometry.storeAny(sphere); } } diff --git a/Source/Engine/Physics/Colliders/MeshCollider.h b/Source/Engine/Physics/Colliders/MeshCollider.h index 22dbd755f..efcf60cee 100644 --- a/Source/Engine/Physics/Colliders/MeshCollider.h +++ b/Source/Engine/Physics/Colliders/MeshCollider.h @@ -25,9 +25,6 @@ private: void OnCollisionDataChanged(); void OnCollisionDataLoaded(); -#if USE_EDITOR - void DrawPhysicsDebug(RenderView& view); -#endif public: @@ -45,10 +42,8 @@ protected: // [Collider] #if USE_EDITOR - void OnEnable() override; - void OnDisable() override; + void DrawPhysicsDebug(RenderView& view) override; #endif void UpdateBounds() override; - void CreateShape() override; - void UpdateGeometry() override; + void GetGeometry(PxGeometryHolder& geometry) override; }; diff --git a/Source/Engine/Physics/Colliders/SphereCollider.cpp b/Source/Engine/Physics/Colliders/SphereCollider.cpp index ef308cf8a..b602b8f84 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.cpp +++ b/Source/Engine/Physics/Colliders/SphereCollider.cpp @@ -4,9 +4,6 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Physics/Utilities.h" #include -#if USE_EDITOR -#include "Engine/Level/Scene/SceneRendering.h" -#endif SphereCollider::SphereCollider(const SpawnParams& params) : Collider(params) @@ -67,26 +64,6 @@ void SphereCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* DESERIALIZE_MEMBER(Radius, _radius); } -#if USE_EDITOR - -void SphereCollider::OnEnable() -{ - GetSceneRendering()->AddPhysicsDebug(this); - - // Base - Collider::OnEnable(); -} - -void SphereCollider::OnDisable() -{ - GetSceneRendering()->RemovePhysicsDebug(this); - - // Base - Collider::OnDisable(); -} - -#endif - void SphereCollider::UpdateBounds() { // Cache bounds @@ -95,32 +72,11 @@ void SphereCollider::UpdateBounds() _sphere.GetBoundingBox(_box); } -void SphereCollider::CreateShape() +void SphereCollider::GetGeometry(PxGeometryHolder& geometry) { - // Setup shape geometry - _cachedScale = GetScale(); const float scaling = _cachedScale.GetAbsolute().MaxValue(); const float radius = Math::Abs(_radius) * scaling; const float minSize = 0.001f; - const PxSphereGeometry geometry(Math::Max(radius, minSize)); - - // Setup shape - CreateShapeBase(geometry); -} - -void SphereCollider::UpdateGeometry() -{ - // Check if has no shape created - if (_shape == nullptr) - return; - - // Setup shape geometry - _cachedScale = GetScale(); - const float scaling = _cachedScale.GetAbsolute().MaxValue(); - const float radius = Math::Abs(_radius) * scaling; - const float minSize = 0.001f; - const PxSphereGeometry geometry(Math::Max(radius, minSize)); - - // Setup shape - _shape->setGeometry(geometry); + const PxSphereGeometry sphere(Math::Max(radius, minSize)); + geometry.storeAny(sphere); } diff --git a/Source/Engine/Physics/Colliders/SphereCollider.h b/Source/Engine/Physics/Colliders/SphereCollider.h index 0835152f0..ed2d4cd09 100644 --- a/Source/Engine/Physics/Colliders/SphereCollider.h +++ b/Source/Engine/Physics/Colliders/SphereCollider.h @@ -37,12 +37,6 @@ public: /// API_PROPERTY() void SetRadius(float value); -private: - -#if USE_EDITOR - void DrawPhysicsDebug(RenderView& view); -#endif - public: // [Collider] @@ -57,10 +51,8 @@ protected: // [Collider] #if USE_EDITOR - void OnEnable() override; - void OnDisable() override; + void DrawPhysicsDebug(RenderView& view) override; #endif void UpdateBounds() override; - void CreateShape() override; - void UpdateGeometry() override; + void GetGeometry(PxGeometryHolder& geometry) override; }; diff --git a/Source/Engine/Physics/Types.h b/Source/Engine/Physics/Types.h index 80a4e30b9..6465dd900 100644 --- a/Source/Engine/Physics/Types.h +++ b/Source/Engine/Physics/Types.h @@ -28,6 +28,7 @@ namespace physx class PxFoundation; class PxShape; class PxGeometry; + class PxGeometryHolder; class PxProfileZoneManager; class PxMaterial; class PxPvd; From 29521b1b03aeaa586e3c19930bf3b096d1000ec3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 17:19:45 +0100 Subject: [PATCH 362/419] Remove Spline init from OnEnable to prevent double setup on SplineModel and SplineCollider --- Source/Engine/Level/Actors/Spline.cpp | 9 --------- Source/Engine/Level/Actors/Spline.h | 1 - 2 files changed, 10 deletions(-) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 08a06d433..1a5e580bb 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -505,12 +505,3 @@ void Spline::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier UpdateSpline(); } } - -void Spline::OnEnable() -{ - // Base - Actor::OnEnable(); - - // Initialize spline - UpdateSpline(); -} diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index 7d7e8dcb1..747530703 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -382,5 +382,4 @@ public: #endif void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; - void OnEnable() override; }; From 6c253ce8922e37df96efe2dd174c31f95ce77302 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 17:20:05 +0100 Subject: [PATCH 363/419] Fix DebugDraw DrawTriangles crash --- Source/Engine/Debug/DebugDraw.cpp | 18 ++++++++++++++---- Source/Engine/Debug/DebugDraw.h | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 1f7e0c596..4341695b7 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -869,9 +869,9 @@ void DebugDraw::DrawTriangles(const Span& vertices, const Color& color, list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles; else list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles; - list->EnsureCapacity(list->Count() + vertices.Length()); + list->EnsureCapacity(list->Count() + vertices.Length() / 3); - for (int32 i = 0; i < vertices.Length() * 3;) + for (int32 i = 0; i < vertices.Length();) { t.V0 = vertices[i++]; t.V1 = vertices[i++]; @@ -881,6 +881,11 @@ void DebugDraw::DrawTriangles(const Span& vertices, const Color& color, } } +void DebugDraw::DrawTriangles(const Array& vertices, const Color& color, float duration, bool depthTest) +{ + DrawTriangles(Span(vertices.Get(), vertices.Count()), color, duration, depthTest); +} + void DebugDraw::DrawTriangles(const Span& vertices, const Span& indices, const Color& color, float duration, bool depthTest) { ASSERT(indices.Length() % 3 == 0); @@ -894,9 +899,9 @@ void DebugDraw::DrawTriangles(const Span& vertices, const Span& list = duration > 0 ? &DebugDrawDepthTest.DefaultTriangles : &DebugDrawDepthTest.OneFrameTriangles; else list = duration > 0 ? &DebugDrawDefault.DefaultTriangles : &DebugDrawDefault.OneFrameTriangles; - list->EnsureCapacity(list->Count() + indices.Length()); + list->EnsureCapacity(list->Count() + indices.Length() / 3); - for (int32 i = 0; i < indices.Length() * 3;) + for (int32 i = 0; i < indices.Length();) { t.V0 = vertices[indices[i++]]; t.V1 = vertices[indices[i++]]; @@ -906,6 +911,11 @@ void DebugDraw::DrawTriangles(const Span& vertices, const Span& } } +void DebugDraw::DrawTriangles(const Array& vertices, const Array& indices, const Color& color, float duration, bool depthTest) +{ + DrawTriangles(Span(vertices.Get(), vertices.Count()), Span(indices.Get(), indices.Count()), color, duration, depthTest); +} + void DebugDraw::DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color, float duration, bool depthTest) { // Check if has no length (just sphere) diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index 6027c6697..81c704894 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -113,6 +113,15 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw); /// If set to true depth test will be performed, otherwise depth will be ignored. API_FUNCTION() static void DrawTriangles(const Span& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + /// + /// Draws the triangles. + /// + /// The triangle vertices list (must have multiple of 3 elements). + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + static void DrawTriangles(const Array& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + /// /// Draws the triangles using the given index buffer. /// @@ -123,6 +132,16 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw); /// If set to true depth test will be performed, otherwise depth will be ignored. API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + /// + /// Draws the triangles using the given index buffer. + /// + /// The triangle vertices list. + /// The triangle indices list (must have multiple of 3 elements). + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + static void DrawTriangles(const Array& vertices, const Array& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + /// /// Draws the wireframe box. /// From 3a3c66b916b8a24d92917d5e32452d419e0e1283 Mon Sep 17 00:00:00 2001 From: Vizepi Date: Wed, 10 Feb 2021 20:32:32 +0100 Subject: [PATCH 364/419] [EDITOR] Fixed layer matrix order ( issue #153 ) --- Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs b/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs index 44a273ee4..6c825f46d 100644 --- a/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/LayersMatrixEditor.cs @@ -76,7 +76,7 @@ namespace FlaxEditor.CustomEditors.Dedicated upperRightCell.AddChild(new Label { Height = labelsHeight, - Text = layerNames[layerIndex], + Text = layerNames[layerNames.Length - layerIndex - 1], HorizontalAlignment = TextAlignment.Near, }); bottomLeftCell.AddChild(new Label From 2263b3db523fb2c1d724cf974bdd7e9979b4066f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 22:59:32 +0100 Subject: [PATCH 365/419] Add ScriptingTypeHandle debugger view to flax.natvis --- Source/flax.natvis | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/flax.natvis b/Source/flax.natvis index 15ccaa7e6..550d8a4d4 100644 --- a/Source/flax.natvis +++ b/Source/flax.natvis @@ -206,4 +206,13 @@ + + + Null + Type={Module->Types._allocation._data[TypeIndex].Fullname} + + Module->Types._allocation._data[TypeIndex] + + + From 4c673bec9b9a93d1cd8063c1cb6e7e7a11f65142 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 22:59:40 +0100 Subject: [PATCH 366/419] Add` BAGUETTE` --- Source/Editor/Windows/SplashScreen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index b05c73761..bc92a8d2a 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -115,6 +115,7 @@ const Char* SplashScreenQuotes[] = TEXT("That's what she said"), TEXT("Compiling Shaders (93,788)"), TEXT("Hi There"), + TEXT("BAGUETTE"), }; SplashScreen::~SplashScreen() From 8158c94d26c1cc233933e638b810f55602f7f394 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Feb 2021 23:13:16 +0100 Subject: [PATCH 367/419] Fix low-level `WindowsPlatform::Log` to not print invalid characters --- .../Engine/Platform/Windows/WindowsPlatform.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 0b1f70989..fa93800d8 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -630,8 +630,19 @@ void WindowsPlatform::Exit() void WindowsPlatform::Log(const StringView& msg) { - OutputDebugStringW(msg.Get()); - OutputDebugStringW(TEXT(PLATFORM_LINE_TERMINATOR)); + Char buffer[500]; + Char* str; + if (msg.Length() + 3 < ARRAY_COUNT(buffer)) + str = buffer; + else + str = new Char[msg.Length() + 3]; + MemoryCopy(str, msg.Get(), msg.Length() * sizeof(Char)); + str[msg.Length() + 0] = '\r'; + str[msg.Length() + 1] = '\n'; + str[msg.Length() + 2] = 0; + OutputDebugStringW(str); + if (str != buffer) + Free(str); } bool WindowsPlatform::IsDebuggerPresent() From 49758fbfff6fd0dc13bdcddd0426d81b0a7fdeaf Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Feb 2021 16:47:43 +0100 Subject: [PATCH 368/419] Add Spline Collider --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 16 +- Source/Engine/Level/Actors/Spline.cpp | 5 + Source/Engine/Level/Actors/Spline.h | 5 +- .../Physics/Colliders/SplineCollider.cpp | 329 ++++++++++++++++++ .../Engine/Physics/Colliders/SplineCollider.h | 61 ++++ 5 files changed, 410 insertions(+), 6 deletions(-) create mode 100644 Source/Engine/Physics/Colliders/SplineCollider.cpp create mode 100644 Source/Engine/Physics/Colliders/SplineCollider.h diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 1eeb55511..9ce64efa9 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -308,10 +308,11 @@ namespace FlaxEditor.SceneGraph.Actors { base.OnContextMenu(contextMenu); - contextMenu.AddButton("Add spline model", OnAddSplineMode); + contextMenu.AddButton("Add spline model", OnAddSplineModel); + contextMenu.AddButton("Add spline collider", OnAddSplineCollider); } - private void OnAddSplineMode() + private void OnAddSplineModel() { var actor = new SplineModel { @@ -321,6 +322,17 @@ namespace FlaxEditor.SceneGraph.Actors Editor.Instance.SceneEditing.Spawn(actor, Actor); } + private void OnAddSplineCollider() + { + var actor = new SplineCollider + { + StaticFlags = Actor.StaticFlags, + Transform = Actor.Transform, + }; + // TODO: auto pick the collision data if already using spline model + Editor.Instance.SceneEditing.Spawn(actor, Actor); + } + /// public override void OnDispose() { diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 1a5e580bb..76ecb9653 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -10,6 +10,11 @@ Spline::Spline(const SpawnParams& params) { } +bool Spline::GetIsLoop() const +{ + return _loop; +} + void Spline::SetIsLoop(bool value) { if (_loop != value) diff --git a/Source/Engine/Level/Actors/Spline.h b/Source/Engine/Level/Actors/Spline.h index 747530703..f19f9a443 100644 --- a/Source/Engine/Level/Actors/Spline.h +++ b/Source/Engine/Level/Actors/Spline.h @@ -28,10 +28,7 @@ public: /// Whether to use spline as closed loop. In that case, ensure to place start and end at the same location. /// API_PROPERTY(Attributes="EditorOrder(0), EditorDisplay(\"Spline\")") - FORCE_INLINE bool GetIsLoop() const - { - return _loop; - } + bool GetIsLoop() const; /// /// Whether to use spline as closed loop. In that case, ensure to place start and end at the same location. diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp new file mode 100644 index 000000000..a529baf7d --- /dev/null +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -0,0 +1,329 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "SplineCollider.h" +#include "Engine/Core/Log.h" +#include "Engine/Core/Math/Matrix.h" +#include "Engine/Level/Actors/Spline.h" +#include "Engine/Serialization/Serialization.h" +#include "Engine/Physics/Utilities.h" +#include "Engine/Physics/Physics.h" +#include "Engine/Profiler/ProfilerCPU.h" +#if COMPILE_WITH_PHYSICS_COOKING +#include "Engine/Physics/CollisionCooking.h" +#include +#include +#endif +#include + +SplineCollider::SplineCollider(const SpawnParams& params) + : Collider(params) +{ + CollisionData.Changed.Bind(this); + CollisionData.Loaded.Bind(this); +} + +void SplineCollider::OnCollisionDataChanged() +{ + // This should not be called during physics simulation, if it happened use write lock on physx scene + ASSERT(!Physics::IsDuringSimulation()); + + if (CollisionData) + { + // Ensure that collision asset is loaded (otherwise objects might fall though collider that is not yet loaded on play begin) + CollisionData->WaitForLoaded(); + } + + UpdateGeometry(); +} + +void SplineCollider::OnCollisionDataLoaded() +{ + UpdateGeometry(); +} + +void SplineCollider::OnSplineUpdated() +{ + if (!_spline || !IsActiveInHierarchy() || _spline->GetSplinePointsCount() < 2 || !CollisionData || !CollisionData->IsLoaded()) + { + _box = BoundingBox(_transform.Translation, _transform.Translation); + BoundingSphere::FromBox(_box, _sphere); + return; + } + + UpdateGeometry(); +} + +bool SplineCollider::CanAttach(RigidBody* rigidBody) const +{ + return false; +} + +bool SplineCollider::CanBeTrigger() const +{ + return false; +} + +#if USE_EDITOR + +#include "Engine/Debug/DebugDraw.h" + +void SplineCollider::DrawPhysicsDebug(RenderView& view) +{ + DEBUG_DRAW_TRIANGLES_EX(_vertexBuffer, _indexBuffer, Color::GreenYellow * 0.8f, 0, true); +} + +void SplineCollider::OnDebugDrawSelected() +{ + DEBUG_DRAW_TRIANGLES_EX(_vertexBuffer, _indexBuffer, Color::GreenYellow, 0, false); + + // Base + Collider::OnDebugDrawSelected(); +} + +#endif + +bool SplineCollider::IntersectsItself(const Ray& ray, float& distance, Vector3& normal) +{ + // Use detailed hit + if (_shape) + { + RayCastHit hitInfo; + if (!RayCast(ray.Position, ray.Direction, hitInfo)) + return false; + distance = hitInfo.Distance; + normal = hitInfo.Normal; + return true; + } + + // Fallback to AABB + return _box.Intersects(ray, distance, normal); +} + +void SplineCollider::Serialize(SerializeStream& stream, const void* otherObj) +{ + // Base + Collider::Serialize(stream, otherObj); + + SERIALIZE_GET_OTHER_OBJ(SplineCollider); + + SERIALIZE(CollisionData); +} + +void SplineCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + // Base + Collider::Deserialize(stream, modifier); + + DESERIALIZE(CollisionData); +} + +void SplineCollider::OnParentChanged() +{ + if (_spline) + { + _spline->SplineUpdated.Unbind(this); + } + + // Base + Collider::OnParentChanged(); + + _spline = Cast(_parent); + if (_spline) + { + _spline->SplineUpdated.Bind(this); + } + + OnSplineUpdated(); +} + +void SplineCollider::EndPlay() +{ + // Base + Collider::EndPlay(); + + // Cleanup + if (_triangleMesh) + { + Physics::RemoveObject(_triangleMesh); + _triangleMesh = nullptr; + } +} + +void SplineCollider::UpdateBounds() +{ + // Unused as bounds are updated during collision building +} + +void SplineCollider::GetGeometry(PxGeometryHolder& geometry) +{ + // Reset bounds + _box = BoundingBox(_transform.Translation, _transform.Translation); + BoundingSphere::FromBox(_box, _sphere); + + // Skip if sth is missing + if (!_spline || !IsActiveInHierarchy() || _spline->GetSplinePointsCount() < 2 || !CollisionData || !CollisionData->IsLoaded()) + { + geometry.storeAny(PxSphereGeometry(0.001f)); + return; + } + PROFILE_CPU(); + + // Extract collision geometry + // TODO: cache memory allocation for dynamic colliders + Array collisionVertices; + Array collisionIndices; + CollisionData->ExtractGeometry(collisionVertices, collisionIndices); + if (collisionIndices.IsEmpty()) + { + geometry.storeAny(PxSphereGeometry(0.001f)); + return; + } + + // Apply local mesh transformation + const Transform localTransform = _localTransform; + if (!localTransform.IsIdentity()) + { + for (int32 i = 0; i < collisionVertices.Count(); i++) + collisionVertices[i] = localTransform.LocalToWorld(collisionVertices[i]); + } + + // Find collision geometry local bounds + BoundingBox localModelBounds; + localModelBounds.Minimum = localModelBounds.Maximum = collisionVertices[0]; + for (int32 i = 1; i < collisionVertices.Count(); i++) + { + Vector3 v = collisionVertices[i]; + localModelBounds.Minimum = Vector3::Min(localModelBounds.Minimum, v); + localModelBounds.Maximum = Vector3::Max(localModelBounds.Maximum, v); + } + auto localModelBoundsSize = localModelBounds.GetSize(); + + // Deform geometry over the spline + const auto& keyframes = _spline->Curve.GetKeyframes(); + const int32 segments = keyframes.Count() - 1; + _vertexBuffer.Resize(collisionVertices.Count() * segments); + _indexBuffer.Resize(collisionIndices.Count() * segments); + const Transform splineTransform = _spline->GetTransform(); + const Transform colliderTransform = GetTransform(); + Transform curveTransform, leftTangent, rightTangent; + for (int32 segment = 0; segment < segments; segment++) + { + // Setup for the spline segment + auto offsetVertices = segment * collisionVertices.Count(); + auto offsetIndices = segment * collisionIndices.Count(); + const auto& start = keyframes[segment]; + const auto& end = keyframes[segment + 1]; + const float length = end.Time - start.Time; + AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent); + AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent); + + // Vertex buffer is deformed along the spline + auto srcVertices = collisionVertices.Get(); + auto dstVertices = _vertexBuffer.Get() + offsetVertices; + for (int32 i = 0; i < collisionVertices.Count(); i++) + { + Vector3 v = srcVertices[i]; + const float alpha = Math::Saturate((v.Z - localModelBounds.Minimum.Z) / localModelBoundsSize.Z); + v.Z = alpha; + + // Evaluate transformation at the curve + AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, curveTransform); + + // Apply spline direction (from position 1st derivative) + Vector3 direction; + AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction); + direction.Normalize(); + Quaternion orientation; + if (direction.IsZero()) + orientation = Quaternion::Identity; + else if (Vector3::Dot(direction, Vector3::Up) >= 0.999f) + Quaternion::RotationAxis(Vector3::Left, PI_HALF, orientation); + else + Quaternion::LookRotation(direction, Vector3::Cross(Vector3::Cross(direction, Vector3::Up), direction), orientation); + curveTransform.Orientation = orientation * curveTransform.Orientation; + + // Transform vertex + v = curveTransform.LocalToWorld(v); + v = splineTransform.LocalToWorld(v); + v = colliderTransform.WorldToLocal(v); + + dstVertices[i] = v; + } + + // Index buffer is the same for every segment except it's shifted + auto srcIndices = collisionIndices.Get(); + auto dstIndices = _indexBuffer.Get() + offsetIndices; + for (int32 i = 0; i < collisionIndices.Count(); i++) + dstIndices[i] = srcIndices[i] + offsetVertices; + } + + // Prepare scale + Vector3 scale = _cachedScale; + scale.Absolute(); + const float minSize = 0.001f; + scale = Vector3::Max(scale, minSize); + + // TODO: add support for cooking collision for static splines in editor and reusing it in game + +#if COMPILE_WITH_PHYSICS_COOKING + // Cook triangle mesh collision + CollisionCooking::CookingInput cookingInput; + cookingInput.VertexCount = _vertexBuffer.Count(); + cookingInput.VertexData = _vertexBuffer.Get(); + cookingInput.IndexCount = _indexBuffer.Count(); + cookingInput.IndexData = _indexBuffer.Get(); + cookingInput.Is16bitIndexData = false; + BytesContainer collisionData; + if (!CollisionCooking::CookTriangleMesh(cookingInput, collisionData)) + { + // Create triangle mesh + if (_triangleMesh) + { + Physics::RemoveObject(_triangleMesh); + _triangleMesh = nullptr; + } + PxDefaultMemoryInputData input(collisionData.Get(), collisionData.Length()); + // TODO: try using getVerticesForModification for dynamic triangle mesh vertices updating when changing curve in the editor + _triangleMesh = Physics::GetPhysics()->createTriangleMesh(input); + if (!_triangleMesh) + { + LOG(Error, "Failed to create triangle mesh from collision data of {0}.", ToString()); + geometry.storeAny(PxSphereGeometry(0.001f)); + return; + } + +#if USE_EDITOR + // Transform vertices back to world space for debug shapes drawing + for (int32 i = 0; i < _vertexBuffer.Count(); i++) + _vertexBuffer[i] = colliderTransform.LocalToWorld(_vertexBuffer[i]); +#endif + + // Update bounds + _box = P2C(_triangleMesh->getLocalBounds()); + Matrix splineWorld; + colliderTransform.GetWorld(splineWorld); + BoundingBox::Transform(_box, splineWorld, _box); + BoundingSphere::FromBox(_box, _sphere); + + // Setup geometry + PxTriangleMeshGeometry triangleMesh; + triangleMesh.scale.scale = C2P(scale); + triangleMesh.triangleMesh = _triangleMesh; + geometry.storeAny(triangleMesh); + +#if !USE_EDITOR + // Free memory for static splines (if editor collision preview is not needed) + if (IsStatic()) + { + _vertexBuffer.Resize(0); + _indexBuffer.Resize(0); + } +#endif + + return; + } +#endif + + LOG(Error, "Cannot build collision data for {0} due to runtime collision cooking diabled.", ToString()); + geometry.storeAny(PxSphereGeometry(0.001f)); +} diff --git a/Source/Engine/Physics/Colliders/SplineCollider.h b/Source/Engine/Physics/Colliders/SplineCollider.h new file mode 100644 index 000000000..b5c0cf318 --- /dev/null +++ b/Source/Engine/Physics/Colliders/SplineCollider.h @@ -0,0 +1,61 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Collider.h" +#include "Engine/Content/AssetReference.h" +#include "Engine/Physics/CollisionData.h" + +class Spline; + +/// +/// A collider represented by an arbitrary mesh that goes over the spline. +/// +/// +/// +API_CLASS() class FLAXENGINE_API SplineCollider : public Collider +{ +DECLARE_SCENE_OBJECT(SplineCollider); +private: + Spline* _spline = nullptr; + PxTriangleMesh* _triangleMesh = nullptr; + Array _vertexBuffer; + Array _indexBuffer; + +public: + + /// + /// Linked collision data asset that contains convex mesh or triangle mesh used to represent a spline collider shape. + /// + API_FIELD(Attributes="EditorOrder(100), DefaultValue(null), EditorDisplay(\"Collider\")") + AssetReference CollisionData; + +private: + + void OnCollisionDataChanged(); + void OnCollisionDataLoaded(); + void OnSplineUpdated(); + +public: + + // [Collider] + bool CanAttach(RigidBody* rigidBody) const override; + bool CanBeTrigger() const override; +#if USE_EDITOR + void OnDebugDrawSelected() override; +#endif + bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override; + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void OnParentChanged() override; + void EndPlay() override; + +protected: + + // [Collider] +#if USE_EDITOR + void DrawPhysicsDebug(RenderView& view) override; +#endif + void UpdateBounds() override; + void GetGeometry(PxGeometryHolder& geometry) override; +}; From 4ba82047e00ce38199798aabd6e0f238c26df858 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Feb 2021 16:47:50 +0100 Subject: [PATCH 369/419] Improve CollisionCooking --- Source/Engine/Physics/CollisionCooking.cpp | 33 +++++++++++++--------- Source/Engine/Physics/CollisionCooking.h | 33 ++++++++-------------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/Source/Engine/Physics/CollisionCooking.cpp b/Source/Engine/Physics/CollisionCooking.cpp index 070f49936..6769e50fc 100644 --- a/Source/Engine/Physics/CollisionCooking.cpp +++ b/Source/Engine/Physics/CollisionCooking.cpp @@ -10,6 +10,8 @@ #include #include +#define CONVEX_VERTEX_MIN 8 +#define CONVEX_VERTEX_MAX 255 #define ENSURE_CAN_COOK \ if (Physics::GetCooking() == nullptr) \ { \ @@ -27,7 +29,10 @@ bool CollisionCooking::CookConvexMesh(CookingInput& input, BytesContainer& outpu desc.points.stride = sizeof(Vector3); desc.points.data = input.VertexData; desc.flags = PxConvexFlag::eCOMPUTE_CONVEX; - desc.vertexLimit = input.ConvexVertexLimit; + if (input.ConvexVertexLimit == 0) + desc.vertexLimit = CONVEX_VERTEX_MAX; + else + desc.vertexLimit = (PxU16)Math::Clamp(input.ConvexVertexLimit, CONVEX_VERTEX_MIN, CONVEX_VERTEX_MAX); if (input.ConvexFlags & ConvexMeshGenerationFlags::SkipValidation) desc.flags |= PxConvexFlag::Enum::eDISABLE_MESH_VALIDATION; if (input.ConvexFlags & ConvexMeshGenerationFlags::UsePlaneShifting) @@ -83,9 +88,9 @@ bool CollisionCooking::CookTriangleMesh(CookingInput& input, BytesContainer& out bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::SerializedOptions& outputOptions, BytesContainer& outputData) { - int32 convexVertexLimit = Math::Clamp(arg.ConvexVertexLimit, 8, 255); + int32 convexVertexLimit = Math::Clamp(arg.ConvexVertexLimit, CONVEX_VERTEX_MIN, CONVEX_VERTEX_MAX); if (arg.ConvexVertexLimit == 0) - convexVertexLimit = 255; + convexVertexLimit = CONVEX_VERTEX_MAX; DataContainer finalVertexData; DataContainer finalIndexData; @@ -106,19 +111,19 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali auto lod = &arg.OverrideModelData->LODs[lodIndex]; const int32 meshesCount = lod->Meshes.Count(); - // Count vertex/index buffer sizes - int32 vCount = 0; - int32 iCount = 0; - for (int32 i = 0; i < meshesCount; i++) - { - const auto mesh = lod->Meshes[i]; - vCount += mesh->Positions.Count(); + // Count vertex/index buffer sizes + int32 vCount = 0; + int32 iCount = 0; + for (int32 i = 0; i < meshesCount; i++) + { + const auto mesh = lod->Meshes[i]; + vCount += mesh->Positions.Count(); - if (needIndexBuffer) - { - iCount += mesh->Indices.Count() * 3; - } + if (needIndexBuffer) + { + iCount += mesh->Indices.Count() * 3; } + } if (meshesCount == 1) { diff --git a/Source/Engine/Physics/CollisionCooking.h b/Source/Engine/Physics/CollisionCooking.h index b368af547..658f88428 100644 --- a/Source/Engine/Physics/CollisionCooking.h +++ b/Source/Engine/Physics/CollisionCooking.h @@ -24,13 +24,13 @@ public: struct CookingInput { - int32 VertexCount; - Vector3* VertexData; - int32 IndexCount; - void* IndexData; - bool Is16bitIndexData; - ConvexMeshGenerationFlags ConvexFlags; - int32 ConvexVertexLimit; + int32 VertexCount = 0; + Vector3* VertexData = nullptr; + int32 IndexCount = 0; + void* IndexData = nullptr; + bool Is16bitIndexData = false; + ConvexMeshGenerationFlags ConvexFlags = ConvexMeshGenerationFlags::None; + int32 ConvexVertexLimit = 255; }; /// @@ -38,21 +38,12 @@ public: /// struct Argument { - CollisionDataType Type; - ModelData* OverrideModelData; + CollisionDataType Type = CollisionDataType::None; + ModelData* OverrideModelData = nullptr; AssetReference Model; - int32 ModelLodIndex; - ConvexMeshGenerationFlags ConvexFlags; - int32 ConvexVertexLimit; - - Argument() - { - Type = CollisionDataType::None; - OverrideModelData = nullptr; - ModelLodIndex = 0; - ConvexFlags = ConvexMeshGenerationFlags::None; - ConvexVertexLimit = 255; - } + int32 ModelLodIndex = 0; + ConvexMeshGenerationFlags ConvexFlags = ConvexMeshGenerationFlags::None; + int32 ConvexVertexLimit = 255; }; /// From 04b650cbc36a885ca37493a5a31663d210e14479 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Feb 2021 16:48:10 +0100 Subject: [PATCH 370/419] Add Pre Rotation to Spline Model and Spline Collider --- Source/Engine/Level/Actors/SplineModel.cpp | 39 +++++++++++-------- Source/Engine/Level/Actors/SplineModel.h | 12 ++++++ .../Physics/Colliders/SplineCollider.cpp | 18 +++++++-- .../Engine/Physics/Colliders/SplineCollider.h | 12 ++++++ 4 files changed, 61 insertions(+), 20 deletions(-) diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index c8a7b7b1f..c06dfe75e 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -28,6 +28,19 @@ SplineModel::~SplineModel() Allocator::Free(_deformationBufferData); } +Quaternion SplineModel::GetPreRotation() const +{ + return _preRotation; +} + +void SplineModel::SetPreRotation(const Quaternion& value) +{ + if (_preRotation == value) + return; + _preRotation = value; + OnSplineUpdated(); +} + float SplineModel::GetQuality() const { return _quality; @@ -108,12 +121,11 @@ void SplineModel::OnSplineUpdated() const int32 segments = keyframes.Count() - 1; const int32 chunksPerSegment = Math::Clamp(Math::CeilToInt(SPLINE_RESOLUTION * _quality), 2, 1024); const float chunksPerSegmentInv = 1.0f / (float)chunksPerSegment; - const Transform splineTransform = _spline->GetTransform(); + const Transform splineTransform = GetTransform(); _instances.Resize(segments, false); - BoundingBox localModelBounds; + BoundingBox localModelBounds(Vector3::Maximum, Vector3::Minimum); { auto& meshes = Model->LODs[0].Meshes; - Vector3 tmp, min = Vector3::Maximum, max = Vector3::Minimum; Vector3 corners[8]; for (int32 j = 0; j < meshes.Count(); j++) { @@ -122,12 +134,9 @@ void SplineModel::OnSplineUpdated() for (int32 i = 0; i < 8; i++) { - //Vector3::Transform(corners[i], localTransform, tmp); - //_localTransform.LocalToWorld(corners[i], tmp); - - // Transform mesh corner using local spline model transformation but use double-precision to prevent issues when rotating model - tmp = corners[i] * _localTransform.Scale; - double rotation[4] = { (double)_localTransform.Orientation.X, (double)_localTransform.Orientation.Y, (double)_localTransform.Orientation.Z, (double)_localTransform.Orientation.W }; + // Transform mesh corner using pre-transform but use double-precision to prevent issues when rotating model + Vector3 tmp = corners[i]; + double rotation[4] = { (double)_preRotation.X, (double)_preRotation.Y, (double)_preRotation.Z, (double)_preRotation.W }; const double length = sqrt(rotation[0] * rotation[0] + rotation[1] * rotation[1] + rotation[2] * rotation[2] + rotation[3] * rotation[3]); const double inv = 1.0 / length; rotation[0] *= inv; @@ -151,13 +160,11 @@ void SplineModel::OnSplineUpdated() (float)(pos[0] * (1.0 - yy - zz) + pos[1] * (xy - wz) + pos[2] * (xz + wy)), (float)(pos[0] * (xy + wz) + pos[1] * (1.0 - xx - zz) + pos[2] * (yz - wx)), (float)(pos[0] * (xz - wy) + pos[1] * (yz + wx) + pos[2] * (1.0 - xx - yy))); - Vector3::Add(tmp, _localTransform.Translation, tmp); - min = Vector3::Min(min, tmp); - max = Vector3::Max(max, tmp); + localModelBounds.Minimum = Vector3::Min(localModelBounds.Minimum, tmp); + localModelBounds.Maximum = Vector3::Max(localModelBounds.Maximum, tmp); } } - localModelBounds = BoundingBox(min, max); } _meshMinZ = localModelBounds.Minimum.Z; _meshMaxZ = localModelBounds.Maximum.Z; @@ -185,9 +192,7 @@ void SplineModel::OnSplineUpdated() segmentPoints.Add(chunkWorld.Translation); maxScale = Math::Max(maxScale, chunkWorld.Scale.GetAbsolute().MaxValue()); } - maxScale = Math::Max(maxScale, _localTransform.Scale.GetAbsolute().MaxValue()); BoundingSphere::FromPoints(segmentPoints.Get(), segmentPoints.Count(), instance.Sphere); - instance.Sphere.Center += _localTransform.Translation; instance.Sphere.Radius *= maxScale * _boundsScale; } @@ -356,8 +361,8 @@ void SplineModel::Draw(RenderContext& renderContext) drawCall.Deformable.MeshMaxZ = _meshMaxZ; drawCall.Deformable.GeometrySize = _box.GetSize(); drawCall.PerInstanceRandom = GetPerInstanceRandom(); - _localTransform.GetWorld(drawCall.Deformable.LocalMatrix); - const Transform splineTransform = _spline->GetTransform(); + Matrix::RotationQuaternion(_preRotation, drawCall.Deformable.LocalMatrix); + const Transform splineTransform = GetTransform(); splineTransform.GetWorld(drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation(); const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant(); diff --git a/Source/Engine/Level/Actors/SplineModel.h b/Source/Engine/Level/Actors/SplineModel.h index a7d53af49..9a2d5420b 100644 --- a/Source/Engine/Level/Actors/SplineModel.h +++ b/Source/Engine/Level/Actors/SplineModel.h @@ -26,6 +26,7 @@ private: char _forcedLod = -1; bool _deformationDirty = false; Array _instances; + Quaternion _preRotation = Quaternion::Identity; Spline* _spline = nullptr; GPUBuffer* _deformationBuffer = nullptr; void* _deformationBufferData = nullptr; @@ -41,6 +42,17 @@ public: API_FIELD(Attributes="EditorOrder(20), DefaultValue(null), EditorDisplay(\"Model\")") AssetReference Model; + /// + /// Gets the rotation applied to the model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// + API_PROPERTY(Attributes="EditorOrder(21), EditorDisplay(\"Model\")") + Quaternion GetPreRotation() const; + + /// + /// Sets the rotation applied to the model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// + API_PROPERTY() void SetPreRotation(const Quaternion& value); + /// /// The draw passes to use for rendering this object. /// diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index a529baf7d..ff670fcd5 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -22,6 +22,19 @@ SplineCollider::SplineCollider(const SpawnParams& params) CollisionData.Loaded.Bind(this); } +Quaternion SplineCollider::GetPreRotation() const +{ + return _preRotation; +} + +void SplineCollider::SetPreRotation(const Quaternion& value) +{ + if (_preRotation == value) + return; + _preRotation = value; + UpdateGeometry(); +} + void SplineCollider::OnCollisionDataChanged() { // This should not be called during physics simulation, if it happened use write lock on physx scene @@ -180,11 +193,10 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry) } // Apply local mesh transformation - const Transform localTransform = _localTransform; - if (!localTransform.IsIdentity()) + if (!_preRotation.IsIdentity()) { for (int32 i = 0; i < collisionVertices.Count(); i++) - collisionVertices[i] = localTransform.LocalToWorld(collisionVertices[i]); + collisionVertices[i] = Vector3::Transform(collisionVertices[i], _preRotation); } // Find collision geometry local bounds diff --git a/Source/Engine/Physics/Colliders/SplineCollider.h b/Source/Engine/Physics/Colliders/SplineCollider.h index b5c0cf318..e7ca5a831 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.h +++ b/Source/Engine/Physics/Colliders/SplineCollider.h @@ -21,6 +21,7 @@ private: PxTriangleMesh* _triangleMesh = nullptr; Array _vertexBuffer; Array _indexBuffer; + Quaternion _preRotation = Quaternion::Identity; public: @@ -30,6 +31,17 @@ public: API_FIELD(Attributes="EditorOrder(100), DefaultValue(null), EditorDisplay(\"Collider\")") AssetReference CollisionData; + /// + /// Gets the rotation applied to the collision data model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// + API_PROPERTY(Attributes="EditorOrder(101), EditorDisplay(\"Collider\")") + Quaternion GetPreRotation() const; + + /// + /// Sets the rotation applied to the collision data model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// + API_PROPERTY() void SetPreRotation(const Quaternion& value); + private: void OnCollisionDataChanged(); From 62acae10a532e6f615a298f46e5fe5c6bd47c6d5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Feb 2021 16:48:21 +0100 Subject: [PATCH 371/419] Fix compile error --- Source/Engine/Core/Types/DataContainer.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Source/Engine/Core/Types/DataContainer.h b/Source/Engine/Core/Types/DataContainer.h index 954ff24e2..8b504e58f 100644 --- a/Source/Engine/Core/Types/DataContainer.h +++ b/Source/Engine/Core/Types/DataContainer.h @@ -324,9 +324,7 @@ public: void Append(T* data, int32 length) { if (length <= 0) - { return; - } if (Base::Length() == 0) { Copy(data, length); @@ -337,16 +335,13 @@ public: const auto prevLength = Base::_length; Base::_length = prevLength + length; - Base::_data = Allocator::Allocate(Base::_length * sizeof(T)); + Base::_data = (T*)Allocator::Allocate(Base::_length * sizeof(T)); Platform::MemoryCopy(Base::_data, prev, prevLength * sizeof(T)); Platform::MemoryCopy(Base::_data + prevLength * sizeof(T), data, length * sizeof(T)); if (_isAllocated && prev) - { Allocator::Free(prev); - } - _isAllocated = true; } From 5a23df6478d266886085f2837c7e17a2eaaf3526 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 10:34:50 +0100 Subject: [PATCH 372/419] Fix missing saving Pre Rotation --- Source/Engine/Level/Actors/SplineModel.cpp | 2 ++ Source/Engine/Physics/Colliders/SplineCollider.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index c06dfe75e..2a0972ffa 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -447,6 +447,7 @@ void SplineModel::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_MEMBER(BoundsScale, _boundsScale); SERIALIZE_MEMBER(LODBias, _lodBias); SERIALIZE_MEMBER(ForcedLOD, _forcedLod); + SERIALIZE_MEMBER(PreRotation, _preRotation) SERIALIZE(Model); SERIALIZE(DrawModes); @@ -463,6 +464,7 @@ void SplineModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod DESERIALIZE_MEMBER(BoundsScale, _boundsScale); DESERIALIZE_MEMBER(LODBias, _lodBias); DESERIALIZE_MEMBER(ForcedLOD, _forcedLod); + DESERIALIZE_MEMBER(PreRotation, _preRotation); DESERIALIZE(Model); DESERIALIZE(DrawModes); diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index ff670fcd5..6855eae69 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -120,6 +120,7 @@ void SplineCollider::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_GET_OTHER_OBJ(SplineCollider); SERIALIZE(CollisionData); + SERIALIZE_MEMBER(PreRotation, _preRotation) } void SplineCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -128,6 +129,7 @@ void SplineCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* Collider::Deserialize(stream, modifier); DESERIALIZE(CollisionData); + DESERIALIZE_MEMBER(PreRotation, _preRotation); } void SplineCollider::OnParentChanged() @@ -325,7 +327,7 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry) #if !USE_EDITOR // Free memory for static splines (if editor collision preview is not needed) - if (IsStatic()) + if (IsTransformStatic()) { _vertexBuffer.Resize(0); _indexBuffer.Resize(0); From 80e2aee92b5ad61d39e172ccec38552b1dec11f4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 10:57:45 +0100 Subject: [PATCH 373/419] Fixes and tweaks --- Source/Engine/Audio/AudioClip.cpp | 2 +- Source/Engine/Content/Assets/Model.cpp | 2 +- Source/Engine/Content/Assets/SkinnedModel.cpp | 2 +- Source/Engine/Content/BinaryAsset.cpp | 2 +- .../Engine/Content/Loading/Tasks/LoadAssetDataTask.h | 2 +- Source/Engine/Content/Loading/Tasks/LoadAssetTask.h | 2 +- Source/Engine/Content/WeakAssetReference.h | 11 ++--------- Source/Engine/Engine/Base/GameBase.cpp | 4 ++-- Source/Engine/Graphics/GPUResourceProperty.h | 9 --------- Source/Engine/Physics/CollisionCooking.cpp | 2 +- 10 files changed, 11 insertions(+), 27 deletions(-) diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp index 4005e46b9..29dd2a153 100644 --- a/Source/Engine/Audio/AudioClip.cpp +++ b/Source/Engine/Audio/AudioClip.cpp @@ -125,7 +125,7 @@ void AudioClip::StreamingTask::OnEnd() { ASSERT(_asset->_streamingTask == this); _asset->_streamingTask = nullptr; - _asset.Unlink(); + _asset = nullptr; } _dataLock.Release(); diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index 897e589a8..8cdd0601e 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -104,7 +104,7 @@ protected: { ASSERT(_asset->_streamingTask == this); _asset->_streamingTask = nullptr; - _asset.Unlink(); + _asset = nullptr; } _dataLock.Release(); diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index 5309cf6b9..dfccfec19 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -97,7 +97,7 @@ protected: { ASSERT(_asset->_streamingTask == this); _asset->_streamingTask = nullptr; - _asset.Unlink(); + _asset = nullptr; } _dataLock.Release(); diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index 7160cb287..1dbc672f2 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -491,7 +491,7 @@ protected: void OnEnd() override { _dataLock.Release(); - _asset.Unlink(); + _asset = nullptr; // Base ContentLoadTask::OnEnd(); diff --git a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h index 9d97a439f..5d552b996 100644 --- a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h +++ b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h @@ -82,7 +82,7 @@ protected: void OnEnd() override { _dataLock.Release(); - _asset.Unlink(); + _asset = nullptr; // Base ContentLoadTask::OnEnd(); diff --git a/Source/Engine/Content/Loading/Tasks/LoadAssetTask.h b/Source/Engine/Content/Loading/Tasks/LoadAssetTask.h index 7eed30399..7a7ea285f 100644 --- a/Source/Engine/Content/Loading/Tasks/LoadAssetTask.h +++ b/Source/Engine/Content/Loading/Tasks/LoadAssetTask.h @@ -68,7 +68,7 @@ protected: void OnEnd() override { - _asset.Unlink(); + _asset = nullptr; // Base ContentLoadTask::OnEnd(); diff --git a/Source/Engine/Content/WeakAssetReference.h b/Source/Engine/Content/WeakAssetReference.h index 9ed81b521..76f6b1fff 100644 --- a/Source/Engine/Content/WeakAssetReference.h +++ b/Source/Engine/Content/WeakAssetReference.h @@ -66,14 +66,6 @@ public: return _asset ? _asset->GetOrCreateManagedInstance() : nullptr; } - /// - /// Clears the asset reference. - /// - FORCE_INLINE void Unlink() - { - OnSet(nullptr); - } - /// /// Gets the asset property value as string. /// @@ -103,7 +95,8 @@ protected: { ASSERT(_asset == asset); Unload(); - Unlink(); + asset->OnUnloaded.Unbind(this); + asset = nullptr; } }; diff --git a/Source/Engine/Engine/Base/GameBase.cpp b/Source/Engine/Engine/Base/GameBase.cpp index c313b0737..62e187e7d 100644 --- a/Source/Engine/Engine/Base/GameBase.cpp +++ b/Source/Engine/Engine/Base/GameBase.cpp @@ -259,13 +259,13 @@ void GameBaseImpl::OnSplashScreenEnd() { // Hide splash screen SplashScreenTime = 0; - SplashScreen.Unlink(); + SplashScreen = nullptr; MainRenderTask::Instance->PostRender.Unbind(&OnPostRender); // Load the first scene LOG(Info, "Loading the first scene"); const auto sceneId = FirstScene ? FirstScene.GetID() : Guid::Empty; - FirstScene.Unlink(); + FirstScene = nullptr; if (Level::LoadSceneAsync(sceneId)) { LOG(Fatal, "Cannot load the first scene."); diff --git a/Source/Engine/Graphics/GPUResourceProperty.h b/Source/Engine/Graphics/GPUResourceProperty.h index 5ad31439d..49e3c7b9b 100644 --- a/Source/Engine/Graphics/GPUResourceProperty.h +++ b/Source/Engine/Graphics/GPUResourceProperty.h @@ -75,12 +75,8 @@ public: GPUResourceProperty& operator=(const GPUResourceProperty& other) { - // Protect against invalid self-assignment if (this != &other) - { Set(other.Get()); - } - return *this; } @@ -158,7 +154,6 @@ public: /// Value to assign void Set(T* value) { - // Check if value will change if (_resource != value) { // Remove reference from the old one @@ -179,7 +174,6 @@ public: /// void Unlink() { - // Check if value will change if (_resource) { // Remove reference from the old one @@ -194,10 +188,7 @@ private: { if (_resource) { - // Unlink _resource = nullptr; - - // Fire event OnUnload(this); } } diff --git a/Source/Engine/Physics/CollisionCooking.cpp b/Source/Engine/Physics/CollisionCooking.cpp index 6769e50fc..9076d979e 100644 --- a/Source/Engine/Physics/CollisionCooking.cpp +++ b/Source/Engine/Physics/CollisionCooking.cpp @@ -15,7 +15,7 @@ #define ENSURE_CAN_COOK \ if (Physics::GetCooking() == nullptr) \ { \ - LOG(Warning, "Physics collisions cooking is disabled at runtime. Enable Physics Settings option SupportCookingAtRuntime to use terrain generation at runtime."); \ + LOG(Warning, "Physics collisions cooking is disabled at runtime. Enable Physics Settings option SupportCookingAtRuntime to use collision generation at runtime."); \ return true; \ } From 0e78d13146b50c19a4e528d8c3d8b9c9f79d5b34 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 11:15:51 +0100 Subject: [PATCH 374/419] Add support for navmesh on spline collider --- Source/Engine/Navigation/NavMeshBuilder.cpp | 13 +++++++++++++ Source/Engine/Physics/Colliders/SplineCollider.cpp | 10 ++++++++++ Source/Engine/Physics/Colliders/SplineCollider.h | 11 +++++++++++ 3 files changed, 34 insertions(+) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index fc0e9d51c..55319bb9e 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -16,6 +16,7 @@ #include "Engine/Physics/Colliders/SphereCollider.h" #include "Engine/Physics/Colliders/CapsuleCollider.h" #include "Engine/Physics/Colliders/MeshCollider.h" +#include "Engine/Physics/Colliders/SplineCollider.h" #include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Terrain/TerrainPatch.h" #include "Engine/Terrain/Terrain.h" @@ -259,6 +260,18 @@ struct NavigationSceneRasterization e.RasterizeTriangles(); } + else if (const auto* splineCollider = dynamic_cast(actor)) + { + PROFILE_CPU_NAMED("SplineCollider"); + + auto collisionData = splineCollider->CollisionData.Get(); + if (!collisionData || collisionData->WaitForLoaded()) + return true; + + splineCollider->ExtractGeometry(vb, ib); + + e.RasterizeTriangles(); + } else if (const auto* terrain = dynamic_cast(actor)) { PROFILE_CPU_NAMED("Terrain"); diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index 6855eae69..947942fc8 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -35,6 +35,16 @@ void SplineCollider::SetPreRotation(const Quaternion& value) UpdateGeometry(); } +#if USE_EDITOR + +void SplineCollider::ExtractGeometry(Array& vertexBuffer, Array& indexBuffer) const +{ + vertexBuffer.Add(_vertexBuffer); + indexBuffer.Add(_indexBuffer); +} + +#endif + void SplineCollider::OnCollisionDataChanged() { // This should not be called during physics simulation, if it happened use write lock on physx scene diff --git a/Source/Engine/Physics/Colliders/SplineCollider.h b/Source/Engine/Physics/Colliders/SplineCollider.h index e7ca5a831..3bfcfacf1 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.h +++ b/Source/Engine/Physics/Colliders/SplineCollider.h @@ -42,6 +42,17 @@ public: /// API_PROPERTY() void SetPreRotation(const Quaternion& value); +#if USE_EDITOR + + /// + /// Extracts the collision data geometry into list of triangles. + /// + /// The output vertex buffer. + /// The output index buffer. + void ExtractGeometry(Array& vertexBuffer, Array& indexBuffer) const; + +#endif + private: void OnCollisionDataChanged(); From 17d0f40e1cb1f49c1130bc3359f1cc9731e1a0fb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 11:27:13 +0100 Subject: [PATCH 375/419] Add automatic navmesh update when editing spline with navmesh-relevant collider --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index 9ce64efa9..a9dec21b6 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -46,6 +46,7 @@ namespace FlaxEditor.SceneGraph.Actors { var actor = (Spline)_node.Actor; actor.SetSplineTransform(Index, value); + OnSplineEdited(actor); } } @@ -111,6 +112,7 @@ namespace FlaxEditor.SceneGraph.Actors { var spline = Object.Find(ref SplineId); sceneModule.MarkSceneEdited(spline.Scene); + OnSplineEdited(spline); } } @@ -142,6 +144,7 @@ namespace FlaxEditor.SceneGraph.Actors undoAction = action; var splineNode = (SplineNode)SceneGraphFactory.FindNode(action.SplineId); splineNode.OnUpdate(); + OnSplineEdited(actor); return splineNode.ActorChildNodes[newIndex]; } @@ -194,6 +197,7 @@ namespace FlaxEditor.SceneGraph.Actors if (splineNode == null) return null; splineNode.OnUpdate(); + OnSplineEdited(spline); return splineNode.ActorChildNodes[data.Index]; } } @@ -333,6 +337,19 @@ namespace FlaxEditor.SceneGraph.Actors Editor.Instance.SceneEditing.Spawn(actor, Actor); } + private static void OnSplineEdited(Spline spline) + { + var collider = spline.GetChild(); + if (collider && collider.Scene && collider.IsActiveInHierarchy && collider.HasStaticFlag(StaticFlags.Navigation) && !Editor.IsPlayMode) + { + var options = Editor.Instance.Options.Options.General; + if (options.AutoRebuildNavMesh) + { + Navigation.BuildNavMesh(collider.Scene, collider.Box, options.AutoRebuildNavMeshTimeoutMs); + } + } + } + /// public override void OnDispose() { From 9fd5e8928c3b00ae74c1fc114f0dc2baf2a144d8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 11:27:30 +0100 Subject: [PATCH 376/419] Add missing scene graph nodes types mapping --- Source/Editor/SceneGraph/SceneGraphFactory.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs index 5f3dd8b0f..9abf44af3 100644 --- a/Source/Editor/SceneGraph/SceneGraphFactory.cs +++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs @@ -66,6 +66,8 @@ namespace FlaxEditor.SceneGraph CustomNodesTypes.Add(typeof(ParticleEffect), typeof(ParticleEffectNode)); CustomNodesTypes.Add(typeof(SceneAnimationPlayer), typeof(SceneAnimationPlayerNode)); CustomNodesTypes.Add(typeof(Spline), typeof(SplineNode)); + CustomNodesTypes.Add(typeof(SplineModel), typeof(ActorNode)); + CustomNodesTypes.Add(typeof(SplineCollider), typeof(ColliderNode)); } /// From 4dd8e19f791fe43491b063587a7ee7afa25303c5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 11:27:47 +0100 Subject: [PATCH 377/419] Fix Scene Queries to lock scene access --- Source/Engine/Level/SceneQuery.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/SceneQuery.h b/Source/Engine/Level/SceneQuery.h index 4dedf0b8d..245c4c8a7 100644 --- a/Source/Engine/Level/SceneQuery.h +++ b/Source/Engine/Level/SceneQuery.h @@ -3,10 +3,10 @@ #pragma once // Enables locking scenes during scene query execution can provide some safety when using scene queries from other threads but may provide stalls on a main thread -#define SCENE_QUERIES_WITH_LOCK 0 +#define SCENE_QUERIES_WITH_LOCK 1 -#include "Scene/Scene.h" #include "Level.h" +#include "Scene/Scene.h" #if SCENE_QUERIES_WITH_LOCK #include "Engine/Threading/Threading.h" #endif From 74c1ac89c812efa7f7186348afa9f3cd0723a659 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 11:43:46 +0100 Subject: [PATCH 378/419] Add `DebugDraw.DrawWireTriangles` for wireframe geometry debug drawing --- Source/Engine/Debug/DebugDraw.cpp | 148 +++++++++++++++--- Source/Engine/Debug/DebugDraw.h | 42 +++++ .../Physics/Colliders/SplineCollider.cpp | 4 +- 3 files changed, 168 insertions(+), 26 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 4341695b7..afc2311ff 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -130,6 +130,8 @@ struct DebugDrawData Array OneFrameLines; Array DefaultTriangles; Array OneFrameTriangles; + Array DefaultWireTriangles; + Array OneFrameWireTriangles; inline int32 Count() const { @@ -143,7 +145,7 @@ struct DebugDrawData inline int32 TrianglesCount() const { - return DefaultTriangles.Count() + OneFrameTriangles.Count(); + return DefaultTriangles.Count() + OneFrameTriangles.Count() + DefaultWireTriangles.Count() + OneFrameWireTriangles.Count(); } inline void Add(const DebugLine& l) @@ -162,13 +164,23 @@ struct DebugDrawData OneFrameTriangles.Add(t); } + inline void AddWire(const DebugTriangle& t) + { + if (t.TimeLeft > 0) + DefaultWireTriangles.Add(t); + else + OneFrameWireTriangles.Add(t); + } + inline void Update(float deltaTime) { UpdateList(deltaTime, DefaultLines); UpdateList(deltaTime, DefaultTriangles); + UpdateList(deltaTime, DefaultWireTriangles); OneFrameLines.Clear(); OneFrameTriangles.Clear(); + OneFrameWireTriangles.Clear(); } inline void Clear() @@ -177,6 +189,8 @@ struct DebugDrawData OneFrameLines.Clear(); DefaultTriangles.Clear(); OneFrameTriangles.Clear(); + DefaultWireTriangles.Clear(); + OneFrameWireTriangles.Clear(); } inline void Release() @@ -185,16 +199,20 @@ struct DebugDrawData OneFrameLines.Resize(0); DefaultTriangles.Resize(0); OneFrameTriangles.Resize(0); + DefaultWireTriangles.Resize(0); + OneFrameWireTriangles.Resize(0); } }; DebugDrawData DebugDrawDefault; DebugDrawData DebugDrawDepthTest; AssetReference DebugDrawShader; -PsData DebugDrawPsWireDefault; -PsData DebugDrawPsWireDepthTest; -PsData DebugDrawPsDefault; -PsData DebugDrawPsDepthTest; +PsData DebugDrawPsLinesDefault; +PsData DebugDrawPsLinesDepthTest; +PsData DebugDrawPsWireTrianglesDefault; +PsData DebugDrawPsWireTrianglesDepthTest; +PsData DebugDrawPsTrianglesDefault; +PsData DebugDrawPsTrianglesDepthTest; DynamicVertexBuffer* DebugDrawVB = nullptr; Vector3 SphereCache[DEBUG_DRAW_SPHERE_VERTICES]; Vector3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES]; @@ -453,16 +471,21 @@ void DebugDrawService::Update() // Default desc.PS = shader->GetPS("PS"); desc.PrimitiveTopologyType = PrimitiveTopologyType::Line; - failed |= DebugDrawPsWireDefault.Create(desc); + failed |= DebugDrawPsLinesDefault.Create(desc); desc.PrimitiveTopologyType = PrimitiveTopologyType::Triangle; - failed |= DebugDrawPsDefault.Create(desc); + failed |= DebugDrawPsTrianglesDefault.Create(desc); + desc.Wireframe = true; + failed |= DebugDrawPsWireTrianglesDefault.Create(desc); // Depth Test + desc.Wireframe = false; desc.PS = shader->GetPS("PS_DepthTest"); desc.PrimitiveTopologyType = PrimitiveTopologyType::Line; - failed |= DebugDrawPsWireDepthTest.Create(desc); + failed |= DebugDrawPsLinesDepthTest.Create(desc); desc.PrimitiveTopologyType = PrimitiveTopologyType::Triangle; - failed |= DebugDrawPsDepthTest.Create(desc); + failed |= DebugDrawPsTrianglesDepthTest.Create(desc); + desc.Wireframe = true; + failed |= DebugDrawPsWireTrianglesDepthTest.Create(desc); if (failed) { @@ -482,10 +505,12 @@ void DebugDrawService::Dispose() // Release resources SphereTriangleCache.Resize(0); - DebugDrawPsWireDefault.Release(); - DebugDrawPsWireDepthTest.Release(); - DebugDrawPsDepthTest.Release(); - DebugDrawPsDepthTest.Release(); + DebugDrawPsLinesDefault.Release(); + DebugDrawPsLinesDepthTest.Release(); + DebugDrawPsWireTrianglesDefault.Release(); + DebugDrawPsWireTrianglesDepthTest.Release(); + DebugDrawPsTrianglesDefault.Release(); + DebugDrawPsTrianglesDepthTest.Release(); SAFE_DELETE(DebugDrawVB); DebugDrawShader = nullptr; } @@ -495,9 +520,9 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe PROFILE_GPU_CPU("Debug Draw"); // Ensure to have shader loaded and any lines to render - const int32 DebugDrawDepthTestCount = DebugDrawDepthTest.Count(); - const int32 DebugDrawDefaultCount = DebugDrawDefault.Count(); - if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || DebugDrawDepthTestCount + DebugDrawDefaultCount == 0) + const int32 debugDrawDepthTestCount = DebugDrawDepthTest.Count(); + const int32 debugDrawDefaultCount = DebugDrawDefault.Count(); + if (DebugDrawShader == nullptr || !DebugDrawShader->IsLoaded() || debugDrawDepthTestCount + debugDrawDefaultCount == 0) return; if (renderContext.Buffers == nullptr || !DebugDrawVB) return; @@ -517,6 +542,8 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe const DebugDrawCall defaultLines = WriteLists(vertexCounter, DebugDrawDefault.DefaultLines, DebugDrawDefault.OneFrameLines); const DebugDrawCall depthTestTriangles = WriteLists(vertexCounter, DebugDrawDepthTest.DefaultTriangles, DebugDrawDepthTest.OneFrameTriangles); const DebugDrawCall defaultTriangles = WriteLists(vertexCounter, DebugDrawDefault.DefaultTriangles, DebugDrawDefault.OneFrameTriangles); + const DebugDrawCall depthTestWireTriangles = WriteLists(vertexCounter, DebugDrawDepthTest.DefaultWireTriangles, DebugDrawDepthTest.OneFrameWireTriangles); + const DebugDrawCall defaultWireTriangles = WriteLists(vertexCounter, DebugDrawDefault.DefaultWireTriangles, DebugDrawDefault.OneFrameWireTriangles); DebugDrawVB->Flush(context); #if COMPILE_WITH_PROFILER ProfilerCPU::EndEvent(updateBufferProfileKey); @@ -536,7 +563,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe #define DRAW(drawCall) if (drawCall.VertexCount) // Draw with depth test - if (depthTestLines.VertexCount + depthTestTriangles.VertexCount > 0) + if (depthTestLines.VertexCount + depthTestTriangles.VertexCount + depthTestWireTriangles.VertexCount > 0) { if (data.EnableDepthTest) context->BindSR(0, renderContext.Buffers->DepthBuffer); @@ -547,16 +574,25 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe // Lines if (depthTestLines.VertexCount) { - auto state = data.EnableDepthTest ? &DebugDrawPsWireDepthTest : &DebugDrawPsWireDefault; + auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault; context->SetState(state->Get(enableDepthWrite, true)); context->BindVB(ToSpan(&vb, 1)); context->Draw(depthTestLines.StartVertex, depthTestLines.VertexCount); } + + // Wire Triangles + if (depthTestWireTriangles.VertexCount) + { + auto state = data.EnableDepthTest ? &DebugDrawPsWireTrianglesDepthTest : &DebugDrawPsWireTrianglesDefault; + context->SetState(state->Get(enableDepthWrite, true)); + context->BindVB(ToSpan(&vb, 1)); + context->Draw(depthTestWireTriangles.StartVertex, depthTestWireTriangles.VertexCount); + } // Triangles if (depthTestTriangles.VertexCount) { - auto state = data.EnableDepthTest ? &DebugDrawPsDepthTest : &DebugDrawPsDefault; + auto state = data.EnableDepthTest ? &DebugDrawPsTrianglesDepthTest : &DebugDrawPsTrianglesDefault; context->SetState(state->Get(enableDepthWrite, true)); context->BindVB(ToSpan(&vb, 1)); context->Draw(depthTestTriangles.StartVertex, depthTestTriangles.VertexCount); @@ -567,22 +603,30 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe } // Draw without depth - if (defaultLines.VertexCount + defaultTriangles.VertexCount > 0) + if (defaultLines.VertexCount + defaultTriangles.VertexCount + defaultWireTriangles.VertexCount > 0) { context->SetRenderTarget(target); // Lines if (defaultLines.VertexCount) { - context->SetState(DebugDrawPsWireDefault.Get(false, false)); + context->SetState(DebugDrawPsLinesDefault.Get(false, false)); context->BindVB(ToSpan(&vb, 1)); context->Draw(defaultLines.StartVertex, defaultLines.VertexCount); } + // Wire Triangles + if (defaultWireTriangles.VertexCount) + { + context->SetState(DebugDrawPsWireTrianglesDefault.Get(false, false)); + context->BindVB(ToSpan(&vb, 1)); + context->Draw(defaultWireTriangles.StartVertex, defaultWireTriangles.VertexCount); + } + // Triangles if (defaultTriangles.VertexCount) { - context->SetState(DebugDrawPsDefault.Get(false, false)); + context->SetState(DebugDrawPsTrianglesDefault.Get(false, false)); context->BindVB(ToSpan(&vb, 1)); context->Draw(defaultTriangles.StartVertex, defaultTriangles.VertexCount); } @@ -876,7 +920,6 @@ void DebugDraw::DrawTriangles(const Span& vertices, const Color& color, t.V0 = vertices[i++]; t.V1 = vertices[i++]; t.V2 = vertices[i++]; - list->Add(t); } } @@ -906,7 +949,6 @@ void DebugDraw::DrawTriangles(const Span& vertices, const Span& t.V0 = vertices[indices[i++]]; t.V1 = vertices[indices[i++]]; t.V2 = vertices[indices[i++]]; - list->Add(t); } } @@ -916,6 +958,64 @@ void DebugDraw::DrawTriangles(const Array& vertices, const Array(vertices.Get(), vertices.Count()), Span(indices.Get(), indices.Count()), color, duration, depthTest); } +void DebugDraw::DrawWireTriangles(const Span& vertices, const Color& color, float duration, bool depthTest) +{ + ASSERT(vertices.Length() % 3 == 0); + + DebugTriangle t; + t.Color = Color32(color); + t.TimeLeft = duration; + + Array* list; + if (depthTest) + list = duration > 0 ? &DebugDrawDepthTest.DefaultWireTriangles : &DebugDrawDepthTest.OneFrameWireTriangles; + else + list = duration > 0 ? &DebugDrawDefault.DefaultWireTriangles : &DebugDrawDefault.OneFrameWireTriangles; + list->EnsureCapacity(list->Count() + vertices.Length() / 3); + + for (int32 i = 0; i < vertices.Length();) + { + t.V0 = vertices[i++]; + t.V1 = vertices[i++]; + t.V2 = vertices[i++]; + list->Add(t); + } +} + +void DebugDraw::DrawWireTriangles(const Array& vertices, const Color& color, float duration, bool depthTest) +{ + DrawWireTriangles(Span(vertices.Get(), vertices.Count()), color, duration, depthTest); +} + +void DebugDraw::DrawWireTriangles(const Span& vertices, const Span& indices, const Color& color, float duration, bool depthTest) +{ + ASSERT(indices.Length() % 3 == 0); + + DebugTriangle t; + t.Color = Color32(color); + t.TimeLeft = duration; + + Array* list; + if (depthTest) + list = duration > 0 ? &DebugDrawDepthTest.DefaultWireTriangles : &DebugDrawDepthTest.OneFrameWireTriangles; + else + list = duration > 0 ? &DebugDrawDefault.DefaultWireTriangles : &DebugDrawDefault.OneFrameWireTriangles; + list->EnsureCapacity(list->Count() + indices.Length() / 3); + + for (int32 i = 0; i < indices.Length();) + { + t.V0 = vertices[indices[i++]]; + t.V1 = vertices[indices[i++]]; + t.V2 = vertices[indices[i++]]; + list->Add(t); + } +} + +void DebugDraw::DrawWireTriangles(const Array& vertices, const Array& indices, const Color& color, float duration, bool depthTest) +{ + DrawWireTriangles(Span(vertices.Get(), vertices.Count()), Span(indices.Get(), indices.Count()), color, duration, depthTest); +} + void DebugDraw::DrawWireTube(const Vector3& position, const Quaternion& orientation, float radius, float length, const Color& color, float duration, bool depthTest) { // Check if has no length (just sphere) diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index 81c704894..69c3d4fed 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -142,6 +142,44 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw); /// If set to true depth test will be performed, otherwise depth will be ignored. static void DrawTriangles(const Array& vertices, const Array& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + /// + /// Draws the wireframe triangles. + /// + /// The triangle vertices list (must have multiple of 3 elements). + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + + /// + /// Draws the wireframe triangles. + /// + /// The triangle vertices list (must have multiple of 3 elements). + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + static void DrawWireTriangles(const Array& vertices, const Color& color, float duration = 0.0f, bool depthTest = true); + + /// + /// Draws the wireframe triangles using the given index buffer. + /// + /// The triangle vertices list. + /// The triangle indices list (must have multiple of 3 elements). + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawWireTriangles(const Span& vertices, const Span& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + + /// + /// Draws the wireframe triangles using the given index buffer. + /// + /// The triangle vertices list. + /// The triangle indices list (must have multiple of 3 elements). + /// The color. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + static void DrawWireTriangles(const Array& vertices, const Array& indices, const Color& color, float duration = 0.0f, bool depthTest = true); + /// /// Draws the wireframe box. /// @@ -251,6 +289,8 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw); #define DEBUG_DRAW_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawSphere(sphere, color, duration, depthTest) #define DEBUG_DRAW_BOX(box, color, duration, depthTest) DebugDraw::DrawBox(box, color, duration, depthTest) #define DEBUG_DRAW_WIRE_TRIANGLE(v0, v1, v2, color, duration, depthTest) DebugDraw::DrawWireTriangle(v0, v1, v2, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLES(vertices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLES_EX(vertices, indices, color, duration, depthTest) DebugDraw::DrawWireTriangles(vertices, indices, color, duration, depthTest) #define DEBUG_DRAW_WIRE_BOX(box, color, duration, depthTest) DebugDraw::DrawWireBox(box, color, duration, depthTest) #define DEBUG_DRAW_WIRE_FRUSTUM(frustum, color, duration, depthTest) DebugDraw::DrawWireFrustum(frustum, color, duration, depthTest) #define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) DebugDraw::DrawWireSphere(sphere, color, duration, depthTest) @@ -270,6 +310,8 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw); #define DEBUG_DRAW_SPHERE(sphere, color, duration, depthTest) #define DEBUG_DRAW_BOX(box, color, duration, depthTest) #define DEBUG_DRAW_WIRE_TRIANGLE(v0, v1, v2, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLES(vertices, color, duration, depthTest) +#define DEBUG_DRAW_WIRE_TRIANGLES_EX(vertices, indices, color, duration, depthTest) #define DEBUG_DRAW_WIRE_BOX(box, color, duration, depthTest) #define DEBUG_DRAW_WIRE_FRUSTUM(frustum, color, duration, depthTest) #define DEBUG_DRAW_WIRE_SPHERE(sphere, color, duration, depthTest) diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index 947942fc8..8e7266755 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -92,12 +92,12 @@ bool SplineCollider::CanBeTrigger() const void SplineCollider::DrawPhysicsDebug(RenderView& view) { - DEBUG_DRAW_TRIANGLES_EX(_vertexBuffer, _indexBuffer, Color::GreenYellow * 0.8f, 0, true); + DEBUG_DRAW_WIRE_TRIANGLES_EX(_vertexBuffer, _indexBuffer, Color::GreenYellow * 0.8f, 0, true); } void SplineCollider::OnDebugDrawSelected() { - DEBUG_DRAW_TRIANGLES_EX(_vertexBuffer, _indexBuffer, Color::GreenYellow, 0, false); + DEBUG_DRAW_WIRE_TRIANGLES_EX(_vertexBuffer, _indexBuffer, Color::GreenYellow, 0, false); // Base Collider::OnDebugDrawSelected(); From 34b6cfebc3763d6e4c68fddd2047cf9bc746a482 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 12:20:58 +0100 Subject: [PATCH 379/419] Fix navmesh update on some spline editing operations --- Source/Editor/CustomEditors/Dedicated/SplineEditor.cs | 3 +++ Source/Editor/SceneGraph/Actors/SplineNode.cs | 2 +- Source/Editor/Undo/Actions/EditSplineAction.cs | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs index 9eedd0ed3..51d137ea9 100644 --- a/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SplineEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using FlaxEditor.Actions; +using FlaxEditor.SceneGraph.Actors; using FlaxEngine; using FlaxEngine.GUI; @@ -40,6 +41,7 @@ namespace FlaxEditor.CustomEditors.Dedicated spline.SetTangentsLinear(); if (enableUndo) Presenter.Undo.AddAction(new EditSplineAction(spline, before)); + SplineNode.OnSplineEdited(spline); Editor.Instance.Scene.MarkSceneEdited(spline.Scene); } } @@ -56,6 +58,7 @@ namespace FlaxEditor.CustomEditors.Dedicated spline.SetTangentsSmooth(); if (enableUndo) Presenter.Undo.AddAction(new EditSplineAction(spline, before)); + SplineNode.OnSplineEdited(spline); Editor.Instance.Scene.MarkSceneEdited(spline.Scene); } } diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index a9dec21b6..f0db64fca 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -337,7 +337,7 @@ namespace FlaxEditor.SceneGraph.Actors Editor.Instance.SceneEditing.Spawn(actor, Actor); } - private static void OnSplineEdited(Spline spline) + internal static void OnSplineEdited(Spline spline) { var collider = spline.GetChild(); if (collider && collider.Scene && collider.IsActiveInHierarchy && collider.HasStaticFlag(StaticFlags.Navigation) && !Editor.IsPlayMode) diff --git a/Source/Editor/Undo/Actions/EditSplineAction.cs b/Source/Editor/Undo/Actions/EditSplineAction.cs index 3bb8dac5c..7fb836459 100644 --- a/Source/Editor/Undo/Actions/EditSplineAction.cs +++ b/Source/Editor/Undo/Actions/EditSplineAction.cs @@ -2,6 +2,7 @@ using System; using FlaxEditor.Modules; +using FlaxEditor.SceneGraph.Actors; using FlaxEngine; namespace FlaxEditor.Actions @@ -45,6 +46,7 @@ namespace FlaxEditor.Actions if (spline == null) return; spline.SplineKeyframes = _after; + SplineNode.OnSplineEdited(spline); } /// @@ -54,6 +56,7 @@ namespace FlaxEditor.Actions if (spline == null) return; spline.SplineKeyframes = _before; + SplineNode.OnSplineEdited(spline); } /// From 8661091337073f411904642970bf42c0acdb546a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 15:16:04 +0100 Subject: [PATCH 380/419] Add Spline Rope Body actor for ropes, chains and cables physics --- Source/Editor/SceneGraph/Actors/SplineNode.cs | 11 + Source/Editor/SceneGraph/SceneGraphFactory.cs | 1 + .../Engine/Physics/Actors/SplineRopeBody.cpp | 213 ++++++++++++++++++ Source/Engine/Physics/Actors/SplineRopeBody.h | 70 ++++++ 4 files changed, 295 insertions(+) create mode 100644 Source/Engine/Physics/Actors/SplineRopeBody.cpp create mode 100644 Source/Engine/Physics/Actors/SplineRopeBody.h diff --git a/Source/Editor/SceneGraph/Actors/SplineNode.cs b/Source/Editor/SceneGraph/Actors/SplineNode.cs index f0db64fca..3d41d0904 100644 --- a/Source/Editor/SceneGraph/Actors/SplineNode.cs +++ b/Source/Editor/SceneGraph/Actors/SplineNode.cs @@ -314,6 +314,7 @@ namespace FlaxEditor.SceneGraph.Actors contextMenu.AddButton("Add spline model", OnAddSplineModel); contextMenu.AddButton("Add spline collider", OnAddSplineCollider); + contextMenu.AddButton("Add spline rope body", OnAddSplineRopeBody); } private void OnAddSplineModel() @@ -337,6 +338,16 @@ namespace FlaxEditor.SceneGraph.Actors Editor.Instance.SceneEditing.Spawn(actor, Actor); } + private void OnAddSplineRopeBody() + { + var actor = new SplineRopeBody + { + StaticFlags = StaticFlags.None, + Transform = Actor.Transform, + }; + Editor.Instance.SceneEditing.Spawn(actor, Actor); + } + internal static void OnSplineEdited(Spline spline) { var collider = spline.GetChild(); diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs index 9abf44af3..071cb6e5b 100644 --- a/Source/Editor/SceneGraph/SceneGraphFactory.cs +++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs @@ -68,6 +68,7 @@ namespace FlaxEditor.SceneGraph CustomNodesTypes.Add(typeof(Spline), typeof(SplineNode)); CustomNodesTypes.Add(typeof(SplineModel), typeof(ActorNode)); CustomNodesTypes.Add(typeof(SplineCollider), typeof(ColliderNode)); + CustomNodesTypes.Add(typeof(SplineRopeBody), typeof(ActorNode)); } /// diff --git a/Source/Engine/Physics/Actors/SplineRopeBody.cpp b/Source/Engine/Physics/Actors/SplineRopeBody.cpp new file mode 100644 index 000000000..acdc8ef59 --- /dev/null +++ b/Source/Engine/Physics/Actors/SplineRopeBody.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "SplineRopeBody.h" +#include "Engine/Level/Actors/Spline.h" +#include "Engine/Level/Scene/Scene.h" +#include "Engine/Physics/Physics.h" +#include "Engine/Engine/Time.h" +#include "Engine/Profiler/ProfilerCPU.h" +#include "Engine/Serialization/Serialization.h" + +SplineRopeBody::SplineRopeBody(const SpawnParams& params) + : Actor(params) +{ +} + +void SplineRopeBody::Tick() +{ + if (!_spline || _spline->GetSplinePointsCount() < 2) + return; + PROFILE_CPU(); + + // TODO: add as configs + const float SubstepTime = 0.02f; + const int32 SolverIterations = 1; + + // Cache data + const Vector3 gravity = Physics::GetGravity() * GravityScale; + auto& keyframes = _spline->Curve.GetKeyframes(); + const Transform splineTransform = _spline->GetTransform(); + const int32 keyframesCount = keyframes.Count(); + const float substepTime = SubstepTime; + const float substepTimeSqr = substepTime * substepTime; + bool splineDirty = false; + + // Synchronize spline keyframes with simulated masses + if (_masses.Count() > keyframesCount) + _masses.Resize(keyframesCount); + else + { + _masses.EnsureCapacity(keyframesCount); + while (_masses.Count() < keyframesCount) + { + const int32 i = _masses.Count(); + auto& mass = _masses.AddOne(); + mass.PrevPosition = splineTransform.LocalToWorld(keyframes[i].Value.Translation); + if (i != 0) + mass.SegmentLength = Vector3::Distance(mass.PrevPosition, _masses[i - 1].PrevPosition); + else + mass.SegmentLength = 0.0f; + } + } + { + // Rope start + auto& mass = _masses.First(); + mass.Position = mass.PrevPosition = GetPosition(); + mass.Unconstrained = false; + if (splineTransform.LocalToWorld(keyframes.First().Value.Translation) != mass.Position) + splineDirty = true; + } + for (int32 i = 1; i < keyframesCount; i++) + { + auto& mass = _masses[i]; + mass.Unconstrained = true; + mass.Position = splineTransform.LocalToWorld(keyframes[i].Value.Translation); + } + if (AttachEnd) + { + // Rope end + auto& mass = _masses.Last(); + mass.Position = mass.PrevPosition = AttachEnd->GetPosition(); + mass.Unconstrained = false; + if (splineTransform.LocalToWorld(keyframes.Last().Value.Translation) != mass.Position) + splineDirty = true; + } + + // Perform simulation in substeps to have better stability + _time += Time::Update.DeltaTime.GetTotalSeconds(); + while (_time > substepTime) + { + // Verlet integration + // [Reference: https://en.wikipedia.org/wiki/Verlet_integration] + const Vector3 force = gravity + AdditionalForce; + for (int32 i = 0; i < keyframesCount; i++) + { + auto& mass = _masses[i]; + if (mass.Unconstrained) + { + const Vector3 velocity = mass.Position - mass.PrevPosition; + mass.PrevPosition = mass.Position; + mass.Position = mass.Position + velocity + (substepTimeSqr * force); + keyframes[i].Value.Translation = splineTransform.WorldToLocal(mass.Position); + } + } + + // Constraints solving + for (int32 iteration = 0; iteration < SolverIterations; iteration++) + { + // Distance constraint + for (int32 i = 1; i < keyframesCount; i++) + { + auto& massA = _masses[i - 1]; + auto& massB = _masses[i]; + Vector3 offset = massB.Position - massA.Position; + const float distance = offset.Length(); + const float scale = (distance - massB.SegmentLength) / Math::Max(distance, ZeroTolerance); + if (massA.Unconstrained && massB.Unconstrained) + { + offset *= scale * 0.5f; + massA.Position += offset; + massB.Position -= offset; + } + else if (massA.Unconstrained) + { + massA.Position += scale * offset; + } + else if (massB.Unconstrained) + { + massB.Position -= scale * offset; + } + } + + // Stiffness constraint + if (EnableStiffness) + { + for (int32 i = 2; i < keyframesCount; i++) + { + auto& massA = _masses[i - 2]; + auto& massB = _masses[i]; + Vector3 offset = massB.Position - massA.Position; + const float distance = offset.Length(); + const float scale = (distance - massB.SegmentLength * 2.0f) / Math::Max(distance, ZeroTolerance); + if (massA.Unconstrained && massB.Unconstrained) + { + offset *= scale * 0.5f; + massA.Position += offset; + massB.Position -= offset; + } + else if (massA.Unconstrained) + { + massA.Position += scale * offset; + } + else if (massB.Unconstrained) + { + massB.Position -= scale * offset; + } + } + } + } + + _time -= substepTime; + splineDirty = true; + } + + // Update spline and relevant components (eg. spline model) + if (splineDirty) + { + for (int32 i = 0; i < keyframesCount; i++) + keyframes[i].Value.Translation = splineTransform.WorldToLocal(_masses[i].Position); + + _spline->UpdateSpline(); + } +} + +void SplineRopeBody::Serialize(SerializeStream& stream, const void* otherObj) +{ + Actor::Serialize(stream, otherObj); + + SERIALIZE_GET_OTHER_OBJ(SplineRopeBody); + + SERIALIZE(AttachEnd); + SERIALIZE(GravityScale); + SERIALIZE(AdditionalForce); + SERIALIZE(EnableStiffness); +} + +void SplineRopeBody::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + Actor::Deserialize(stream, modifier); + + DESERIALIZE(AttachEnd); + DESERIALIZE(GravityScale); + DESERIALIZE(AdditionalForce); + DESERIALIZE(EnableStiffness); +} + +void SplineRopeBody::OnEnable() +{ + GetScene()->Ticking.Update.AddTick(this); + + Actor::OnEnable(); +} + +void SplineRopeBody::OnDisable() +{ + Actor::OnDisable(); + + GetScene()->Ticking.Update.RemoveTick(this); +} + +void SplineRopeBody::OnParentChanged() +{ + Actor::OnParentChanged(); + + _spline = Cast(_parent); +} + +void SplineRopeBody::OnTransformChanged() +{ + Actor::OnTransformChanged(); + + _box = BoundingBox(_transform.Translation, _transform.Translation); + _sphere = BoundingSphere(_transform.Translation, 0.0f); +} diff --git a/Source/Engine/Physics/Actors/SplineRopeBody.h b/Source/Engine/Physics/Actors/SplineRopeBody.h new file mode 100644 index 000000000..5ac0506a5 --- /dev/null +++ b/Source/Engine/Physics/Actors/SplineRopeBody.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Level/Actor.h" +#include "Engine/Scripting/ScriptingObjectReference.h" + +class Spline; + +/// +/// Physical simulation actor for ropes, chains and cables represented by a spline. +/// +/// +API_CLASS() class FLAXENGINE_API SplineRopeBody : public Actor +{ +DECLARE_SCENE_OBJECT(SplineRopeBody); +private: + + struct Mass + { + Vector3 Position; + float SegmentLength; + Vector3 PrevPosition; + bool Unconstrained; + }; + + Spline* _spline = nullptr; + float _time = 0.0f; + Array _masses; + +public: + + /// + /// The target actor too attach the rope end to. If unset the rope end will run freely. + /// + API_FIELD(Attributes="EditorOrder(0), DefaultValue(null), EditorDisplay(\"Rope\")") + ScriptingObjectReference AttachEnd; + + /// + /// The world gravity scale applied to the rope. Can be used to adjust gravity force or disable it. + /// + API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Rope\")") + float GravityScale = 1.0f; + + /// + /// The additional, external force applied to rope (world-space). This can be eg. wind force. + /// + API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"Rope\")") + Vector3 AdditionalForce = Vector3::Zero; + + /// + /// If checked, the physics solver will use stiffness constraint for rope. It will be less likely to bend over and will preserve more it's shape. + /// + API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Rope\")") + bool EnableStiffness = false; + +private: + + void Tick(); + +public: + + // [Actor] + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + void OnEnable() override; + void OnDisable() override; + void OnTransformChanged() override; + void OnParentChanged() override; +}; From 55194edd2cd9d1f146dfbf6b2121f8a2e0c51bed Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 15:28:45 +0100 Subject: [PATCH 381/419] Fix missing NavMesh node type --- Source/Editor/SceneGraph/SceneGraphFactory.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs index 071cb6e5b..8103f5f05 100644 --- a/Source/Editor/SceneGraph/SceneGraphFactory.cs +++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs @@ -69,6 +69,7 @@ namespace FlaxEditor.SceneGraph CustomNodesTypes.Add(typeof(SplineModel), typeof(ActorNode)); CustomNodesTypes.Add(typeof(SplineCollider), typeof(ColliderNode)); CustomNodesTypes.Add(typeof(SplineRopeBody), typeof(ActorNode)); + CustomNodesTypes.Add(typeof(NavMesh), typeof(ActorNode)); } /// From bd5897eaab88bae9670cbb89c7531dc9dd5c89f0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 Feb 2021 17:18:32 +0100 Subject: [PATCH 382/419] Fixes and tweaks to SplineRopeBody --- .../Engine/Physics/Actors/SplineRopeBody.cpp | 92 +++++++------------ Source/Engine/Physics/Actors/SplineRopeBody.h | 9 +- 2 files changed, 38 insertions(+), 63 deletions(-) diff --git a/Source/Engine/Physics/Actors/SplineRopeBody.cpp b/Source/Engine/Physics/Actors/SplineRopeBody.cpp index acdc8ef59..a610e76ce 100644 --- a/Source/Engine/Physics/Actors/SplineRopeBody.cpp +++ b/Source/Engine/Physics/Actors/SplineRopeBody.cpp @@ -19,10 +19,6 @@ void SplineRopeBody::Tick() return; PROFILE_CPU(); - // TODO: add as configs - const float SubstepTime = 0.02f; - const int32 SolverIterations = 1; - // Cache data const Vector3 gravity = Physics::GetGravity() * GravityScale; auto& keyframes = _spline->Curve.GetKeyframes(); @@ -92,17 +88,40 @@ void SplineRopeBody::Tick() } } - // Constraints solving - for (int32 iteration = 0; iteration < SolverIterations; iteration++) + // Distance constraint + for (int32 i = 1; i < keyframesCount; i++) { - // Distance constraint - for (int32 i = 1; i < keyframesCount; i++) + auto& massA = _masses[i - 1]; + auto& massB = _masses[i]; + Vector3 offset = massB.Position - massA.Position; + const float distance = offset.Length(); + const float scale = (distance - massB.SegmentLength) / Math::Max(distance, ZeroTolerance); + if (massA.Unconstrained && massB.Unconstrained) { - auto& massA = _masses[i - 1]; + offset *= scale * 0.5f; + massA.Position += offset; + massB.Position -= offset; + } + else if (massA.Unconstrained) + { + massA.Position += scale * offset; + } + else if (massB.Unconstrained) + { + massB.Position -= scale * offset; + } + } + + // Stiffness constraint + if (EnableStiffness) + { + for (int32 i = 2; i < keyframesCount; i++) + { + auto& massA = _masses[i - 2]; auto& massB = _masses[i]; Vector3 offset = massB.Position - massA.Position; const float distance = offset.Length(); - const float scale = (distance - massB.SegmentLength) / Math::Max(distance, ZeroTolerance); + const float scale = (distance - massB.SegmentLength * 2.0f) / Math::Max(distance, ZeroTolerance); if (massA.Unconstrained && massB.Unconstrained) { offset *= scale * 0.5f; @@ -118,33 +137,6 @@ void SplineRopeBody::Tick() massB.Position -= scale * offset; } } - - // Stiffness constraint - if (EnableStiffness) - { - for (int32 i = 2; i < keyframesCount; i++) - { - auto& massA = _masses[i - 2]; - auto& massB = _masses[i]; - Vector3 offset = massB.Position - massA.Position; - const float distance = offset.Length(); - const float scale = (distance - massB.SegmentLength * 2.0f) / Math::Max(distance, ZeroTolerance); - if (massA.Unconstrained && massB.Unconstrained) - { - offset *= scale * 0.5f; - massA.Position += offset; - massB.Position -= offset; - } - else if (massA.Unconstrained) - { - massA.Position += scale * offset; - } - else if (massB.Unconstrained) - { - massB.Position -= scale * offset; - } - } - } } _time -= substepTime; @@ -161,31 +153,9 @@ void SplineRopeBody::Tick() } } -void SplineRopeBody::Serialize(SerializeStream& stream, const void* otherObj) -{ - Actor::Serialize(stream, otherObj); - - SERIALIZE_GET_OTHER_OBJ(SplineRopeBody); - - SERIALIZE(AttachEnd); - SERIALIZE(GravityScale); - SERIALIZE(AdditionalForce); - SERIALIZE(EnableStiffness); -} - -void SplineRopeBody::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) -{ - Actor::Deserialize(stream, modifier); - - DESERIALIZE(AttachEnd); - DESERIALIZE(GravityScale); - DESERIALIZE(AdditionalForce); - DESERIALIZE(EnableStiffness); -} - void SplineRopeBody::OnEnable() { - GetScene()->Ticking.Update.AddTick(this); + GetScene()->Ticking.FixedUpdate.AddTick(this); Actor::OnEnable(); } @@ -194,7 +164,7 @@ void SplineRopeBody::OnDisable() { Actor::OnDisable(); - GetScene()->Ticking.Update.RemoveTick(this); + GetScene()->Ticking.FixedUpdate.RemoveTick(this); } void SplineRopeBody::OnParentChanged() diff --git a/Source/Engine/Physics/Actors/SplineRopeBody.h b/Source/Engine/Physics/Actors/SplineRopeBody.h index 5ac0506a5..32e175ba7 100644 --- a/Source/Engine/Physics/Actors/SplineRopeBody.h +++ b/Source/Engine/Physics/Actors/SplineRopeBody.h @@ -13,6 +13,7 @@ class Spline; /// API_CLASS() class FLAXENGINE_API SplineRopeBody : public Actor { +API_AUTO_SERIALIZATION(); DECLARE_SCENE_OBJECT(SplineRopeBody); private: @@ -54,6 +55,12 @@ public: API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Rope\")") bool EnableStiffness = false; + /// + /// The rope simulation update substep (in seconds). Defines the frequency of physics update. + /// + API_FIELD(Attributes="EditorOrder(40), Limit(0, 0.1f, 0.0001f), EditorDisplay(\"Rope\")") + float SubstepTime = 0.02f; + private: void Tick(); @@ -61,8 +68,6 @@ private: public: // [Actor] - void Serialize(SerializeStream& stream, const void* otherObj) override; - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; void OnEnable() override; void OnDisable() override; void OnTransformChanged() override; From 311793dd7e5a88b8d3882f4b3ffc7b32104fbfb7 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 12 Feb 2021 22:50:47 +0100 Subject: [PATCH 383/419] Fix StartMouseCapture(true) with secondary monitors on the left Yes, negative mouse coordinates are a thing --- Source/Engine/Platform/Base/WindowBase.h | 1 + Source/Engine/Platform/Windows/WindowsWindow.cpp | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Platform/Base/WindowBase.h b/Source/Engine/Platform/Base/WindowBase.h index 3da48bb7a..a8215bc8b 100644 --- a/Source/Engine/Platform/Base/WindowBase.h +++ b/Source/Engine/Platform/Base/WindowBase.h @@ -284,6 +284,7 @@ protected: Vector2 _trackingMouseOffset; bool _isUsingMouseOffset; + Rectangle _mouseOffsetScreenSize; bool _isTrackingMouse; explicit WindowBase(const CreateWindowSettings& settings); diff --git a/Source/Engine/Platform/Windows/WindowsWindow.cpp b/Source/Engine/Platform/Windows/WindowsWindow.cpp index dc130949c..61c8737b8 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.cpp @@ -479,6 +479,10 @@ void WindowsWindow::StartTrackingMouse(bool useMouseScreenOffset) _trackingMouseOffset = Vector2::Zero; _isUsingMouseOffset = useMouseScreenOffset; + int32 x = 0 , y = 0, width = 0, height = 0; + GetScreenInfo(x, y, width, height); + _mouseOffsetScreenSize = Rectangle(x, y, width, height); + SetCapture(_handle); } } @@ -712,18 +716,20 @@ LRESULT WindowsWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) if (_isTrackingMouse && _isUsingMouseOffset) { // Check if move mouse to another edge of the desktop - Vector2 destopSize = Platform::GetVirtualDesktopSize(); + Vector2 desktopLocation = _mouseOffsetScreenSize.Location; + Vector2 destopSize = _mouseOffsetScreenSize.GetBottomRight(); + const Vector2 mousePos(static_cast(WINDOWS_GET_X_LPARAM(lParam)), static_cast(WINDOWS_GET_Y_LPARAM(lParam))); Vector2 mousePosition = ClientToScreen(mousePos); Vector2 newMousePosition = mousePosition; - if (mousePosition.X <= 1) + if (mousePosition.X <= desktopLocation.X + 2) newMousePosition.X = destopSize.X - 2; else if (mousePosition.X >= destopSize.X - 1) - newMousePosition.X = 2; - if (mousePosition.Y <= 1) + newMousePosition.X = desktopLocation.X + 2; + if (mousePosition.Y <= desktopLocation.Y + 2) newMousePosition.Y = destopSize.Y - 2; else if (mousePosition.Y >= destopSize.Y - 1) - newMousePosition.Y = 2; + newMousePosition.Y = desktopLocation.Y + 2; if (!Vector2::NearEqual(mousePosition, newMousePosition)) { _trackingMouseOffset -= newMousePosition - mousePosition; From ccc60af4b619ab81173315e7dd00b82ea9cf03aa Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Sat, 13 Feb 2021 00:46:39 +0200 Subject: [PATCH 384/419] Fix crash in Windows platform logging with long lines --- Source/Engine/Platform/Windows/WindowsPlatform.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index fa93800d8..cb9999a08 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -630,12 +630,12 @@ void WindowsPlatform::Exit() void WindowsPlatform::Log(const StringView& msg) { - Char buffer[500]; + Char buffer[512]; Char* str; if (msg.Length() + 3 < ARRAY_COUNT(buffer)) str = buffer; else - str = new Char[msg.Length() + 3]; + str = (Char*)Allocate((msg.Length() + 3) * sizeof(Char), 16); MemoryCopy(str, msg.Get(), msg.Length() * sizeof(Char)); str[msg.Length() + 0] = '\r'; str[msg.Length() + 1] = '\n'; From ac8c185056940588ee7e269ef938e574767a1ade Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sun, 14 Feb 2021 08:43:50 +0100 Subject: [PATCH 385/419] Fix TreeNode hovering with collapsed children and also set DefaultNodeOffsetY to get rid of the spaces between the nodes --- Source/Editor/GUI/Tree/TreeNode.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 727da3edd..f7e5f115a 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -21,7 +21,7 @@ namespace FlaxEditor.GUI.Tree /// /// The default node offset on Y axis. /// - public const float DefaultNodeOffsetY = 1; + public const float DefaultNodeOffsetY = 0; private Tree _tree; @@ -539,7 +539,7 @@ namespace FlaxEditor.GUI.Tree { if (new Rectangle(_headerRect.X, _headerRect.Y - DefaultDragInsertPositionMargin - DefaultNodeOffsetY, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location)) _dragOverMode = DragItemPositioning.Above; - else if (IsCollapsed && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location)) + else if ((IsCollapsed || !HasAnyVisibleChild) && new Rectangle(_headerRect.X, _headerRect.Bottom - DefaultDragInsertPositionMargin, _headerRect.Width, DefaultDragInsertPositionMargin * 2.0f).Contains(location)) _dragOverMode = DragItemPositioning.Below; else _dragOverMode = DragItemPositioning.At; From ed1a3c2964996602769c335f8bef9c609b7c1ec6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Feb 2021 14:54:56 +0100 Subject: [PATCH 386/419] Update mono --- Source/Engine/Scripting/ScriptingObject.cpp | 1 - .../Binaries/ThirdParty/x64/MonoPosixHelper.dll | 2 +- .../Windows/Binaries/ThirdParty/x64/eglib.pdb | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libmini.pdb | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libmono-static.lib | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb | 4 ++-- .../Windows/Binaries/ThirdParty/x64/libmonoutils.pdb | 4 ++-- Source/Tools/Flax.Build/Deps/Dependencies/mono.cs | 10 +++++----- 9 files changed, 18 insertions(+), 19 deletions(-) diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index dd9cf57a8..5cdea3ab4 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -11,7 +11,6 @@ #include "ManagedCLR/MClass.h" #include "ManagedCLR/MUtils.h" #include "ManagedCLR/MField.h" -#include "ManagedCLR/MUtils.h" #if PLATFORM_LINUX #include "ManagedCLR/MCore.h" #endif diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll index 65c280e99..dd082f208 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/MonoPosixHelper.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ad92c41f484482e217f791b5ae7963f5d55c2799bc5578125a88f2d40f33262 +oid sha256:35a252fcb61a805c85558646d073e32f7eb54666931a73380b4dea2879c72584 size 157696 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb index 1cb5d5f43..a79f59aac 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/eglib.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:86d71f01f6ba35e763467eb5b2be703831065ae7c9438fde4a32e1cd40d68880 -size 151552 +oid sha256:1ff34e923a27392a70eb465df1ecc03372aadac6f2753832cce3d82cd5ae1b75 +size 159744 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb index 2c08c3457..166a8df92 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libgcmonosgen.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7a6731228669866ebfb450bdd14bdb6e54eef8dcc10c36640016630ded6ef73 -size 290816 +oid sha256:d165b1a28ce64c030b4d242b86b813292ce038e0155ad0d6025ed7472e036ce4 +size 348160 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb index a06e2ddf2..719362b2d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmini.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6e12dfaf49963f4b736d9fbd7bbe16ccbf79045d64df71edc5209ffb25e0d1e0 -size 667648 +oid sha256:9cc98ae831f784a7dd6f15a1dbb083c63e6c50100a201501a7825a6d85baf53e +size 864256 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib index ab2d5015d..1cc28852d 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmono-static.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b90fdbcfab9d89be16789604edbab46ee8ed0159846be60714ef27ac4444baf2 -size 28391654 +oid sha256:2eac1fd52b8d87a0fe4dce3c02e68da8b35bfaa44cb1689d5d8935f2ba09531b +size 28241218 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb index e2e07cc4a..c60e7dbe4 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoruntime.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db47e63ec05d2c50d661f89c062931787be1722e0387f0076b910a9092fbe962 -size 995328 +oid sha256:188ebd527b432f33f8bfccb4fbada27b029f362918ab5c4b6f82584f9d30b6f3 +size 1249280 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoutils.pdb b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoutils.pdb index 87d823d36..30be1562b 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoutils.pdb +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/libmonoutils.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3846908f7e00e2ef725c4c770dc69c277491c7e83df6047871fc9cd672d47a6d -size 299008 +oid sha256:289966b7d9ff6255e38a6e45d2b9a5179c42e0f1add6abb70f7fa5501dfc59bc +size 356352 diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs b/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs index ca23d81ea..92c9bdec9 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs @@ -258,9 +258,8 @@ namespace Flax.Deps.Dependencies "mono_type_normalize", }; - private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture) + private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture, string configuration = "Release") { - var configuration = "Release"; string buildPlatform; switch (architecture) { @@ -491,12 +490,13 @@ namespace Flax.Deps.Dependencies { case TargetPlatform.Windows: { - BuildMsvc(options, platform, TargetArchitecture.x64); + var configuration = "Release"; + BuildMsvc(options, platform, TargetArchitecture.x64, configuration); //BuildBcl(options, platform); // Export header files - Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-dynamic.vcxproj"), "Release", "x64"); - Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "build-install.vcxproj"), "Release", "x64"); + Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-dynamic.vcxproj"), configuration, "x64"); + Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "build-install.vcxproj"), configuration, "x64"); // Get exported mono methods to forward them in engine module (on Win32 platforms) GetMonoExports(options); From 6bdc31df5a2360784a9ce0db76e68789f876a8ce Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Feb 2021 15:53:08 +0100 Subject: [PATCH 387/419] Fix using `get_Control` getter method from UIControl in Visual Script --- Source/Editor/Utilities/Utils.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index ac34d9e45..26c5ec92b 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -532,6 +532,8 @@ namespace FlaxEditor.Utilities break; case VariantType.Enum: case VariantType.Structure: + case VariantType.ManagedObject: + case VariantType.Typename: stream.Write(int.MaxValue); stream.WriteStrAnsi(type.FullName, 77); break; @@ -761,7 +763,7 @@ namespace FlaxEditor.Utilities data[i] = (byte)(c ^ 77); } var typeName = System.Text.Encoding.ASCII.GetString(data); - return TypeUtils.GetType(typeName).Type; + return TypeUtils.GetManagedType(typeName); } if (typeNameLength > 0) { @@ -773,7 +775,7 @@ namespace FlaxEditor.Utilities data[i] = (char)(c ^ 77); } var typeName = new string(data); - return TypeUtils.GetType(typeName).Type; + return TypeUtils.GetManagedType(typeName); } switch (variantType) { @@ -826,7 +828,7 @@ namespace FlaxEditor.Utilities data[i] = (byte)(c ^ 77); } var typeName = System.Text.Encoding.ASCII.GetString(data); - type = TypeUtils.GetType(typeName).Type; + type = TypeUtils.GetManagedType(typeName); } else if (typeNameLength > 0) { @@ -838,7 +840,7 @@ namespace FlaxEditor.Utilities data[i] = (char)(c ^ 77); } var typeName = new string(data); - type = TypeUtils.GetType(typeName).Type; + type = TypeUtils.GetManagedType(typeName); } switch (variantType) { From 3bbda838a22cfb98198aa62bfb9e9e27626bebe4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Feb 2021 16:50:35 +0100 Subject: [PATCH 388/419] Fix error when spawning prefab with rigidbody --- Source/Engine/Level/Actor.cpp | 2 ++ Source/Engine/Level/Level.cpp | 2 +- Source/Engine/Level/Prefabs/PrefabManager.cpp | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 81a5b3155..e88791442 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -43,6 +43,8 @@ Actor::Actor(const SpawnParams& params) , _staticFlags(StaticFlags::FullyStatic) , _localTransform(Transform::Identity) , _transform(Transform::Identity) + , _sphere(BoundingSphere::Empty) + , _box(BoundingBox::Empty) , HideFlags(HideFlags::None) { } diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 219893012..62d066803 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -973,7 +973,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, bool autoI if (obj && obj->GetParent() == nullptr) { sceneObjects->At(i) = nullptr; - LOG(Warning, "Scene object {0} {1} has missing parent object after scene load. Removing it.", obj->GetID(), obj->ToString()); + LOG(Warning, "Scene object {0} {1} has missing parent object after load. Removing it.", obj->GetID(), obj->ToString()); obj->DeleteObject(); } } diff --git a/Source/Engine/Level/Prefabs/PrefabManager.cpp b/Source/Engine/Level/Prefabs/PrefabManager.cpp index 44be0181b..512d6e9b7 100644 --- a/Source/Engine/Level/Prefabs/PrefabManager.cpp +++ b/Source/Engine/Level/Prefabs/PrefabManager.cpp @@ -181,6 +181,8 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary_parent = parent; + if (parent) + parent->Children.Add(root); // Link actors hierarchy for (int32 i = 0; i < sceneObjects->Count(); i++) @@ -226,7 +228,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, DictionaryGetParent() == nullptr) { sceneObjects->At(i) = nullptr; - LOG(Warning, "Scene object {0} {1} has missing parent objct after prefab spawn. Removing it.", obj->GetID(), obj->ToString()); + LOG(Warning, "Scene object {0} {1} has missing parent object after load. Removing it.", obj->GetID(), obj->ToString()); obj->DeleteObject(); } } From a947dc0cc372162d5de3dbd0a00a5cbb4de61047 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Feb 2021 17:12:29 +0100 Subject: [PATCH 389/419] Fix properties names formatting for UI with 2 character words Fixes #229 --- Source/Editor/CustomEditors/CustomEditorsUtil.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs index 2f97da333..9b83dd820 100644 --- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs +++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs @@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors // Space before word starting with uppercase letter if (char.IsUpper(c) && i > 0) { - if (i + 2 < length && !char.IsUpper(name[i + 1]) && !char.IsUpper(name[i + 2])) + if (i + 1 < length && !char.IsUpper(name[i + 1])) sb.Append(' '); } // Space instead of underscore From 865f2b871d2ff1430f1390126c3d5ac0477be526 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Feb 2021 18:09:29 +0100 Subject: [PATCH 390/419] Fix default frame rate for fbx imported clips to 14 https://github.com/nem0/OpenFBX/commit/2650de730d3f2d41b5042992f9443affd58e3553 --- Source/ThirdParty/OpenFBX/ofbx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/ThirdParty/OpenFBX/ofbx.cpp b/Source/ThirdParty/OpenFBX/ofbx.cpp index dc7c92718..36f942cf8 100644 --- a/Source/ThirdParty/OpenFBX/ofbx.cpp +++ b/Source/ThirdParty/OpenFBX/ofbx.cpp @@ -2894,7 +2894,7 @@ static float getFramerateFromTimeMode(FrameRate time_mode, float custom_frame_ra { switch (time_mode) { - case FrameRate_DEFAULT: return 1; + case FrameRate_DEFAULT: return 14; case FrameRate_120: return 120; case FrameRate_100: return 100; case FrameRate_60: return 60; From f82ef8c065ba875fb6b0ce59c491ed3860f47afc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Feb 2021 18:18:58 +0100 Subject: [PATCH 391/419] Fix AssetPicker buttons usage without mouse down click over the control Fixes #233 --- Source/Editor/GUI/AssetPicker.cs | 53 +++++++++++++++----------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/Source/Editor/GUI/AssetPicker.cs b/Source/Editor/GUI/AssetPicker.cs index 63c1276ab..db012ab1e 100644 --- a/Source/Editor/GUI/AssetPicker.cs +++ b/Source/Editor/GUI/AssetPicker.cs @@ -355,12 +355,10 @@ namespace FlaxEditor.GUI _mousePos = location; // Check if start drag drop - if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f) + if (_isMouseDown && Vector2.Distance(location, _mouseDownPos) > 10.0f && IconRect.Contains(_mouseDownPos)) { - // Clear flag - _isMouseDown = false; - // Do the drag + _isMouseDown = false; DoDrag(); } @@ -370,35 +368,35 @@ namespace FlaxEditor.GUI /// public override bool OnMouseUp(Vector2 location, MouseButton button) { - if (button == MouseButton.Left) + if (button == MouseButton.Left && _isMouseDown) { _isMouseDown = false; - } - // Buttons logic - if (Button1Rect.Contains(location)) - { - // Show asset picker popup - Focus(); - AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem => + // Buttons logic + if (Button1Rect.Contains(location)) { - SelectedItem = assetItem; - RootWindow.Focus(); + // Show asset picker popup Focus(); - }); - } - else if (_selected != null || _selectedItem != null) - { - if (Button2Rect.Contains(location) && _selectedItem != null) - { - // Select asset - Editor.Instance.Windows.ContentWin.Select(_selectedItem); + AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, assetItem => + { + SelectedItem = assetItem; + RootWindow.Focus(); + Focus(); + }); } - else if (Button3Rect.Contains(location)) + else if (_selected != null || _selectedItem != null) { - // Deselect asset - Focus(); - SelectedItem = null; + if (Button2Rect.Contains(location) && _selectedItem != null) + { + // Select asset + Editor.Instance.Windows.ContentWin.Select(_selectedItem); + } + else if (Button3Rect.Contains(location)) + { + // Deselect asset + Focus(); + SelectedItem = null; + } } } @@ -409,8 +407,7 @@ namespace FlaxEditor.GUI /// public override bool OnMouseDown(Vector2 location, MouseButton button) { - // Set flag for dragging asset - if (button == MouseButton.Left && IconRect.Contains(location)) + if (button == MouseButton.Left) { _isMouseDown = true; _mouseDownPos = location; From b4bf488e3c82f6f8f79173009af0cc1ca612af99 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Feb 2021 18:41:28 +0100 Subject: [PATCH 392/419] Fix double-click mouse event not setting mouse button down Fixes #235 --- Source/Engine/Input/Input.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 72cc6bcb9..825e7f6b2 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -191,6 +191,7 @@ bool Mouse::Update(EventQueue& queue) } case EventType::MouseDoubleClick: { + _state.MouseButtons[static_cast(e.MouseData.Button)] = true; break; } case EventType::MouseWheel: From e42a6b0cccbe122c01a476971211fc7f0d771a14 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 10:53:49 +0100 Subject: [PATCH 393/419] Fix default actor bounds --- Source/Engine/Level/Actor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actor.cpp b/Source/Engine/Level/Actor.cpp index 509fae98a..324e6ada1 100644 --- a/Source/Engine/Level/Actor.cpp +++ b/Source/Engine/Level/Actor.cpp @@ -43,7 +43,7 @@ Actor::Actor(const SpawnParams& params) , _localTransform(Transform::Identity) , _transform(Transform::Identity) , _sphere(BoundingSphere::Empty) - , _box(BoundingBox::Empty) + , _box(BoundingBox::Zero) , HideFlags(HideFlags::None) { } From c9ea812a2462bec0f8cf1ebd5288796afbefda87 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 11:52:06 +0100 Subject: [PATCH 394/419] Optimize DrawCall to pack indirect draw arg and graphics draw data with union --- Source/Engine/Graphics/Models/Mesh.cpp | 16 +++---- Source/Engine/Graphics/Models/SkinnedMesh.cpp | 6 +-- Source/Engine/Level/Actors/SplineModel.cpp | 2 - Source/Engine/Particles/ParticleManager.cpp | 20 ++++----- Source/Engine/Renderer/DrawCall.h | 45 +++++++++++-------- Source/Engine/Renderer/RenderList.cpp | 18 ++++---- .../Engine/ShadowsOfMordor/Builder.Jobs.cpp | 2 +- Source/Engine/Terrain/TerrainChunk.cpp | 4 -- Source/Engine/Terrain/TerrainManager.cpp | 4 +- Source/Engine/UI/TextRender.cpp | 6 +-- 10 files changed, 57 insertions(+), 66 deletions(-) diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index e7007f4ec..8285883ae 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -359,8 +359,8 @@ void Mesh::GetDrawCallGeometry(DrawCall& drawCall) const drawCall.Geometry.VertexBuffersOffsets[0] = 0; drawCall.Geometry.VertexBuffersOffsets[1] = 0; drawCall.Geometry.VertexBuffersOffsets[2] = 0; - drawCall.Geometry.StartIndex = 0; - drawCall.Geometry.IndicesCount = _triangles * 3; + drawCall.Draw.StartIndex = 0; + drawCall.Draw.IndicesCount = _triangles * 3; } void Mesh::Render(GPUContext* context) const @@ -386,11 +386,9 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons drawCall.Geometry.VertexBuffersOffsets[0] = 0; drawCall.Geometry.VertexBuffersOffsets[1] = 0; drawCall.Geometry.VertexBuffersOffsets[2] = 0; - drawCall.Geometry.StartIndex = 0; - drawCall.Geometry.IndicesCount = _triangles * 3; + drawCall.Draw.StartIndex = 0; + drawCall.Draw.IndicesCount = _triangles * 3; drawCall.InstanceCount = 1; - drawCall.IndirectArgsBuffer = nullptr; - drawCall.IndirectArgsOffset = 0; drawCall.Material = material; drawCall.World = world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); @@ -449,11 +447,9 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float drawCall.Geometry.VertexBuffers[2] = info.VertexColors[_lodIndex]; drawCall.Geometry.VertexBuffersOffsets[2] = vertexOffset * sizeof(VB2ElementType); } - drawCall.Geometry.StartIndex = 0; - drawCall.Geometry.IndicesCount = _triangles * 3; + drawCall.Draw.StartIndex = 0; + drawCall.Draw.IndicesCount = _triangles * 3; drawCall.InstanceCount = 1; - drawCall.IndirectArgsBuffer = nullptr; - drawCall.IndirectArgsOffset = 0; drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index e9e7f8438..9e26867ab 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -186,11 +186,9 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, drawCall.Geometry.VertexBuffersOffsets[0] = 0; drawCall.Geometry.VertexBuffersOffsets[1] = 0; drawCall.Geometry.VertexBuffersOffsets[2] = 0; - drawCall.Geometry.StartIndex = 0; - drawCall.Geometry.IndicesCount = _triangles * 3; + drawCall.Draw.StartIndex = 0; + drawCall.Draw.IndicesCount = _triangles * 3; drawCall.InstanceCount = 1; - drawCall.IndirectArgsBuffer = nullptr; - drawCall.IndirectArgsOffset = 0; drawCall.Material = material; drawCall.World = *info.World; drawCall.ObjectPosition = drawCall.World.GetTranslation(); diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 2a0972ffa..01118ba6a 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -353,8 +353,6 @@ void SplineModel::Draw(RenderContext& renderContext) // Draw all segments DrawCall drawCall; drawCall.InstanceCount = 1; - drawCall.IndirectArgsBuffer = nullptr; - drawCall.IndirectArgsOffset = 0; drawCall.Deformable.SplineDeformation = _deformationBuffer; drawCall.Deformable.ChunksPerSegment = _chunksPerSegment; drawCall.Deformable.MeshMinZ = _meshMinZ; diff --git a/Source/Engine/Particles/ParticleManager.cpp b/Source/Engine/Particles/ParticleManager.cpp index 429420950..f2e70fee3 100644 --- a/Source/Engine/Particles/ParticleManager.cpp +++ b/Source/Engine/Particles/ParticleManager.cpp @@ -83,8 +83,8 @@ public: drawCall.Geometry.VertexBuffersOffsets[0] = 0; drawCall.Geometry.VertexBuffersOffsets[1] = 0; drawCall.Geometry.VertexBuffersOffsets[2] = 0; - drawCall.Geometry.StartIndex = 0; - drawCall.Geometry.IndicesCount = IndexCount; + drawCall.Draw.StartIndex = 0; + drawCall.Draw.IndicesCount = IndexCount; } }; @@ -173,10 +173,6 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa const auto context = GPUDevice::Instance->GetMainContext(); auto emitter = buffer->Emitter; - drawCall.InstanceCount = 1; - drawCall.IndirectArgsBuffer = nullptr; - drawCall.IndirectArgsOffset = 0; - // Check if need to perform any particles sorting if (emitter->Graph.SortModules.HasItems() && renderContext.View.Pass != DrawPass::Depth) { @@ -516,8 +512,8 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa drawCall.Geometry.VertexBuffersOffsets[0] = 0; drawCall.Geometry.VertexBuffersOffsets[1] = 0; drawCall.Geometry.VertexBuffersOffsets[2] = 0; - drawCall.Geometry.StartIndex = ribbonModulesDrawIndicesStart[ribbonModuleIndex]; - drawCall.Geometry.IndicesCount = ribbonModulesDrawIndicesCount[ribbonModuleIndex]; + drawCall.Draw.StartIndex = ribbonModulesDrawIndicesStart[ribbonModuleIndex]; + drawCall.Draw.IndicesCount = ribbonModulesDrawIndicesCount[ribbonModuleIndex]; drawCall.InstanceCount = 1; renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false); @@ -802,8 +798,8 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa // Submit draw call SpriteRenderer.SetupDrawCall(drawCall); drawCall.InstanceCount = 0; - drawCall.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer; - drawCall.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs); + drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer; + drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs); renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false); indirectDrawCallIndex++; @@ -830,8 +826,8 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa // Execute draw call mesh.GetDrawCallGeometry(drawCall); drawCall.InstanceCount = 0; - drawCall.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer; - drawCall.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs); + drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer; + drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs); renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false); indirectDrawCallIndex++; } diff --git a/Source/Engine/Renderer/DrawCall.h b/Source/Engine/Renderer/DrawCall.h index bb25c0a57..036ba26bd 100644 --- a/Source/Engine/Renderer/DrawCall.h +++ b/Source/Engine/Renderer/DrawCall.h @@ -134,16 +134,6 @@ struct DrawCall /// The geometry vertex buffers byte offsets. /// uint32 VertexBuffersOffsets[3]; - - /// - /// The location of the first index read by the GPU from the index buffer. - /// - int32 StartIndex; - - /// - /// The indices count. - /// - int32 IndicesCount; } Geometry; /// @@ -151,15 +141,34 @@ struct DrawCall /// int32 InstanceCount; - /// - /// The indirect draw arguments offset. - /// - uint32 IndirectArgsOffset; + union + { + struct + { + /// + /// The location of the first index read by the GPU from the index buffer. + /// + int32 StartIndex; - /// - /// The indirect draw arguments buffer. - /// - GPUBuffer* IndirectArgsBuffer; + /// + /// The indices count. + /// + int32 IndicesCount; + }; + + struct + { + /// + /// The indirect draw arguments offset. + /// + uint32 IndirectArgsOffset; + + /// + /// The indirect draw arguments buffer. + /// + GPUBuffer* IndirectArgsBuffer; + }; + } Draw; // Per-material shader data packed into union union diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index dd711a5e0..7056e8737 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -514,8 +514,8 @@ namespace return a.Material == b.Material && a.Material->CanUseInstancing(handler) && Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 && - a.IndirectArgsBuffer == nullptr && - b.IndirectArgsBuffer == nullptr && + a.InstanceCount != 0 && + b.InstanceCount != 0 && a.WorldDeterminantSign == b.WorldDeterminantSign; } } @@ -690,20 +690,20 @@ DRAW: context->BindIB(drawCall.Geometry.IndexBuffer); - if (drawCall.IndirectArgsBuffer) + if (drawCall.InstanceCount == 0) { // No support for batching indirect draw calls ASSERT(batch.BatchSize == 1); context->BindVB(ToSpan(vb, vbCount), vbOffsets); - context->DrawIndexedInstancedIndirect(drawCall.IndirectArgsBuffer, drawCall.IndirectArgsOffset); + context->DrawIndexedInstancedIndirect(drawCall.Draw.IndirectArgsBuffer, drawCall.Draw.IndirectArgsOffset); } else { if (batch.BatchSize == 1) { context->BindVB(ToSpan(vb, vbCount), vbOffsets); - context->DrawIndexedInstanced(drawCall.Geometry.IndicesCount, batch.InstanceCount, 0, 0, drawCall.Geometry.StartIndex); + context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, batch.InstanceCount, 0, 0, drawCall.Draw.StartIndex); } else { @@ -712,7 +712,7 @@ DRAW: vbOffsets[vbCount] = 0; vbCount++; context->BindVB(ToSpan(vb, vbCount), vbOffsets); - context->DrawIndexedInstanced(drawCall.Geometry.IndicesCount, batch.InstanceCount, instanceBufferOffset, 0, drawCall.Geometry.StartIndex); + context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, batch.InstanceCount, instanceBufferOffset, 0, drawCall.Draw.StartIndex); instanceBufferOffset += batch.BatchSize; } @@ -735,13 +735,13 @@ DRAW: context->BindIB(drawCall.Geometry.IndexBuffer); context->BindVB(ToSpan(drawCall.Geometry.VertexBuffers, 3), drawCall.Geometry.VertexBuffersOffsets); - if (drawCall.IndirectArgsBuffer) + if (drawCall.InstanceCount == 0) { - context->DrawIndexedInstancedIndirect(drawCall.IndirectArgsBuffer, drawCall.IndirectArgsOffset); + context->DrawIndexedInstancedIndirect(drawCall.Draw.IndirectArgsBuffer, drawCall.Draw.IndirectArgsOffset); } else { - context->DrawIndexedInstanced(drawCall.Geometry.IndicesCount, drawCall.InstanceCount, 0, 0, drawCall.Geometry.StartIndex); + context->DrawIndexedInstanced(drawCall.Draw.IndicesCount, drawCall.InstanceCount, 0, 0, drawCall.Draw.StartIndex); } } } diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index 7ec466eee..20c2fb325 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -173,7 +173,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) context->SetState(_psRenderCacheTerrain); context->BindIB(drawCall.Geometry.IndexBuffer); context->BindVB(ToSpan(drawCall.Geometry.VertexBuffers, 1)); - context->DrawIndexed(drawCall.Geometry.IndicesCount, 0, drawCall.Geometry.StartIndex); + context->DrawIndexed(drawCall.Draw.IndicesCount, 0, drawCall.Draw.StartIndex); break; } diff --git a/Source/Engine/Terrain/TerrainChunk.cpp b/Source/Engine/Terrain/TerrainChunk.cpp index 63a7bb04a..08e5946ee 100644 --- a/Source/Engine/Terrain/TerrainChunk.cpp +++ b/Source/Engine/Terrain/TerrainChunk.cpp @@ -83,8 +83,6 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const if (TerrainManager::GetChunkGeometry(drawCall, chunkSize, lod)) return; drawCall.InstanceCount = 1; - drawCall.IndirectArgsBuffer = nullptr; - drawCall.IndirectArgsOffset = 0; drawCall.Material = _cachedDrawMaterial; drawCall.World = _world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); @@ -140,8 +138,6 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi if (TerrainManager::GetChunkGeometry(drawCall, chunkSize, lod)) return; drawCall.InstanceCount = 1; - drawCall.IndirectArgsBuffer = nullptr; - drawCall.IndirectArgsOffset = 0; drawCall.Material = material; drawCall.World = _world; drawCall.ObjectPosition = drawCall.World.GetTranslation(); diff --git a/Source/Engine/Terrain/TerrainManager.cpp b/Source/Engine/Terrain/TerrainManager.cpp index 0638acd45..caf620ff0 100644 --- a/Source/Engine/Terrain/TerrainManager.cpp +++ b/Source/Engine/Terrain/TerrainManager.cpp @@ -37,8 +37,8 @@ public: drawCall.Geometry.VertexBuffersOffsets[0] = 0; drawCall.Geometry.VertexBuffersOffsets[1] = 0; drawCall.Geometry.VertexBuffersOffsets[2] = 0; - drawCall.Geometry.StartIndex = 0; - drawCall.Geometry.IndicesCount = IndicesCount; + drawCall.Draw.StartIndex = 0; + drawCall.Draw.IndicesCount = IndicesCount; } }; diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index 5707b4461..8e00c2cc3 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -336,14 +336,12 @@ void TextRender::Draw(RenderContext& renderContext) drawCall.Geometry.VertexBuffersOffsets[1] = 0; drawCall.Geometry.VertexBuffersOffsets[2] = 0; drawCall.InstanceCount = 1; - drawCall.IndirectArgsBuffer = nullptr; - drawCall.IndirectArgsOffset = 0; // Submit draw calls for (const auto& e : _drawChunks) { - drawCall.Geometry.IndicesCount = e.IndicesCount; - drawCall.Geometry.StartIndex = e.StartIndex; + drawCall.Draw.IndicesCount = e.IndicesCount; + drawCall.Draw.StartIndex = e.StartIndex; drawCall.Material = e.Material; renderContext.List->AddDrawCall(drawModes, GetStaticFlags(), drawCall, true); } From cd2a02a5c8c34fc71cf0d5085554dc261fe2c31f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 11:55:31 +0100 Subject: [PATCH 395/419] Add `-shaderdebug` cmd line switch to Editor for shaders debugging --- Source/Engine/Engine/CommandLine.cpp | 1 + Source/Engine/Engine/CommandLine.h | 5 +++++ Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp | 8 ++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Engine/CommandLine.cpp b/Source/Engine/Engine/CommandLine.cpp index 707f99de1..edfc9c268 100644 --- a/Source/Engine/Engine/CommandLine.cpp +++ b/Source/Engine/Engine/CommandLine.cpp @@ -133,6 +133,7 @@ bool CommandLine::Parse(const Char* cmdLine) PARSE_BOOL_SWITCH("-genprojectfiles ", GenProjectFiles); PARSE_ARG_SWITCH("-build ", Build); PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile); + PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug); #endif diff --git a/Source/Engine/Engine/CommandLine.h b/Source/Engine/Engine/CommandLine.h index 065135b48..278620bf5 100644 --- a/Source/Engine/Engine/CommandLine.h +++ b/Source/Engine/Engine/CommandLine.h @@ -150,6 +150,11 @@ public: /// Nullable SkipCompile; + /// + /// -shaderdebug (enables debugging data generation for shaders and disables shader compiler optimizations) + /// + Nullable ShaderDebug; + #endif }; diff --git a/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp b/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp index 2446c5ea6..c97b5e5bf 100644 --- a/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp +++ b/Source/Engine/Graphics/Shaders/Cache/ShaderAssetBase.cpp @@ -3,6 +3,7 @@ #include "ShaderAssetBase.h" #include "ShaderStorage.h" #include "ShaderCacheManager.h" +#include "Engine/Engine/CommandLine.h" #include "Engine/Serialization/MemoryReadStream.h" #include "Engine/ShadowsOfMordor/AtlasChartsPacker.h" @@ -225,8 +226,11 @@ bool ShaderAssetBase::LoadShaderCache(ShaderCacheResult& result) options.SourceLength = sourceLength; options.Profile = shaderProfile; options.Output = &cacheStream; - //options.GenerateDebugData = true; - //options.NoOptimize = true; + if (CommandLine::Options.ShaderDebug) + { + options.GenerateDebugData = true; + options.NoOptimize = true; + } auto& platformDefine = options.Macros.AddOne(); #if PLATFORM_WINDOWS platformDefine.Name = "PLATFORM_WINDOWS"; From b6557cb15cfcf497ca29f322fbe96ee24de2a4d4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 12:02:25 +0100 Subject: [PATCH 396/419] Fix showing shader source code window over a calling window --- Source/Editor/Utilities/Utils.cs | 6 ++++-- Source/Editor/Windows/Assets/MaterialWindow.cs | 12 ++++-------- .../Editor/Windows/Assets/ParticleEmitterWindow.cs | 12 ++++-------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 6de74e6ea..95ad9ade5 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1646,7 +1646,8 @@ namespace FlaxEditor.Utilities /// /// The source code. /// The window title. - public static void ShowSourceCodeWindow(string source, string title) + /// The context control used to show source code window popup in a proper location. + public static void ShowSourceCodeWindow(string source, string title, Control control = null) { if (string.IsNullOrEmpty(source)) { @@ -1659,8 +1660,9 @@ namespace FlaxEditor.Utilities settings.AllowMaximize = false; settings.AllowMinimize = false; settings.HasSizingFrame = false; - settings.StartPosition = WindowStartPosition.CenterScreen; + settings.StartPosition = WindowStartPosition.CenterParent; settings.Size = new Vector2(500, 600) * Platform.DpiScale; + settings.Parent = control?.RootWindow?.Window ?? Editor.Instance.Windows.MainWindow; settings.Title = title; var dialog = Platform.CreateWindow(ref settings); diff --git a/Source/Editor/Windows/Assets/MaterialWindow.cs b/Source/Editor/Windows/Assets/MaterialWindow.cs index f49e6a79c..53d85f493 100644 --- a/Source/Editor/Windows/Assets/MaterialWindow.cs +++ b/Source/Editor/Windows/Assets/MaterialWindow.cs @@ -233,18 +233,14 @@ namespace FlaxEditor.Windows.Assets // Toolstrip _toolstrip.AddSeparator(); - _toolstrip.AddButton(editor.Icons.BracketsSlash32, () => ShowSourceCode(_asset)).LinkTooltip("Show generated shader source code"); + _toolstrip.AddButton(editor.Icons.BracketsSlash32, ShowSourceCode).LinkTooltip("Show generated shader source code"); _toolstrip.AddButton(editor.Icons.Docs32, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/index.html")).LinkTooltip("See documentation to learn more"); } - /// - /// Shows the material source code window. - /// - /// The material asset. - public static void ShowSourceCode(Material material) + private void ShowSourceCode() { - var source = Editor.GetShaderSourceCode(material); - Utilities.Utils.ShowSourceCodeWindow(source, "Material Source"); + var source = Editor.GetShaderSourceCode(_asset); + Utilities.Utils.ShowSourceCodeWindow(source, "Material Source", this); } /// diff --git a/Source/Editor/Windows/Assets/ParticleEmitterWindow.cs b/Source/Editor/Windows/Assets/ParticleEmitterWindow.cs index 17b5a928d..ab61a9d68 100644 --- a/Source/Editor/Windows/Assets/ParticleEmitterWindow.cs +++ b/Source/Editor/Windows/Assets/ParticleEmitterWindow.cs @@ -139,18 +139,14 @@ namespace FlaxEditor.Windows.Assets // Toolstrip _toolstrip.AddSeparator(); - _toolstrip.AddButton(editor.Icons.BracketsSlash32, () => ShowSourceCode(_asset)).LinkTooltip("Show generated shader source code"); + _toolstrip.AddButton(editor.Icons.BracketsSlash32, ShowSourceCode).LinkTooltip("Show generated shader source code"); _toolstrip.AddButton(editor.Icons.Docs32, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more"); } - /// - /// Shows the ParticleEmitter source code window. - /// - /// The ParticleEmitter asset. - public static void ShowSourceCode(ParticleEmitter particleEmitter) + private void ShowSourceCode() { - var source = Editor.GetShaderSourceCode(particleEmitter); - Utilities.Utils.ShowSourceCodeWindow(source, "Particle Emitter GPU Simulation Source"); + var source = Editor.GetShaderSourceCode(_asset); + Utilities.Utils.ShowSourceCodeWindow(source, "Particle Emitter GPU Simulation Source", this); } /// From 788907f3b121f322b44c7a29fabc94984e58ec4d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 12:09:46 +0100 Subject: [PATCH 397/419] Remove deprecated and unused `ISceneObject` and `ITransformable` --- Source/Editor/CustomEditors/CustomEditor.cs | 4 +- .../CustomEditors/Dedicated/ActorEditor.cs | 2 +- .../CustomEditors/Values/ValueContainer.cs | 4 +- Source/Editor/SceneGraph/SceneGraphNode.cs | 2 +- Source/Engine/Engine/ISceneObject.cs | 37 ------------------- Source/Engine/Engine/ITransformable.cs | 15 -------- Source/Engine/Level/Actor.cs | 2 +- Source/Engine/Scripting/Script.cs | 2 +- 8 files changed, 8 insertions(+), 60 deletions(-) delete mode 100644 Source/Engine/Engine/ISceneObject.cs delete mode 100644 Source/Engine/Engine/ITransformable.cs diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 5f992729f..347eb608f 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -624,7 +624,7 @@ namespace FlaxEditor.CustomEditors return FindPrefabRoot(actor.Parent); } - private ISceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId) + private SceneObject FindObjectWithPrefabObjectId(Actor actor, ref Guid prefabObjectId) { if (actor.PrefabObjectID == prefabObjectId) return actor; @@ -667,7 +667,7 @@ namespace FlaxEditor.CustomEditors { // Special case for object references // If prefab object has reference to other object in prefab needs to revert to matching prefab instance object not the reference prefab object value - if (Values.ReferenceValue is ISceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink) + if (Values.ReferenceValue is SceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink) { if (Values.Count > 1) { diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 924f5aeb3..b8491abfa 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -206,7 +206,7 @@ namespace FlaxEditor.CustomEditors.Dedicated node.Text = CustomEditorsUtil.GetPropertyNameUI(removed.PrefabObject.GetType().Name); } // Actor or Script - else if (editor.Values[0] is ISceneObject sceneObject) + else if (editor.Values[0] is SceneObject sceneObject) { node.TextColor = sceneObject.HasPrefabLink ? FlaxEngine.GUI.Style.Current.ProgressNormal : FlaxEngine.GUI.Style.Current.BackgroundSelected; node.Text = CustomEditorsUtil.GetPropertyNameUI(sceneObject.GetType().Name); diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs index b2f26bb7b..f8cecb801 100644 --- a/Source/Editor/CustomEditors/Values/ValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs @@ -167,14 +167,14 @@ namespace FlaxEditor.CustomEditors { if (_hasReferenceValue) { - if (_referenceValue is ISceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink) + if (_referenceValue is SceneObject referenceSceneObject && referenceSceneObject.HasPrefabLink) { for (int i = 0; i < Count; i++) { if (this[i] == referenceSceneObject) continue; - if (this[i] == null || (this[i] is ISceneObject valueSceneObject && valueSceneObject.PrefabObjectID != referenceSceneObject.PrefabObjectID)) + if (this[i] == null || (this[i] is SceneObject valueSceneObject && valueSceneObject.PrefabObjectID != referenceSceneObject.PrefabObjectID)) return true; } } diff --git a/Source/Editor/SceneGraph/SceneGraphNode.cs b/Source/Editor/SceneGraph/SceneGraphNode.cs index cc47c20f2..835295147 100644 --- a/Source/Editor/SceneGraph/SceneGraphNode.cs +++ b/Source/Editor/SceneGraph/SceneGraphNode.cs @@ -15,7 +15,7 @@ namespace FlaxEditor.SceneGraph /// A class is responsible for Scene Graph management. /// [HideInEditor] - public abstract class SceneGraphNode : ITransformable + public abstract class SceneGraphNode { /// /// The parent node. diff --git a/Source/Engine/Engine/ISceneObject.cs b/Source/Engine/Engine/ISceneObject.cs deleted file mode 100644 index 2b5ae7706..000000000 --- a/Source/Engine/Engine/ISceneObject.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using System; - -namespace FlaxEngine -{ - /// - /// Interface for scene objects that unifies various properties used across actors and scripts. - /// - public interface ISceneObject - { - /// - /// Gets the scene object which contains this object. - /// - Scene Scene { get; } - - /// - /// Gets a value indicating whether this object has a valid linkage to the prefab asset. - /// - bool HasPrefabLink { get; } - - /// - /// Gets the prefab asset ID. Empty if no prefab link exists. - /// - Guid PrefabID { get; } - - /// - /// Gets the ID of the object within a object that is used for synchronization with this object. Empty if no prefab link exists. - /// - Guid PrefabObjectID { get; } - - /// - /// Breaks the prefab linkage for this object (including all children). - /// - void BreakPrefabLink(); - } -} diff --git a/Source/Engine/Engine/ITransformable.cs b/Source/Engine/Engine/ITransformable.cs deleted file mode 100644 index a8bbd910c..000000000 --- a/Source/Engine/Engine/ITransformable.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -namespace FlaxEngine -{ - /// - /// Interface for objects that can be transformed. - /// - public interface ITransformable - { - /// - /// Gets or sets the transform. - /// - Transform Transform { get; set; } - } -} diff --git a/Source/Engine/Level/Actor.cs b/Source/Engine/Level/Actor.cs index 260fef44e..cfb454354 100644 --- a/Source/Engine/Level/Actor.cs +++ b/Source/Engine/Level/Actor.cs @@ -4,7 +4,7 @@ using System; namespace FlaxEngine { - partial class Actor : ITransformable, ISceneObject + partial class Actor { /// /// Returns true if object is fully static on the scene, otherwise false. diff --git a/Source/Engine/Scripting/Script.cs b/Source/Engine/Scripting/Script.cs index 7f8561c68..c1a0894f0 100644 --- a/Source/Engine/Scripting/Script.cs +++ b/Source/Engine/Scripting/Script.cs @@ -2,7 +2,7 @@ namespace FlaxEngine { - partial class Script : ISceneObject + partial class Script { /// /// Gets the scene object which contains this script. From 29f0834cc452b09ce7eb298e7f6933941bf1fb2a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 13:23:09 +0100 Subject: [PATCH 398/419] Optimize build tool --- .../Flax.Build/Bindings/BindingsGenerator.cs | 43 +++---- .../Tools/Flax.Build/Build/Builder.Rules.cs | 108 +++++++++++------- .../Build/NativeCpp/Builder.NativeCpp.cs | 7 +- Source/Tools/Flax.Build/Build/Platform.cs | 2 +- Source/Tools/Flax.Build/Build/Sdk.cs | 2 +- Source/Tools/Flax.Build/Deps/DepsBuilder.cs | 2 +- 6 files changed, 99 insertions(+), 65 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index 419820f56..93fe10c0b 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using Flax.Build.NativeCpp; using BuildData = Flax.Build.Builder.BuildData; @@ -58,11 +57,8 @@ namespace Flax.Build.Bindings private static ModuleInfo ParseModuleInner(BuildData buildData, Module module, BuildOptions moduleOptions = null) { - if (buildData.ModulesInfo.TryGetValue(module, out var moduleInfo)) - return moduleInfo; - // Setup bindings module info descriptor - moduleInfo = new ModuleInfo + var moduleInfo = new ModuleInfo { Module = module, Name = module.BinaryModuleName, @@ -81,26 +77,35 @@ namespace Flax.Build.Bindings throw new Exception($"Cannot parse module {module.Name} without options."); // Collect all header files that can have public symbols for API - var headerFiles = moduleOptions.SourceFiles.Where(x => x.EndsWith(".h")).ToList(); - - // Skip if no header files to process + var headerFiles = new List(moduleOptions.SourceFiles.Count / 2); + for (int i = 0; i < moduleOptions.SourceFiles.Count; i++) + { + if (moduleOptions.SourceFiles[i].EndsWith(".h", StringComparison.OrdinalIgnoreCase)) + headerFiles.Add(moduleOptions.SourceFiles[i]); + } if (headerFiles.Count == 0) return moduleInfo; + if (module.Name == "Core") + { + // Special case for Core module to ignore API tags defines + for (int i = 0; i < headerFiles.Count; i++) + { + if (headerFiles[i].EndsWith("Config.h", StringComparison.Ordinal)) + { + headerFiles.RemoveAt(i); + break; + } + } + } - // TODO: use aynsc tasks or thread pool to load and parse multiple header files at once using multi-threading + // TODO: use async tasks or thread pool to load and parse multiple header files at once using multi-threading // Find and load files with API tags string[] headerFilesContents = null; - //using (new ProfileEventScope("LoadHeaderFiles")) + using (new ProfileEventScope("LoadHeaderFiles")) { - var anyApi = false; for (int i = 0; i < headerFiles.Count; i++) { - // Skip scripting tags definitions file - if (headerFiles[i].Replace('\\', '/').EndsWith("Engine/Core/Config.h", StringComparison.Ordinal)) - continue; - - // Check if file contains any valid API tag var contents = File.ReadAllText(headerFiles[i]); for (int j = 0; j < ApiTokens.SearchTags.Length; j++) { @@ -109,15 +114,13 @@ namespace Flax.Build.Bindings if (headerFilesContents == null) headerFilesContents = new string[headerFiles.Count]; headerFilesContents[i] = contents; - anyApi = true; break; } } } - - if (!anyApi) - return moduleInfo; } + if (headerFilesContents == null) + return moduleInfo; // Skip if none of the headers was modified after last time generated C++ file was edited // TODO: skip parsing if module has API cache file -> then load it from disk diff --git a/Source/Tools/Flax.Build/Build/Builder.Rules.cs b/Source/Tools/Flax.Build/Build/Builder.Rules.cs index 9982e3601..1c4609c2e 100644 --- a/Source/Tools/Flax.Build/Build/Builder.Rules.cs +++ b/Source/Tools/Flax.Build/Build/Builder.Rules.cs @@ -12,12 +12,31 @@ namespace Flax.Build partial class Builder { private static RulesAssembly _rules; + private static Type[] _buildTypes; /// /// The build configuration files postfix. /// public static string BuildFilesPostfix = ".Build.cs"; + /// + /// The cached list of types from Flax.Build assembly. Reused by other build tool utilities to improve performance. + /// + internal static Type[] BuildTypes + { + get + { + if (_buildTypes == null) + { + using (new ProfileEventScope("CacheBuildTypes")) + { + _buildTypes = typeof(Program).Assembly.GetTypes(); + } + } + return _buildTypes; + } + } + /// /// The rules assembly data. /// @@ -128,7 +147,7 @@ namespace Flax.Build using (new ProfileEventScope("InitInBuildPlugins")) { - foreach (var type in typeof(Program).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Plugin)))) + foreach (var type in BuildTypes.Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(typeof(Plugin)))) { var plugin = (Plugin)Activator.CreateInstance(type); plugin.Init(); @@ -176,67 +195,74 @@ namespace Flax.Build // Prepare targets and modules objects Type[] types; - Target[] targetObjects; - Module[] moduleObjects; - Plugin[] pluginObjects; + var targetObjects = new List(16); + var moduleObjects = new List(256); + var pluginObjects = new List(); using (new ProfileEventScope("GetTypes")) { types = assembly.GetTypes(); - targetObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Target))).Select(type => + for (var i = 0; i < types.Length; i++) { - var target = (Target)Activator.CreateInstance(type); - - var targetFilename = target.Name + BuildFilesPostfix; - target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase)); - if (target.FilePath == null) + var type = types[i]; + if (!type.IsClass || type.IsAbstract) + continue; + if (type.IsSubclassOf(typeof(Target))) { - targetFilename = target.Name + "Target" + BuildFilesPostfix; + var target = (Target)Activator.CreateInstance(type); + + var targetFilename = target.Name + BuildFilesPostfix; target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase)); if (target.FilePath == null) { - if (target.Name.EndsWith("Target")) - { - targetFilename = target.Name.Substring(0, target.Name.Length - "Target".Length) + BuildFilesPostfix; - target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase)); - } + targetFilename = target.Name + "Target" + BuildFilesPostfix; + target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase)); if (target.FilePath == null) { - throw new Exception(string.Format("Failed to find source file path for {0}", target)); + if (target.Name.EndsWith("Target")) + { + targetFilename = target.Name.Substring(0, target.Name.Length - "Target".Length) + BuildFilesPostfix; + target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase)); + } + if (target.FilePath == null) + { + throw new Exception(string.Format("Failed to find source file path for {0}", target)); + } } } + target.FolderPath = Path.GetDirectoryName(target.FilePath); + target.Init(); + targetObjects.Add(target); } - target.FolderPath = Path.GetDirectoryName(target.FilePath); - target.Init(); - return target; - }).ToArray(); - moduleObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Module))).Select(type => - { - var module = (Module)Activator.CreateInstance(type); - - var moduleFilename = module.Name + BuildFilesPostfix; - module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase)); - if (module.FilePath == null) + else if (type.IsSubclassOf(typeof(Module))) { - moduleFilename = module.Name + "Module" + BuildFilesPostfix; + var module = (Module)Activator.CreateInstance(type); + + var moduleFilename = module.Name + BuildFilesPostfix; module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase)); if (module.FilePath == null) { - throw new Exception(string.Format("Failed to find source file path for {0}", module)); + moduleFilename = module.Name + "Module" + BuildFilesPostfix; + module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase)); + if (module.FilePath == null) + { + throw new Exception(string.Format("Failed to find source file path for {0}", module)); + } } + module.FolderPath = Path.GetDirectoryName(module.FilePath); + module.Init(); + moduleObjects.Add(module); } - module.FolderPath = Path.GetDirectoryName(module.FilePath); - module.Init(); - return module; - }).ToArray(); - pluginObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Plugin))).Select(type => - { - var plugin = (Plugin)Activator.CreateInstance(type); - plugin.Init(); - return plugin; - }).ToArray(); + else if (type.IsSubclassOf(typeof(Plugin))) + { + var plugin = (Plugin)Activator.CreateInstance(type); + + plugin.Init(); + pluginObjects.Add(plugin); + } + } } - _rules = new RulesAssembly(assembly, targetObjects, moduleObjects, pluginObjects); + _rules = new RulesAssembly(assembly, targetObjects.ToArray(), moduleObjects.ToArray(), pluginObjects.ToArray()); } return _rules; diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index 70d3722fb..af50cfe1e 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -330,7 +330,12 @@ namespace Flax.Build } // Collect all files to compile - var cppFiles = moduleOptions.SourceFiles.Where(x => x.EndsWith(".cpp")).ToList(); + var cppFiles = new List(moduleOptions.SourceFiles.Count / 2); + for (int i = 0; i < moduleOptions.SourceFiles.Count; i++) + { + if (moduleOptions.SourceFiles[i].EndsWith(".cpp", StringComparison.OrdinalIgnoreCase)) + cppFiles.Add(moduleOptions.SourceFiles[i]); + } if (!string.IsNullOrEmpty(module.BinaryModuleName)) { diff --git a/Source/Tools/Flax.Build/Build/Platform.cs b/Source/Tools/Flax.Build/Build/Platform.cs index 07a18a2a9..15addce27 100644 --- a/Source/Tools/Flax.Build/Build/Platform.cs +++ b/Source/Tools/Flax.Build/Build/Platform.cs @@ -157,7 +157,7 @@ namespace Flax.Build if (_platforms == null) { using (new ProfileEventScope("GetPlatforms")) - _platforms = typeof(Platform).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Platform))).Select(Activator.CreateInstance).Cast().ToArray(); + _platforms = Builder.BuildTypes.Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(typeof(Platform))).Select(Activator.CreateInstance).Cast().ToArray(); } foreach (var platform in _platforms) diff --git a/Source/Tools/Flax.Build/Build/Sdk.cs b/Source/Tools/Flax.Build/Build/Sdk.cs index 450d8f43f..5c7044705 100644 --- a/Source/Tools/Flax.Build/Build/Sdk.cs +++ b/Source/Tools/Flax.Build/Build/Sdk.cs @@ -68,7 +68,7 @@ namespace Flax.Build using (new ProfileEventScope("GetSdks")) { _sdks = new Dictionary(); - var types = typeof(Sdk).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Sdk))); + var types = Builder.BuildTypes.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Sdk))); foreach (var type in types) { object instance = null; diff --git a/Source/Tools/Flax.Build/Deps/DepsBuilder.cs b/Source/Tools/Flax.Build/Deps/DepsBuilder.cs index 7ac4b0d69..6d6eaf5b3 100644 --- a/Source/Tools/Flax.Build/Deps/DepsBuilder.cs +++ b/Source/Tools/Flax.Build/Deps/DepsBuilder.cs @@ -51,7 +51,7 @@ namespace Flax.Deps } // Get all deps - var dependencies = typeof(DepsBuilder).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(Dependency))).Select(Activator.CreateInstance).Cast().ToArray(); + var dependencies = Builder.BuildTypes.Where(x => x.IsSubclassOf(typeof(Dependency))).Select(Activator.CreateInstance).Cast().ToArray(); if (dependencies.Length == 0) Log.Warning("No dependencies found!"); for (var i = 0; i < dependencies.Length; i++) From cdd68c9a89705904db8f0c6bac32ef54801a05c3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 13:32:38 +0100 Subject: [PATCH 399/419] Add MaxConcurrency and ConcurrencyProcessorScale options for Flax.Build cmd line --- Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs | 4 ++-- Source/Tools/Flax.Build/Configuration.cs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs b/Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs index c874dba51..9b4a571f1 100644 --- a/Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs +++ b/Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs @@ -27,12 +27,12 @@ namespace Flax.Build.BuildSystem.Graph /// /// The maximum amount of threads to be used for the parallel execution. /// - public int ThreadCountMax = 1410; + public int ThreadCountMax = Configuration.MaxConcurrency; /// /// The amount of threads to allocate per processor. Use it to allocate more threads for faster execution or use less to keep reduce CPU usage during build. /// - public float ProcessorCountScale = 1.0f; + public float ProcessorCountScale = Configuration.ConcurrencyProcessorScale; /// public override int Execute(List tasks) diff --git a/Source/Tools/Flax.Build/Configuration.cs b/Source/Tools/Flax.Build/Configuration.cs index 4fb58c4f2..baa4e30b1 100644 --- a/Source/Tools/Flax.Build/Configuration.cs +++ b/Source/Tools/Flax.Build/Configuration.cs @@ -133,6 +133,18 @@ namespace Flax.Build [CommandLine("logfile", "", "The log file path relative to the working directory. Set to empty to disable it/")] public static string LogFile = "Cache/Intermediate/Log.txt"; + /// + /// The maximum allowed concurrency for a build system (maximum active worker threads count). + /// + [CommandLine("maxConcurrency", "", "The maximum allowed concurrency for a build system (maximum active worker threads count).")] + public static int MaxConcurrency = 1410; + + /// + /// The concurrency scale for a build system that specifies how many worker threads allocate per-logical processor. + /// + [CommandLine("concurrencyProcessorScale", "", "The concurrency scale for a build system that specifies how many worker threads allocate per-logical processor.")] + public static float ConcurrencyProcessorScale = 1.0f; + /// /// The output binaries folder path relative to the working directory. /// From 5714741a5d55d7fe3da253f19b4f86c281dbccc8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 15:04:23 +0100 Subject: [PATCH 400/419] Add support for multi-threaded scripting API headers parsing --- .../Bindings/BindingsGenerator.Parsing.cs | 19 +- .../Flax.Build/Bindings/BindingsGenerator.cs | 606 +++++++++--------- Source/Tools/Flax.Build/Bindings/FileInfo.cs | 9 +- .../Tools/Flax.Build/Bindings/ModuleInfo.cs | 9 + 4 files changed, 331 insertions(+), 312 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index c5c58c318..66f93fde3 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -17,6 +17,7 @@ namespace Flax.Build.Bindings public Stack ScopeTypeStack; public Stack ScopeAccessStack; public Dictionary PreprocessorDefines; + public List StringCache; public ApiTypeInfo ValidScopeInfoFromStack { @@ -46,14 +47,12 @@ namespace Flax.Build.Bindings } } - private static List _commentCache; - private static string[] ParseComment(ref ParsingContext context) { - if (_commentCache == null) - _commentCache = new List(); + if (context.StringCache == null) + context.StringCache = new List(); else - _commentCache.Clear(); + context.StringCache.Clear(); int tokensCount = 0; bool isValid = true; @@ -77,7 +76,7 @@ namespace Flax.Build.Bindings if (commentLine.StartsWith("// ")) commentLine = "/// " + commentLine.Substring(3); - _commentCache.Insert(0, commentLine); + context.StringCache.Insert(0, commentLine); break; } default: @@ -90,14 +89,14 @@ namespace Flax.Build.Bindings for (var i = 0; i < tokensCount; i++) context.Tokenizer.NextToken(true, true); - if (_commentCache.Count == 1) + if (context.StringCache.Count == 1) { // Ensure to have summary begin/end pair - _commentCache.Insert(0, "/// "); - _commentCache.Add("/// "); + context.StringCache.Insert(0, "/// "); + context.StringCache.Add("/// "); } - return _commentCache.ToArray(); + return context.StringCache.ToArray(); } private struct TagParameter diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index 93fe10c0b..1b12cb838 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using Flax.Build.NativeCpp; using BuildData = Flax.Build.Builder.BuildData; @@ -98,324 +100,327 @@ namespace Flax.Build.Bindings } } - // TODO: use async tasks or thread pool to load and parse multiple header files at once using multi-threading - - // Find and load files with API tags - string[] headerFilesContents = null; - using (new ProfileEventScope("LoadHeaderFiles")) + // Parse bindings + Log.Verbose($"Parsing API bindings for {module.Name} ({moduleInfo.Name})"); + int concurrency = Math.Min(Math.Max(1, (int)(Environment.ProcessorCount * Configuration.ConcurrencyProcessorScale)), Configuration.MaxConcurrency); + concurrency = 1; // Disable concurrency for parsing (the gain is unnoticeable or even worse in some cases) + if (concurrency == 1 || headerFiles.Count < 2 * concurrency) { + // Single-threaded for (int i = 0; i < headerFiles.Count; i++) { - var contents = File.ReadAllText(headerFiles[i]); - for (int j = 0; j < ApiTokens.SearchTags.Length; j++) + using (new ProfileEventScope(Path.GetFileName(headerFiles[i]))) { - if (contents.Contains(ApiTokens.SearchTags[j])) - { - if (headerFilesContents == null) - headerFilesContents = new string[headerFiles.Count]; - headerFilesContents[i] = contents; - break; - } + ParseModuleInnerAsync(moduleInfo, moduleOptions, headerFiles, i); } } } - if (headerFilesContents == null) - return moduleInfo; - - // Skip if none of the headers was modified after last time generated C++ file was edited - // TODO: skip parsing if module has API cache file -> then load it from disk - /*if (!forceGenerate) + else { - var lastGenerateTime = File.GetLastWriteTime(bindings.GeneratedCppFilePath); - var anyModified = false; - for (int i = 0; i < headerFiles.Count; i++) + // Sort by file size to improve performance (parse larger files first) + headerFiles.Sort((a, b) => new System.IO.FileInfo(b).Length.CompareTo(new System.IO.FileInfo(a).Length)); + + // Multi-threaded + ThreadPool.GetMinThreads(out var workerThreads, out var completionPortThreads); + if (workerThreads != concurrency) + ThreadPool.SetMaxThreads(concurrency, completionPortThreads); + Parallel.For(0, headerFiles.Count, (i, state) => { - if (File.GetLastWriteTime(headerFiles[i]) > lastGenerateTime) + using (new ProfileEventScope(Path.GetFileName(headerFiles[i]))) { - anyModified = true; - break; + ParseModuleInnerAsync(moduleInfo, moduleOptions, headerFiles, i); } - } - - if (!anyModified) - return; - }*/ - - Log.Verbose($"Parsing API bindings for {module.Name} ({moduleInfo.Name})"); - - // Process all header files to generate the module API code reflection - var context = new ParsingContext - { - CurrentAccessLevel = AccessLevel.Public, - ScopeTypeStack = new Stack(), - ScopeAccessStack = new Stack(), - PreprocessorDefines = new Dictionary(), - }; - for (int i = 0; i < headerFiles.Count; i++) - { - if (headerFilesContents[i] == null) - continue; - var fileInfo = new FileInfo - { - Parent = null, - Children = new List(), - Name = headerFiles[i], - Namespace = moduleInfo.Name, - }; - moduleInfo.AddChild(fileInfo); - - try - { - // Tokenize the source - var tokenizer = new Tokenizer(); - tokenizer.Tokenize(headerFilesContents[i]); - - // Init the context - context.Tokenizer = tokenizer; - context.File = fileInfo; - context.ScopeInfo = null; - context.ScopeTypeStack.Clear(); - context.ScopeAccessStack.Clear(); - context.PreprocessorDefines.Clear(); - context.EnterScope(fileInfo); - - // Process the source code - ApiTypeInfo scopeType = null; - Token prevToken = null; - while (true) - { - // Move to the next token - var token = tokenizer.NextToken(); - if (token == null) - continue; - if (token.Type == TokenType.EndOfFile) - break; - - // Parse API_.. tags in source code - if (token.Type == TokenType.Identifier && token.Value.StartsWith("API_", StringComparison.Ordinal)) - { - if (string.Equals(token.Value, ApiTokens.Class, StringComparison.Ordinal)) - { - if (!(context.ScopeInfo is FileInfo)) - throw new NotImplementedException("TODO: add support for nested classes in scripting API"); - - var classInfo = ParseClass(ref context); - scopeType = classInfo; - context.ScopeInfo.AddChild(scopeType); - context.CurrentAccessLevel = AccessLevel.Public; - } - else if (string.Equals(token.Value, ApiTokens.Property, StringComparison.Ordinal)) - { - var propertyInfo = ParseProperty(ref context); - } - else if (string.Equals(token.Value, ApiTokens.Function, StringComparison.Ordinal)) - { - var functionInfo = ParseFunction(ref context); - - if (context.ScopeInfo is ClassInfo classInfo) - classInfo.Functions.Add(functionInfo); - else if (context.ScopeInfo is StructureInfo structureInfo) - structureInfo.Functions.Add(functionInfo); - else - throw new Exception($"Not supported free-function {functionInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it."); - } - else if (string.Equals(token.Value, ApiTokens.Enum, StringComparison.Ordinal)) - { - var enumInfo = ParseEnum(ref context); - context.ScopeInfo.AddChild(enumInfo); - } - else if (string.Equals(token.Value, ApiTokens.Struct, StringComparison.Ordinal)) - { - var structureInfo = ParseStructure(ref context); - scopeType = structureInfo; - context.ScopeInfo.AddChild(scopeType); - context.CurrentAccessLevel = AccessLevel.Public; - } - else if (string.Equals(token.Value, ApiTokens.Field, StringComparison.Ordinal)) - { - var fieldInfo = ParseField(ref context); - var scopeInfo = context.ValidScopeInfoFromStack; - - if (scopeInfo is ClassInfo classInfo) - classInfo.Fields.Add(fieldInfo); - else if (scopeInfo is StructureInfo structureInfo) - structureInfo.Fields.Add(fieldInfo); - else - throw new Exception($"Not supported location for field {fieldInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class or structure to use API bindings for it."); - } - else if (string.Equals(token.Value, ApiTokens.Event, StringComparison.Ordinal)) - { - var eventInfo = ParseEvent(ref context); - var scopeInfo = context.ValidScopeInfoFromStack; - - if (scopeInfo is ClassInfo classInfo) - classInfo.Events.Add(eventInfo); - else - throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it."); - } - else if (string.Equals(token.Value, ApiTokens.InjectCppCode, StringComparison.Ordinal)) - { - var injectCppCodeInfo = ParseInjectCppCode(ref context); - fileInfo.AddChild(injectCppCodeInfo); - } - else if (string.Equals(token.Value, ApiTokens.Interface, StringComparison.Ordinal)) - { - if (!(context.ScopeInfo is FileInfo)) - throw new NotImplementedException("TODO: add support for nested interfaces in scripting API"); - - var interfaceInfo = ParseInterface(ref context); - scopeType = interfaceInfo; - context.ScopeInfo.AddChild(scopeType); - context.CurrentAccessLevel = AccessLevel.Public; - } - else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal)) - { - if (context.ScopeInfo is ClassInfo classInfo) - classInfo.IsAutoSerialization = true; - else if (context.ScopeInfo is StructureInfo structureInfo) - structureInfo.IsAutoSerialization = true; - else - throw new Exception($"Not supported location for {ApiTokens.AutoSerialization} at line {tokenizer.CurrentLine}. Place it in the class or structure that uses API bindings."); - } - } - - // Track access level inside class - if (context.ScopeInfo != null && token.Type == TokenType.Colon && prevToken != null && prevToken.Type == TokenType.Identifier) - { - if (string.Equals(prevToken.Value, "public", StringComparison.Ordinal)) - { - context.CurrentAccessLevel = AccessLevel.Public; - } - else if (string.Equals(prevToken.Value, "protected", StringComparison.Ordinal)) - { - context.CurrentAccessLevel = AccessLevel.Protected; - } - else if (string.Equals(prevToken.Value, "private", StringComparison.Ordinal)) - { - context.CurrentAccessLevel = AccessLevel.Private; - } - } - - // Handle preprocessor blocks - if (token.Type == TokenType.Preprocessor) - { - token = tokenizer.NextToken(); - switch (token.Value) - { - case "define": - { - token = tokenizer.NextToken(); - var name = token.Value; - var value = string.Empty; - token = tokenizer.NextToken(true); - while (token.Type != TokenType.Newline) - { - value += token.Value; - token = tokenizer.NextToken(true); - } - value = value.Trim(); - context.PreprocessorDefines[name] = value; - break; - } - case "if": - { - // Parse condition - var condition = string.Empty; - token = tokenizer.NextToken(true); - while (token.Type != TokenType.Newline) - { - var tokenValue = token.Value.Trim(); - if (tokenValue.Length == 0) - { - token = tokenizer.NextToken(true); - continue; - } - - // Very simple defines processing - tokenValue = ReplacePreProcessorDefines(tokenValue, context.PreprocessorDefines.Keys); - tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PublicDefinitions); - tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PrivateDefinitions); - tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.CompileEnv.PreprocessorDefinitions); - tokenValue = tokenValue.Replace("false", "0"); - tokenValue = tokenValue.Replace("true", "1"); - tokenValue = tokenValue.Replace("||", "|"); - if (tokenValue.Length != 0 && tokenValue != "1" && tokenValue != "0" && tokenValue != "|") - tokenValue = "0"; - - condition += tokenValue; - token = tokenizer.NextToken(true); - } - - // Filter condition - condition = condition.Replace("1|1", "1"); - condition = condition.Replace("1|0", "1"); - condition = condition.Replace("0|1", "1"); - - // Skip chunk of code of condition fails - if (condition != "1") - { - ParsePreprocessorIf(fileInfo, tokenizer, ref token); - } - - break; - } - case "ifdef": - { - // Parse condition - var define = string.Empty; - token = tokenizer.NextToken(true); - while (token.Type != TokenType.Newline) - { - define += token.Value; - token = tokenizer.NextToken(true); - } - - // Check condition - define = define.Trim(); - if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define)) - { - ParsePreprocessorIf(fileInfo, tokenizer, ref token); - } - - break; - } - } - } - - // Scope tracking - if (token.Type == TokenType.LeftCurlyBrace) - { - context.ScopeTypeStack.Push(scopeType); - context.ScopeInfo = context.ScopeTypeStack.Peek(); - scopeType = null; - } - else if (token.Type == TokenType.RightCurlyBrace) - { - context.ScopeTypeStack.Pop(); - if (context.ScopeTypeStack.Count == 0) - throw new Exception($"Mismatch of the {{}} braces pair in file '{fileInfo.Name}' at line {tokenizer.CurrentLine}."); - context.ScopeInfo = context.ScopeTypeStack.Peek(); - if (context.ScopeInfo is FileInfo) - context.CurrentAccessLevel = AccessLevel.Public; - } - - prevToken = token; - } - } - catch (Exception ex) - { - Log.Error($"Failed to parse '{fileInfo.Name}' file to generate bindings."); - Log.Exception(ex); - throw; - } + }); } // Initialize API - moduleInfo.Init(buildData); + using (new ProfileEventScope("Init")) + { + moduleInfo.Init(buildData); + } return moduleInfo; } + private static void ParseModuleInnerAsync(ModuleInfo moduleInfo, BuildOptions moduleOptions, List headerFiles, int workIndex) + { + // Find and load files with API tags + bool hasApi = false; + string headerFileContents = File.ReadAllText(headerFiles[workIndex]); + for (int j = 0; j < ApiTokens.SearchTags.Length; j++) + { + if (headerFileContents.Contains(ApiTokens.SearchTags[j])) + { + hasApi = true; + break; + } + } + if (!hasApi) + return; + + // Process header file to generate the module API code reflection + var fileInfo = new FileInfo + { + Parent = null, + Children = new List(), + Name = headerFiles[workIndex], + Namespace = moduleInfo.Name, + }; + lock (moduleInfo) + { + moduleInfo.AddChild(fileInfo); + } + + try + { + // Tokenize the source + var tokenizer = new Tokenizer(); + tokenizer.Tokenize(headerFileContents); + + // Init the context + var context = new ParsingContext + { + File = fileInfo, + Tokenizer = tokenizer, + ScopeInfo = null, + CurrentAccessLevel = AccessLevel.Public, + ScopeTypeStack = new Stack(), + ScopeAccessStack = new Stack(), + PreprocessorDefines = new Dictionary(), + }; + context.EnterScope(fileInfo); + + // Process the source code + ApiTypeInfo scopeType = null; + Token prevToken = null; + while (true) + { + // Move to the next token + var token = tokenizer.NextToken(); + if (token == null) + continue; + if (token.Type == TokenType.EndOfFile) + break; + + // Parse API_.. tags in source code + if (token.Type == TokenType.Identifier && token.Value.StartsWith("API_", StringComparison.Ordinal)) + { + if (string.Equals(token.Value, ApiTokens.Class, StringComparison.Ordinal)) + { + if (!(context.ScopeInfo is FileInfo)) + throw new NotImplementedException("TODO: add support for nested classes in scripting API"); + + var classInfo = ParseClass(ref context); + scopeType = classInfo; + context.ScopeInfo.AddChild(scopeType); + context.CurrentAccessLevel = AccessLevel.Public; + } + else if (string.Equals(token.Value, ApiTokens.Property, StringComparison.Ordinal)) + { + var propertyInfo = ParseProperty(ref context); + } + else if (string.Equals(token.Value, ApiTokens.Function, StringComparison.Ordinal)) + { + var functionInfo = ParseFunction(ref context); + + if (context.ScopeInfo is ClassInfo classInfo) + classInfo.Functions.Add(functionInfo); + else if (context.ScopeInfo is StructureInfo structureInfo) + structureInfo.Functions.Add(functionInfo); + else + throw new Exception($"Not supported free-function {functionInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it."); + } + else if (string.Equals(token.Value, ApiTokens.Enum, StringComparison.Ordinal)) + { + var enumInfo = ParseEnum(ref context); + context.ScopeInfo.AddChild(enumInfo); + } + else if (string.Equals(token.Value, ApiTokens.Struct, StringComparison.Ordinal)) + { + var structureInfo = ParseStructure(ref context); + scopeType = structureInfo; + context.ScopeInfo.AddChild(scopeType); + context.CurrentAccessLevel = AccessLevel.Public; + } + else if (string.Equals(token.Value, ApiTokens.Field, StringComparison.Ordinal)) + { + var fieldInfo = ParseField(ref context); + var scopeInfo = context.ValidScopeInfoFromStack; + + if (scopeInfo is ClassInfo classInfo) + classInfo.Fields.Add(fieldInfo); + else if (scopeInfo is StructureInfo structureInfo) + structureInfo.Fields.Add(fieldInfo); + else + throw new Exception($"Not supported location for field {fieldInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class or structure to use API bindings for it."); + } + else if (string.Equals(token.Value, ApiTokens.Event, StringComparison.Ordinal)) + { + var eventInfo = ParseEvent(ref context); + var scopeInfo = context.ValidScopeInfoFromStack; + + if (scopeInfo is ClassInfo classInfo) + classInfo.Events.Add(eventInfo); + else + throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it."); + } + else if (string.Equals(token.Value, ApiTokens.InjectCppCode, StringComparison.Ordinal)) + { + var injectCppCodeInfo = ParseInjectCppCode(ref context); + fileInfo.AddChild(injectCppCodeInfo); + } + else if (string.Equals(token.Value, ApiTokens.Interface, StringComparison.Ordinal)) + { + if (!(context.ScopeInfo is FileInfo)) + throw new NotImplementedException("TODO: add support for nested interfaces in scripting API"); + + var interfaceInfo = ParseInterface(ref context); + scopeType = interfaceInfo; + context.ScopeInfo.AddChild(scopeType); + context.CurrentAccessLevel = AccessLevel.Public; + } + else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal)) + { + if (context.ScopeInfo is ClassInfo classInfo) + classInfo.IsAutoSerialization = true; + else if (context.ScopeInfo is StructureInfo structureInfo) + structureInfo.IsAutoSerialization = true; + else + throw new Exception($"Not supported location for {ApiTokens.AutoSerialization} at line {tokenizer.CurrentLine}. Place it in the class or structure that uses API bindings."); + } + } + + // Track access level inside class + if (context.ScopeInfo != null && token.Type == TokenType.Colon && prevToken != null && prevToken.Type == TokenType.Identifier) + { + if (string.Equals(prevToken.Value, "public", StringComparison.Ordinal)) + { + context.CurrentAccessLevel = AccessLevel.Public; + } + else if (string.Equals(prevToken.Value, "protected", StringComparison.Ordinal)) + { + context.CurrentAccessLevel = AccessLevel.Protected; + } + else if (string.Equals(prevToken.Value, "private", StringComparison.Ordinal)) + { + context.CurrentAccessLevel = AccessLevel.Private; + } + } + + // Handle preprocessor blocks + if (token.Type == TokenType.Preprocessor) + { + token = tokenizer.NextToken(); + switch (token.Value) + { + case "define": + { + token = tokenizer.NextToken(); + var name = token.Value; + var value = string.Empty; + token = tokenizer.NextToken(true); + while (token.Type != TokenType.Newline) + { + value += token.Value; + token = tokenizer.NextToken(true); + } + value = value.Trim(); + context.PreprocessorDefines[name] = value; + break; + } + case "if": + { + // Parse condition + var condition = string.Empty; + token = tokenizer.NextToken(true); + while (token.Type != TokenType.Newline) + { + var tokenValue = token.Value.Trim(); + if (tokenValue.Length == 0) + { + token = tokenizer.NextToken(true); + continue; + } + + // Very simple defines processing + tokenValue = ReplacePreProcessorDefines(tokenValue, context.PreprocessorDefines.Keys); + tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PublicDefinitions); + tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PrivateDefinitions); + tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.CompileEnv.PreprocessorDefinitions); + tokenValue = tokenValue.Replace("false", "0"); + tokenValue = tokenValue.Replace("true", "1"); + tokenValue = tokenValue.Replace("||", "|"); + if (tokenValue.Length != 0 && tokenValue != "1" && tokenValue != "0" && tokenValue != "|") + tokenValue = "0"; + + condition += tokenValue; + token = tokenizer.NextToken(true); + } + + // Filter condition + condition = condition.Replace("1|1", "1"); + condition = condition.Replace("1|0", "1"); + condition = condition.Replace("0|1", "1"); + + // Skip chunk of code of condition fails + if (condition != "1") + { + ParsePreprocessorIf(fileInfo, tokenizer, ref token); + } + + break; + } + case "ifdef": + { + // Parse condition + var define = string.Empty; + token = tokenizer.NextToken(true); + while (token.Type != TokenType.Newline) + { + define += token.Value; + token = tokenizer.NextToken(true); + } + + // Check condition + define = define.Trim(); + if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define)) + { + ParsePreprocessorIf(fileInfo, tokenizer, ref token); + } + + break; + } + } + } + + // Scope tracking + if (token.Type == TokenType.LeftCurlyBrace) + { + context.ScopeTypeStack.Push(scopeType); + context.ScopeInfo = context.ScopeTypeStack.Peek(); + scopeType = null; + } + else if (token.Type == TokenType.RightCurlyBrace) + { + context.ScopeTypeStack.Pop(); + if (context.ScopeTypeStack.Count == 0) + throw new Exception($"Mismatch of the {{}} braces pair in file '{fileInfo.Name}' at line {tokenizer.CurrentLine}."); + context.ScopeInfo = context.ScopeTypeStack.Peek(); + if (context.ScopeInfo is FileInfo) + context.CurrentAccessLevel = AccessLevel.Public; + } + + prevToken = token; + } + } + catch (Exception ex) + { + Log.Error($"Failed to parse '{fileInfo.Name}' file to generate bindings."); + Log.Exception(ex); + throw; + } + } + private static string ReplacePreProcessorDefines(string text, IEnumerable defines) { foreach (var define in defines) @@ -594,7 +599,6 @@ namespace Flax.Build.Bindings { foreach (var fieldInfo in structureInfo.Fields) { - // TODO: support bit-fields in structure fields if (fieldInfo.Type.IsBitField) throw new NotImplementedException($"TODO: support bit-fields in structure fields (found field {fieldInfo} in structure {structureInfo.Name})"); diff --git a/Source/Tools/Flax.Build/Bindings/FileInfo.cs b/Source/Tools/Flax.Build/Bindings/FileInfo.cs index 25e1e1944..6c4b4dcfe 100644 --- a/Source/Tools/Flax.Build/Bindings/FileInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FileInfo.cs @@ -7,7 +7,7 @@ namespace Flax.Build.Bindings /// /// The native file information for bindings generator. /// - public class FileInfo : ApiTypeInfo, IComparable + public class FileInfo : ApiTypeInfo, IComparable, IComparable { public override void AddChild(ApiTypeInfo apiTypeInfo) { @@ -26,5 +26,12 @@ namespace Flax.Build.Bindings { return System.IO.Path.GetFileName(Name); } + + public int CompareTo(object obj) + { + if (obj is ApiTypeInfo apiTypeInfo) + return Name.CompareTo(apiTypeInfo.Name); + return 0; + } } } diff --git a/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs b/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs index 6626145a9..7bb6d1608 100644 --- a/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs @@ -13,5 +13,14 @@ namespace Flax.Build.Bindings { return "module " + Name; } + + /// + public override void Init(Builder.BuildData buildData) + { + base.Init(buildData); + + // Sort module files to prevent bindings rebuild due to order changes (list might be created in async) + Children.Sort(); + } } } From 4e19b85e00489920f93cbb7e325b62ec0289d738 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 15:04:37 +0100 Subject: [PATCH 401/419] Add support for multi-threaded profile events in build tool --- Source/Tools/Flax.Build/Build/Profiling.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Profiling.cs b/Source/Tools/Flax.Build/Build/Profiling.cs index 98bc2c62a..c5cc89bcb 100644 --- a/Source/Tools/Flax.Build/Build/Profiling.cs +++ b/Source/Tools/Flax.Build/Build/Profiling.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Collections.Generic; using System.Text; +using System.Threading; namespace Flax.Build { @@ -12,7 +13,7 @@ namespace Flax.Build /// public class ProfileEventScope : IDisposable { - private int _id; + private readonly int _id; /// /// Initializes a new instance of the class. @@ -61,6 +62,11 @@ namespace Flax.Build /// The event call depth (for parent-children events evaluation). /// public int Depth; + + /// + /// The calling thread id. + /// + public int ThreadId; } private static int _depth; @@ -78,6 +84,7 @@ namespace Flax.Build e.StartTime = DateTime.Now; e.Duration = TimeSpan.Zero; e.Depth = _depth++; + e.ThreadId = Thread.CurrentThread.ManagedThreadId; _events.Add(e); return _events.Count - 1; } @@ -147,8 +154,8 @@ namespace Flax.Build for (int i = 0; i < _events.Count; i++) { var e = _events[i]; - - contents.Append($"{{ \"pid\":1, \"tid\":1, \"ts\":{(int)((e.StartTime - startTime).TotalMilliseconds * 1000.0)}, \"dur\":{(int)(e.Duration.TotalMilliseconds * 1000.0)}, \"ph\":\"X\", \"name\":\"{e.Name}\", \"args\":{{ \"startTime\":\"{e.StartTime.ToShortTimeString()}\" }} }}\n"); + + contents.Append($"{{ \"pid\":{e.ThreadId}, \"tid\":1, \"ts\":{(int)((e.StartTime - startTime).TotalMilliseconds * 1000.0)}, \"dur\":{(int)(e.Duration.TotalMilliseconds * 1000.0)}, \"ph\":\"X\", \"name\":\"{e.Name}\", \"args\":{{ \"startTime\":\"{e.StartTime.ToShortTimeString()}\" }} }}\n"); if (i + 1 != _events.Count) contents.Append(","); From 134aec2d157d8bbed60eb45a8430685f36d943dc Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 15 Feb 2021 20:52:35 +0100 Subject: [PATCH 402/419] Simplified name NetworkProtocol. --- Source/Engine/Platform/Base/NetworkBase.cpp | 2 +- Source/Engine/Platform/Base/NetworkBase.h | 4 ++-- Source/Engine/Platform/Win32/Win32Network.cpp | 12 ++++++------ Source/Engine/Platform/Win32/Win32Network.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.cpp b/Source/Engine/Platform/Base/NetworkBase.cpp index 06f297cc1..6ba5208f8 100644 --- a/Source/Engine/Platform/Base/NetworkBase.cpp +++ b/Source/Engine/Platform/Base/NetworkBase.cpp @@ -2,7 +2,7 @@ #include "NetworkBase.h" -bool NetworkBase::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv) +bool NetworkBase::CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv) { return true; } diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 6e6821ab4..90195c729 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -9,7 +9,7 @@ API_INJECT_CPP_CODE("#include \"Engine/Platform/Network.h\""); #define SOCKGROUP_MAXCOUNT 64 #define SOCKGROUP_ITEMSIZE 16 -enum class FLAXENGINE_API NetworkProtocolType +enum class FLAXENGINE_API NetworkProtocol { /// Not specified. Undefined, @@ -31,7 +31,7 @@ enum class FLAXENGINE_API NetworkIPVersion struct FLAXENGINE_API NetworkSocket { - NetworkProtocolType Protocol = NetworkProtocolType::Undefined; + NetworkProtocol Protocol = NetworkProtocol::Undefined; NetworkIPVersion IPVersion = NetworkIPVersion::Undefined; byte Data[8] = {}; }; diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index 2da68957b..cea22dacb 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -135,13 +135,13 @@ static void TranslateSockOptToNative(NetworkSocketOption option, int32* level, i } } -bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv) +bool Win32Network::CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv) { socket.Protocol = proto; socket.IPVersion = ipv; const uint8 family = socket.IPVersion == NetworkIPVersion::IPv6 ? AF_INET6 : AF_INET; - const uint8 stype = socket.Protocol == NetworkProtocolType::Tcp ? SOCK_STREAM : SOCK_DGRAM; - const uint8 prot = socket.Protocol == NetworkProtocolType::Tcp ? IPPROTO_TCP : IPPROTO_UDP; + const uint8 stype = socket.Protocol == NetworkProtocol::Tcp ? SOCK_STREAM : SOCK_DGRAM; + const uint8 prot = socket.Protocol == NetworkProtocol::Tcp ? IPPROTO_TCP : IPPROTO_UDP; SOCKET sock; if ((sock = ::socket(family, stype, prot)) == INVALID_SOCKET) @@ -259,7 +259,7 @@ bool Win32Network::Listen(NetworkSocket& socket, uint16 queueSize) bool Win32Network::Accept(NetworkSocket& serverSock, NetworkSocket& newSock, NetworkEndPoint& newEndPoint) { - if (serverSock.Protocol != NetworkProtocolType::Tcp) + if (serverSock.Protocol != NetworkProtocol::Tcp) { LOG(Warning, "Can't accept connection on UDP socket! Socket : {0}", *(SOCKET*)serverSock.Data); return true; @@ -372,7 +372,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, return -1; } uint32 size; - if (endPoint == nullptr && socket.Protocol == NetworkProtocolType::Tcp) + if (endPoint == nullptr && socket.Protocol == NetworkProtocol::Tcp) { if ((size = send(*(SOCKET*)socket.Data, (const char*)data, length, 0)) == SOCKET_ERROR) { @@ -380,7 +380,7 @@ int32 Win32Network::WriteSocket(NetworkSocket socket, byte* data, uint32 length, return -1; } } - else if (endPoint != nullptr && socket.Protocol == NetworkProtocolType::Udp) + else if (endPoint != nullptr && socket.Protocol == NetworkProtocol::Udp) { if ((size = sendto(*(SOCKET*)socket.Data, (const char*)data, length, 0, (const sockaddr*)endPoint->Data, GetAddrSizeFromEP(*endPoint))) == SOCKET_ERROR) { diff --git a/Source/Engine/Platform/Win32/Win32Network.h b/Source/Engine/Platform/Win32/Win32Network.h index b0e313056..7a1aaabbf 100644 --- a/Source/Engine/Platform/Win32/Win32Network.h +++ b/Source/Engine/Platform/Win32/Win32Network.h @@ -13,7 +13,7 @@ class FLAXENGINE_API Win32Network : public NetworkBase public: // [NetworkBase] - static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); + static bool CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv); static bool DestroySocket(NetworkSocket& socket); static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, bool value); static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value); From bc6281ee63a39842b9f7e0c89dda3a2357ed0d4a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 15 Feb 2021 20:53:07 +0100 Subject: [PATCH 403/419] Add MTU option. --- Source/Engine/Platform/Base/NetworkBase.h | 2 ++ Source/Engine/Platform/Win32/Win32Network.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 90195c729..0ae8a3dcc 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -76,6 +76,8 @@ enum class FLAXENGINE_API NetworkSocketOption NoDelay, /// Enables IPv6 communication only for TCP socket. IPv6Only, + /// Retrieve the current path MTU, the socket must be connected UDP/TCP. + Mtu }; struct FLAXENGINE_API NetworkSocketState diff --git a/Source/Engine/Platform/Win32/Win32Network.cpp b/Source/Engine/Platform/Win32/Win32Network.cpp index cea22dacb..38b99a25e 100644 --- a/Source/Engine/Platform/Win32/Win32Network.cpp +++ b/Source/Engine/Platform/Win32/Win32Network.cpp @@ -132,6 +132,7 @@ static void TranslateSockOptToNative(NetworkSocketOption option, int32* level, i SOCKOPT(NetworkSocketOption::Error, SOL_SOCKET, SO_ERROR) SOCKOPT(NetworkSocketOption::NoDelay, IPPROTO_TCP, TCP_NODELAY) SOCKOPT(NetworkSocketOption::IPv6Only, IPPROTO_IPV6, IPV6_V6ONLY) + SOCKOPT(NetworkSocketOption::Mtu, IPPROTO_IP , IP_MTU) } } From a2e13460291c4aa29115f7c9ee652f3024a9578a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 15 Feb 2021 20:53:53 +0100 Subject: [PATCH 404/419] Docs tweak. --- Source/Engine/Platform/Base/NetworkBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 0ae8a3dcc..7c0a1cbd0 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -74,7 +74,7 @@ enum class FLAXENGINE_API NetworkSocketOption Error, /// Enables the Nagle algorithm for TCP sockets. NoDelay, - /// Enables IPv6 communication only for TCP socket. + /// Enables IPv6/Ipv4 dual-stacking, UDP/TCP. IPv6Only, /// Retrieve the current path MTU, the socket must be connected UDP/TCP. Mtu From 26e3400a926d0b684e69ee4eb4d08d2fc238e418 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Mon, 15 Feb 2021 20:54:00 +0100 Subject: [PATCH 405/419] Typo. --- Source/Engine/Platform/Base/NetworkBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Base/NetworkBase.h b/Source/Engine/Platform/Base/NetworkBase.h index 7c0a1cbd0..e5ad03f98 100644 --- a/Source/Engine/Platform/Base/NetworkBase.h +++ b/Source/Engine/Platform/Base/NetworkBase.h @@ -105,7 +105,7 @@ public: /// The protocol. /// The ip version. /// Returns true on error, otherwise false. - static bool CreateSocket(NetworkSocket& socket, NetworkProtocolType proto, NetworkIPVersion ipv); + static bool CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv); /// /// Closes native socket. From 81be73ad8201c1127fc708ce140412a42bc9d685 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 22:08:37 +0100 Subject: [PATCH 406/419] Fix spline model not drawing when using invalid material --- Source/Engine/Level/Actors/SplineModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 01118ba6a..7fc623c8d 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -405,12 +405,12 @@ void SplineModel::Draw(RenderContext& renderContext) continue; // Select material - MaterialBase* material; + MaterialBase* material = nullptr; if (entry.Material && entry.Material->IsLoaded()) material = entry.Material; else if (slot.Material && slot.Material->IsLoaded()) material = slot.Material; - else + if (!material || !material->IsDeformable()) material = GPUDevice::Instance->GetDefaultDeformableMaterial(); if (!material || !material->IsDeformable()) continue; From 0841f5b479ad4f12b7f18ad2b3efd3628985e528 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 15 Feb 2021 22:09:05 +0100 Subject: [PATCH 407/419] Refactor Pre Rotation from spline model and spline collider into Pre Transform for more control --- Source/Engine/Level/Actors/SplineModel.cpp | 26 +++++++++---------- Source/Engine/Level/Actors/SplineModel.h | 10 +++---- .../Physics/Colliders/SplineCollider.cpp | 18 ++++++------- .../Engine/Physics/Colliders/SplineCollider.h | 10 +++---- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 7fc623c8d..84306716b 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -28,16 +28,16 @@ SplineModel::~SplineModel() Allocator::Free(_deformationBufferData); } -Quaternion SplineModel::GetPreRotation() const +Transform SplineModel::GetPreTransform() const { - return _preRotation; + return _preTransform; } -void SplineModel::SetPreRotation(const Quaternion& value) +void SplineModel::SetPreTransform(const Transform& value) { - if (_preRotation == value) + if (_preTransform == value) return; - _preRotation = value; + _preTransform = value; OnSplineUpdated(); } @@ -135,8 +135,8 @@ void SplineModel::OnSplineUpdated() for (int32 i = 0; i < 8; i++) { // Transform mesh corner using pre-transform but use double-precision to prevent issues when rotating model - Vector3 tmp = corners[i]; - double rotation[4] = { (double)_preRotation.X, (double)_preRotation.Y, (double)_preRotation.Z, (double)_preRotation.W }; + Vector3 tmp = corners[i] * _preTransform.Scale; + double rotation[4] = { (double)_preTransform.Orientation.X, (double)_preTransform.Orientation.Y, (double)_preTransform.Orientation.Z, (double)_preTransform.Orientation.W }; const double length = sqrt(rotation[0] * rotation[0] + rotation[1] * rotation[1] + rotation[2] * rotation[2] + rotation[3] * rotation[3]); const double inv = 1.0 / length; rotation[0] *= inv; @@ -157,9 +157,9 @@ void SplineModel::OnSplineUpdated() const double yz = rotation[1] * z; const double zz = rotation[2] * z; tmp = Vector3( - (float)(pos[0] * (1.0 - yy - zz) + pos[1] * (xy - wz) + pos[2] * (xz + wy)), - (float)(pos[0] * (xy + wz) + pos[1] * (1.0 - xx - zz) + pos[2] * (yz - wx)), - (float)(pos[0] * (xz - wy) + pos[1] * (yz + wx) + pos[2] * (1.0 - xx - yy))); + (float)(pos[0] * (1.0 - yy - zz) + pos[1] * (xy - wz) + pos[2] * (xz + wy)) + _preTransform.Translation.X, + (float)(pos[0] * (xy + wz) + pos[1] * (1.0 - xx - zz) + pos[2] * (yz - wx)) + _preTransform.Translation.Y, + (float)(pos[0] * (xz - wy) + pos[1] * (yz + wx) + pos[2] * (1.0 - xx - yy)) + _preTransform.Translation.Z); localModelBounds.Minimum = Vector3::Min(localModelBounds.Minimum, tmp); localModelBounds.Maximum = Vector3::Max(localModelBounds.Maximum, tmp); @@ -359,7 +359,7 @@ void SplineModel::Draw(RenderContext& renderContext) drawCall.Deformable.MeshMaxZ = _meshMaxZ; drawCall.Deformable.GeometrySize = _box.GetSize(); drawCall.PerInstanceRandom = GetPerInstanceRandom(); - Matrix::RotationQuaternion(_preRotation, drawCall.Deformable.LocalMatrix); + _preTransform.GetWorld(drawCall.Deformable.LocalMatrix); const Transform splineTransform = GetTransform(); splineTransform.GetWorld(drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation(); @@ -445,7 +445,7 @@ void SplineModel::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_MEMBER(BoundsScale, _boundsScale); SERIALIZE_MEMBER(LODBias, _lodBias); SERIALIZE_MEMBER(ForcedLOD, _forcedLod); - SERIALIZE_MEMBER(PreRotation, _preRotation) + SERIALIZE_MEMBER(PreTransform, _preTransform) SERIALIZE(Model); SERIALIZE(DrawModes); @@ -462,7 +462,7 @@ void SplineModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod DESERIALIZE_MEMBER(BoundsScale, _boundsScale); DESERIALIZE_MEMBER(LODBias, _lodBias); DESERIALIZE_MEMBER(ForcedLOD, _forcedLod); - DESERIALIZE_MEMBER(PreRotation, _preRotation); + DESERIALIZE_MEMBER(PreTransform, _preTransform); DESERIALIZE(Model); DESERIALIZE(DrawModes); diff --git a/Source/Engine/Level/Actors/SplineModel.h b/Source/Engine/Level/Actors/SplineModel.h index 9a2d5420b..6a0393cdc 100644 --- a/Source/Engine/Level/Actors/SplineModel.h +++ b/Source/Engine/Level/Actors/SplineModel.h @@ -26,7 +26,7 @@ private: char _forcedLod = -1; bool _deformationDirty = false; Array _instances; - Quaternion _preRotation = Quaternion::Identity; + Transform _preTransform = Transform::Identity; Spline* _spline = nullptr; GPUBuffer* _deformationBuffer = nullptr; void* _deformationBufferData = nullptr; @@ -43,15 +43,15 @@ public: AssetReference Model; /// - /// Gets the rotation applied to the model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// Gets the transformation applied to the model geometry before placing it over the spline. Can be used to change the way model goes over the spline. /// API_PROPERTY(Attributes="EditorOrder(21), EditorDisplay(\"Model\")") - Quaternion GetPreRotation() const; + Transform GetPreTransform() const; /// - /// Sets the rotation applied to the model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// Sets the transformation applied to the model geometry before placing it over the spline. Can be used to change the way model goes over the spline. /// - API_PROPERTY() void SetPreRotation(const Quaternion& value); + API_PROPERTY() void SetPreTransform(const Transform& value); /// /// The draw passes to use for rendering this object. diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index 8e7266755..f003971ae 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -22,16 +22,16 @@ SplineCollider::SplineCollider(const SpawnParams& params) CollisionData.Loaded.Bind(this); } -Quaternion SplineCollider::GetPreRotation() const +Transform SplineCollider::GetPreTransform() const { - return _preRotation; + return _preTransform; } -void SplineCollider::SetPreRotation(const Quaternion& value) +void SplineCollider::SetPreTransform(const Transform& value) { - if (_preRotation == value) + if (_preTransform == value) return; - _preRotation = value; + _preTransform = value; UpdateGeometry(); } @@ -130,7 +130,7 @@ void SplineCollider::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_GET_OTHER_OBJ(SplineCollider); SERIALIZE(CollisionData); - SERIALIZE_MEMBER(PreRotation, _preRotation) + SERIALIZE_MEMBER(PreTransform, _preTransform) } void SplineCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -139,7 +139,7 @@ void SplineCollider::Deserialize(DeserializeStream& stream, ISerializeModifier* Collider::Deserialize(stream, modifier); DESERIALIZE(CollisionData); - DESERIALIZE_MEMBER(PreRotation, _preRotation); + DESERIALIZE_MEMBER(PreTransform, _preTransform); } void SplineCollider::OnParentChanged() @@ -205,10 +205,10 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry) } // Apply local mesh transformation - if (!_preRotation.IsIdentity()) + if (!_preTransform.IsIdentity()) { for (int32 i = 0; i < collisionVertices.Count(); i++) - collisionVertices[i] = Vector3::Transform(collisionVertices[i], _preRotation); + collisionVertices[i] = _preTransform.LocalToWorld(collisionVertices[i]); } // Find collision geometry local bounds diff --git a/Source/Engine/Physics/Colliders/SplineCollider.h b/Source/Engine/Physics/Colliders/SplineCollider.h index 3bfcfacf1..42cc25e09 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.h +++ b/Source/Engine/Physics/Colliders/SplineCollider.h @@ -21,7 +21,7 @@ private: PxTriangleMesh* _triangleMesh = nullptr; Array _vertexBuffer; Array _indexBuffer; - Quaternion _preRotation = Quaternion::Identity; + Transform _preTransform = Transform::Identity; public: @@ -32,15 +32,15 @@ public: AssetReference CollisionData; /// - /// Gets the rotation applied to the collision data model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// Gets the transformation applied to the collision data model geometry before placing it over the spline. Can be used to change the way model goes over the spline. /// API_PROPERTY(Attributes="EditorOrder(101), EditorDisplay(\"Collider\")") - Quaternion GetPreRotation() const; + Transform GetPreTransform() const; /// - /// Sets the rotation applied to the collision data model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// Sets the transformation applied to the collision data model geometry before placing it over the spline. Can be used to change the way model goes over the spline. /// - API_PROPERTY() void SetPreRotation(const Quaternion& value); + API_PROPERTY() void SetPreTransform(const Transform& value); #if USE_EDITOR From f11686a7b70c228d46be3e19d457e4ab3951c8b5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 09:41:56 +0100 Subject: [PATCH 408/419] Add support for caching scripting API bindings during build --- .../Tools/Flax.Build/Bindings/ApiTypeInfo.cs | 34 ++- .../Bindings/BindingsGenerator.CSharp.cs | 9 +- .../Bindings/BindingsGenerator.Cache.cs | 266 ++++++++++++++++++ .../Bindings/BindingsGenerator.Cpp.cs | 6 +- .../Bindings/BindingsGenerator.Parsing.cs | 13 - .../Flax.Build/Bindings/BindingsGenerator.cs | 30 +- Source/Tools/Flax.Build/Bindings/ClassInfo.cs | 43 ++- .../Flax.Build/Bindings/ClassStructInfo.cs | 22 +- Source/Tools/Flax.Build/Bindings/EnumInfo.cs | 39 ++- Source/Tools/Flax.Build/Bindings/EventInfo.cs | 16 ++ Source/Tools/Flax.Build/Bindings/FieldInfo.cs | 24 ++ .../Tools/Flax.Build/Bindings/FunctionInfo.cs | 51 +++- .../Flax.Build/Bindings/InjectCppCodeInfo.cs | 16 ++ .../Tools/Flax.Build/Bindings/MemberInfo.cs | 22 +- .../Tools/Flax.Build/Bindings/ModuleInfo.cs | 24 ++ .../Tools/Flax.Build/Bindings/PropertyInfo.cs | 20 ++ .../Flax.Build/Bindings/StructureInfo.cs | 25 +- Source/Tools/Flax.Build/Bindings/TypeInfo.cs | 31 +- Source/Tools/Flax.Build/Flax.Build.csproj | 1 + .../Tools/Flax.Build/Utilities/Utilities.cs | 10 + 20 files changed, 663 insertions(+), 39 deletions(-) create mode 100644 Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs diff --git a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs index 814983e9b..3e2ad1b31 100644 --- a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs @@ -1,16 +1,17 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.IO; namespace Flax.Build.Bindings { /// /// The native type information for bindings generator. /// - public class ApiTypeInfo + public class ApiTypeInfo : IBindingsCache { public ApiTypeInfo Parent; - public List Children; + public List Children = new List(); public string NativeName; public string Name; public string Namespace; @@ -91,6 +92,35 @@ namespace Flax.Build.Bindings Children.Add(apiTypeInfo); } + public virtual void Write(BinaryWriter writer) + { + BindingsGenerator.Write(writer, NativeName); + BindingsGenerator.Write(writer, Name); + BindingsGenerator.Write(writer, Namespace); + BindingsGenerator.Write(writer, Attributes); + BindingsGenerator.Write(writer, Comment); + writer.Write(IsInBuild); + BindingsGenerator.Write(writer, Children); + } + + public virtual void Read(BinaryReader reader) + { + NativeName = BindingsGenerator.Read(reader, NativeName); + Name = BindingsGenerator.Read(reader, Name); + Namespace = BindingsGenerator.Read(reader, Namespace); + Attributes = BindingsGenerator.Read(reader, Attributes); + Comment = BindingsGenerator.Read(reader, Comment); + IsInBuild = reader.ReadBoolean(); + Children = BindingsGenerator.Read(reader, Children); + + // Fix hierarchy + for (int i = 0; i < Children.Count; i++) + { + var child = Children[i]; + child.Parent = this; + } + } + public override string ToString() { return Name; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index d9dc61521..2fd6a6edd 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -521,6 +521,7 @@ namespace Flax.Build.Bindings { if (!useUnmanaged) throw new NotImplementedException("TODO: support events inside non-static and non-scripting API class types."); + var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0; contents.AppendLine(); @@ -542,10 +543,10 @@ namespace Flax.Build.Bindings if (eventInfo.IsStatic) contents.Append("static "); string eventSignature = "event Action"; - if (eventInfo.Type.GenericArgs.Count != 0) + if (paramsCount != 0) { eventSignature += '<'; - for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++) + for (var i = 0; i < paramsCount; i++) { if (i != 0) eventSignature += ", "; @@ -583,7 +584,7 @@ namespace Flax.Build.Bindings if (eventInfo.IsStatic) contents.Append("static "); contents.Append($"void Internal_{eventInfo.Name}_Invoke("); - for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++) + for (var i = 0; i < paramsCount; i++) { if (i != 0) contents.Append(", "); @@ -594,7 +595,7 @@ namespace Flax.Build.Bindings contents.Append(indent).Append('{').AppendLine(); contents.Append(indent).Append(" Internal_").Append(eventInfo.Name); contents.Append('('); - for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++) + for (var i = 0; i < paramsCount; i++) { if (i != 0) contents.Append(", "); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs new file mode 100644 index 000000000..bc141759c --- /dev/null +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs @@ -0,0 +1,266 @@ +// (c) 2012-2020 Wojciech Figat. All rights reserved. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Flax.Build.NativeCpp; + +namespace Flax.Build.Bindings +{ + internal interface IBindingsCache + { + void Write(BinaryWriter writer); + void Read(BinaryReader reader); + } + + partial class BindingsGenerator + { + private static readonly Dictionary _typeCache = new Dictionary(); + + internal static void Write(BinaryWriter writer, string e) + { + var valid = e != null; + writer.Write(valid); + if (valid) + writer.Write(e); + } + + internal static void Write(BinaryWriter writer, string[] list) + { + if (list != null) + { + writer.Write(list.Length); + for (int i = 0; i < list.Length; i++) + writer.Write(list[i]); + } + else + { + writer.Write(0); + } + } + + internal static void Write(BinaryWriter writer, IEnumerable list) + { + if (list != null) + { + writer.Write(list.Count()); + foreach (var e in list) + writer.Write(e); + } + else + { + writer.Write(0); + } + } + + internal static void Write(BinaryWriter writer, T e) where T : IBindingsCache + { + if (e != null) + { + writer.Write(e.GetType().FullName); + e.Write(writer); + } + else + { + writer.Write(string.Empty); + } + } + + internal static void Write(BinaryWriter writer, IList list) where T : IBindingsCache + { + if (list != null) + { + var count = list.Count; + writer.Write(count); + for (int i = 0; i < count; i++) + { + var e = list[i]; + writer.Write(e.GetType().FullName); + e.Write(writer); + } + } + else + { + writer.Write(0); + } + } + + internal static string Read(BinaryReader reader, string e) + { + var valid = reader.ReadBoolean(); + if (valid) + e = reader.ReadString(); + return e; + } + + internal static string[] Read(BinaryReader reader, string[] list) + { + var count = reader.ReadInt32(); + if (count != 0) + { + list = new string[count]; + for (int i = 0; i < count; i++) + list[i] = reader.ReadString(); + } + return list; + } + + internal static T Read(BinaryReader reader, T e) where T : IBindingsCache + { + var typename = reader.ReadString(); + if (string.IsNullOrEmpty(typename)) + return e; + if (!_typeCache.TryGetValue(typename, out var type)) + { + type = Builder.BuildTypes.FirstOrDefault(x => x.FullName == typename); + if (type == null) + { + var msg = $"Missing type {typename}."; + Log.Error(msg); + throw new Exception(msg); + } + _typeCache.Add(typename, type); + } + e = (T)Activator.CreateInstance(type); + e.Read(reader); + return e; + } + + internal static List Read(BinaryReader reader, List list) where T : IBindingsCache + { + var count = reader.ReadInt32(); + if (count != 0) + { + list = new List(count); + for (int i = 0; i < count; i++) + { + var typename = reader.ReadString(); + if (!_typeCache.TryGetValue(typename, out var type)) + { + type = Builder.BuildTypes.FirstOrDefault(x => x.FullName == typename); + if (type == null) + { + var msg = $"Missing type {typename}."; + Log.Error(msg); + throw new Exception(msg); + } + _typeCache.Add(typename, type); + } + var e = (T)Activator.CreateInstance(type); + e.Read(reader); + list.Add(e); + } + } + return list; + } + + private static string GetCachePath(Module module, BuildOptions moduleOptions) + { + return Path.Combine(moduleOptions.IntermediateFolder, module.Name + ".Bindings.cache"); + } + + private static void SaveCache(ModuleInfo moduleInfo, BuildOptions moduleOptions, List headerFiles) + { + var path = GetCachePath(moduleInfo.Module, moduleOptions); + using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) + using (var writer = new BinaryWriter(stream, Encoding.UTF8)) + { + // Version + writer.Write(4); + writer.Write(File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks); + + // Build options + writer.Write(moduleOptions.IntermediateFolder); + writer.Write((int)moduleOptions.Platform.Target); + writer.Write((int)moduleOptions.Architecture); + writer.Write((int)moduleOptions.Configuration); + Write(writer, moduleOptions.PublicDefinitions); + Write(writer, moduleOptions.PrivateDefinitions); + Write(writer, moduleOptions.CompileEnv.PreprocessorDefinitions); + + // Header files + writer.Write(headerFiles.Count); + for (int i = 0; i < headerFiles.Count; i++) + { + var headerFile = headerFiles[i]; + writer.Write(headerFile); + writer.Write(File.GetLastWriteTime(headerFile).Ticks); + } + + // Info + moduleInfo.Write(writer); + } + } + + private static bool LoadCache(ref ModuleInfo moduleInfo, BuildOptions moduleOptions, List headerFiles) + { + var path = GetCachePath(moduleInfo.Module, moduleOptions); + if (!File.Exists(path)) + return false; + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var reader = new BinaryReader(stream, Encoding.UTF8)) + { + // Version + var version = reader.ReadInt32(); + if (version != 4) + return false; + if (File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks != reader.ReadInt64()) + return false; + + // Build options + if (reader.ReadString() != moduleOptions.IntermediateFolder || + reader.ReadInt32() != (int)moduleOptions.Platform.Target || + reader.ReadInt32() != (int)moduleOptions.Architecture || + reader.ReadInt32() != (int)moduleOptions.Configuration) + return false; + var publicDefinitions = Read(reader, Utilities.GetEmptyArray()); + if (publicDefinitions.Length != moduleOptions.PublicDefinitions.Count || publicDefinitions.Any(x => !moduleOptions.PublicDefinitions.Contains(x))) + return false; + var privateDefinitions = Read(reader, Utilities.GetEmptyArray()); + if (privateDefinitions.Length != moduleOptions.PrivateDefinitions.Count || privateDefinitions.Any(x => !moduleOptions.PrivateDefinitions.Contains(x))) + return false; + var preprocessorDefinitions = Read(reader, Utilities.GetEmptyArray()); + if (preprocessorDefinitions.Length != moduleOptions.CompileEnv.PreprocessorDefinitions.Count || preprocessorDefinitions.Any(x => !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(x))) + return false; + + // Header files + var headerFilesCount = reader.ReadInt32(); + if (headerFilesCount != headerFiles.Count) + return false; + for (int i = 0; i < headerFilesCount; i++) + { + var headerFile = headerFiles[i]; + if (headerFile != reader.ReadString()) + return false; + if (File.GetLastWriteTime(headerFile).Ticks > reader.ReadInt64()) + return false; + } + + // Info + var newModuleInfo = new ModuleInfo + { + Module = moduleInfo.Module, + Name = moduleInfo.Name, + Namespace = moduleInfo.Namespace, + Children = new List(), + }; + try + { + newModuleInfo.Read(reader); + + // Skip parsing and use data loaded from cache + moduleInfo = newModuleInfo; + return true; + } + catch + { + // Skip loading cache + return false; + } + } + } + } +} diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index ff2a2fadd..d62a53144 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1091,7 +1091,7 @@ namespace Flax.Build.Bindings { if (!useScripting) continue; - var paramsCount = eventInfo.Type.GenericArgs.Count; + var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0; // C# event invoking wrapper (calls C# event from C++ delegate) CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h"); @@ -1109,7 +1109,7 @@ namespace Flax.Build.Bindings contents.Append(" {").AppendLine(); contents.Append(" static MMethod* mmethod = nullptr;").AppendLine(); contents.Append(" if (!mmethod)").AppendLine(); - contents.AppendFormat(" mmethod = {1}::GetStaticClass()->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, eventInfo.Type.GenericArgs.Count).AppendLine(); + contents.AppendFormat(" mmethod = {1}::GetStaticClass()->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine(); contents.Append(" CHECK(mmethod);").AppendLine(); contents.Append(" MonoObject* exception = nullptr;").AppendLine(); if (paramsCount == 0) @@ -1174,7 +1174,7 @@ namespace Flax.Build.Bindings if (paramsCount == 0) contents.AppendLine(" Variant* params = nullptr;"); else - contents.AppendLine($" Variant params[{eventInfo.Type.GenericArgs.Count}];"); + contents.AppendLine($" Variant params[{paramsCount}];"); for (var i = 0; i < paramsCount; i++) { var paramType = eventInfo.Type.GenericArgs[i]; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 66f93fde3..7714e6abd 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -475,13 +475,8 @@ namespace Flax.Build.Bindings { var desc = new ClassInfo { - Children = new List(), Access = context.CurrentAccessLevel, BaseTypeInheritance = AccessLevel.Private, - Functions = new List(), - Properties = new List(), - Fields = new List(), - Events = new List(), }; // Read the documentation comment @@ -559,7 +554,6 @@ namespace Flax.Build.Bindings { var desc = new InterfaceInfo { - Children = new List(), Access = context.CurrentAccessLevel, }; @@ -622,7 +616,6 @@ namespace Flax.Build.Bindings var desc = new FunctionInfo { Access = context.CurrentAccessLevel, - Parameters = new List(), }; // Read the documentation comment @@ -821,9 +814,7 @@ namespace Flax.Build.Bindings { var desc = new EnumInfo { - Children = new List(), Access = context.CurrentAccessLevel, - Entries = new List(), }; // Read the documentation comment @@ -978,10 +969,7 @@ namespace Flax.Build.Bindings { var desc = new StructureInfo { - Children = new List(), Access = context.CurrentAccessLevel, - Fields = new List(), - Functions = new List(), }; // Read the documentation comment @@ -1200,7 +1188,6 @@ namespace Flax.Build.Bindings context.Tokenizer.ExpectToken(TokenType.LeftParent); var desc = new InjectCppCodeInfo { - Children = new List(), Code = context.Tokenizer.ExpectToken(TokenType.String).Value.Replace("\\\"", "\""), }; desc.Code = desc.Code.Substring(1, desc.Code.Length - 2); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index 1b12cb838..0135f1130 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -100,6 +100,26 @@ namespace Flax.Build.Bindings } } + // Sort file paths to have stable results + headerFiles.Sort(); + + // Load cache + using (new ProfileEventScope("LoadCache")) + { + if (LoadCache(ref moduleInfo, moduleOptions, headerFiles)) + { + buildData.ModulesInfo[module] = moduleInfo; + + // Initialize API + using (new ProfileEventScope("Init")) + { + moduleInfo.Init(buildData); + } + + return moduleInfo; + } + } + // Parse bindings Log.Verbose($"Parsing API bindings for {module.Name} ({moduleInfo.Name})"); int concurrency = Math.Min(Math.Max(1, (int)(Environment.ProcessorCount * Configuration.ConcurrencyProcessorScale)), Configuration.MaxConcurrency); @@ -117,9 +137,6 @@ namespace Flax.Build.Bindings } else { - // Sort by file size to improve performance (parse larger files first) - headerFiles.Sort((a, b) => new System.IO.FileInfo(b).Length.CompareTo(new System.IO.FileInfo(a).Length)); - // Multi-threaded ThreadPool.GetMinThreads(out var workerThreads, out var completionPortThreads); if (workerThreads != concurrency) @@ -133,6 +150,12 @@ namespace Flax.Build.Bindings }); } + // Save cache + using (new ProfileEventScope("SaveCache")) + { + SaveCache(moduleInfo, moduleOptions, headerFiles); + } + // Initialize API using (new ProfileEventScope("Init")) { @@ -162,7 +185,6 @@ namespace Flax.Build.Bindings var fileInfo = new FileInfo { Parent = null, - Children = new List(), Name = headerFiles[workIndex], Namespace = moduleInfo.Name, }; diff --git a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs index a83ac2b55..c68ff1a59 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.IO; using System.Linq; namespace Flax.Build.Bindings @@ -29,10 +30,10 @@ namespace Flax.Build.Bindings public bool IsAutoSerialization; public bool NoSpawn; public bool NoConstructor; - public List Functions; - public List Properties; - public List Fields; - public List Events; + public List Functions = new List(); + public List Properties = new List(); + public List Fields = new List(); + public List Events = new List(); internal HashSet UniqueFunctionNames; @@ -74,6 +75,40 @@ namespace Flax.Build.Bindings } } + public override void Write(BinaryWriter writer) + { + // TODO: convert into flags + writer.Write(IsStatic); + writer.Write(IsSealed); + writer.Write(IsAbstract); + writer.Write(IsAutoSerialization); + writer.Write(NoSpawn); + writer.Write(NoConstructor); + BindingsGenerator.Write(writer, Functions); + BindingsGenerator.Write(writer, Properties); + BindingsGenerator.Write(writer, Fields); + BindingsGenerator.Write(writer, Events); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + // TODO: convert into flags + IsStatic = reader.ReadBoolean(); + IsSealed = reader.ReadBoolean(); + IsAbstract = reader.ReadBoolean(); + IsAutoSerialization = reader.ReadBoolean(); + NoSpawn = reader.ReadBoolean(); + NoConstructor = reader.ReadBoolean(); + Functions = BindingsGenerator.Read(reader, Functions); + Properties = BindingsGenerator.Read(reader, Properties); + Fields = BindingsGenerator.Read(reader, Fields); + Events = BindingsGenerator.Read(reader, Events); + + base.Read(reader); + } + public int GetScriptVTableSize(Builder.BuildData buildData, out int offset) { if (_scriptVTableSize == -1) diff --git a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs index dcb2d2e9e..2f0dad003 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs @@ -1,7 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -using System; using System.Collections.Generic; +using System.IO; namespace Flax.Build.Bindings { @@ -36,5 +36,25 @@ namespace Flax.Build.Bindings Interfaces = null; } } + + public override void Write(BinaryWriter writer) + { + writer.Write((byte)Access); + writer.Write((byte)BaseTypeInheritance); + BindingsGenerator.Write(writer, BaseType); + BindingsGenerator.Write(writer, InterfaceNames); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + Access = (AccessLevel)reader.ReadByte(); + BaseTypeInheritance = (AccessLevel)reader.ReadByte(); + BaseType = BindingsGenerator.Read(reader, BaseType); + InterfaceNames = BindingsGenerator.Read(reader, InterfaceNames); + + base.Read(reader); + } } } diff --git a/Source/Tools/Flax.Build/Bindings/EnumInfo.cs b/Source/Tools/Flax.Build/Bindings/EnumInfo.cs index bf9fc8132..ddfcb3a38 100644 --- a/Source/Tools/Flax.Build/Bindings/EnumInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/EnumInfo.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.IO; namespace Flax.Build.Bindings { @@ -10,7 +11,7 @@ namespace Flax.Build.Bindings /// public class EnumInfo : ApiTypeInfo { - public struct EntryInfo + public struct EntryInfo : IBindingsCache { public string Name; public string[] Comment; @@ -21,11 +22,27 @@ namespace Flax.Build.Bindings { return Name + (string.IsNullOrEmpty(Value) ? string.Empty : " = " + Value); } + + public void Write(BinaryWriter writer) + { + writer.Write(Name); + BindingsGenerator.Write(writer, Comment); + BindingsGenerator.Write(writer, Value); + BindingsGenerator.Write(writer, Attributes); + } + + public void Read(BinaryReader reader) + { + Name = reader.ReadString(); + Comment = BindingsGenerator.Read(reader, Comment); + Value = BindingsGenerator.Read(reader, Value); + Attributes = BindingsGenerator.Read(reader, Attributes); + } } public AccessLevel Access; public TypeInfo UnderlyingType; - public List Entries; + public List Entries = new List(); public override bool IsValueType => true; public override bool IsEnum => true; @@ -36,6 +53,24 @@ namespace Flax.Build.Bindings throw new NotSupportedException("API enums cannot have sub-types."); } + public override void Write(BinaryWriter writer) + { + writer.Write((byte)Access); + BindingsGenerator.Write(writer, UnderlyingType); + BindingsGenerator.Write(writer, Entries); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + Access = (AccessLevel)reader.ReadByte(); + UnderlyingType = BindingsGenerator.Read(reader, UnderlyingType); + Entries = BindingsGenerator.Read(reader, Entries); + + base.Read(reader); + } + public override string ToString() { return "enum " + Name; diff --git a/Source/Tools/Flax.Build/Bindings/EventInfo.cs b/Source/Tools/Flax.Build/Bindings/EventInfo.cs index de93332ac..905bee803 100644 --- a/Source/Tools/Flax.Build/Bindings/EventInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/EventInfo.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.IO; + namespace Flax.Build.Bindings { /// @@ -9,6 +11,20 @@ namespace Flax.Build.Bindings { public TypeInfo Type; + public override void Write(BinaryWriter writer) + { + BindingsGenerator.Write(writer, Type); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + Type = BindingsGenerator.Read(reader, Type); + + base.Read(reader); + } + public override string ToString() { var result = string.Empty; diff --git a/Source/Tools/Flax.Build/Bindings/FieldInfo.cs b/Source/Tools/Flax.Build/Bindings/FieldInfo.cs index b1488ed09..d01cde5e7 100644 --- a/Source/Tools/Flax.Build/Bindings/FieldInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FieldInfo.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.IO; + namespace Flax.Build.Bindings { /// @@ -16,6 +18,28 @@ namespace Flax.Build.Bindings public bool HasDefaultValue => !string.IsNullOrEmpty(DefaultValue); + public override void Write(BinaryWriter writer) + { + BindingsGenerator.Write(writer, Type); + // TODO: convert into flags + writer.Write(IsReadOnly); + writer.Write(NoArray); + BindingsGenerator.Write(writer, DefaultValue); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + Type = BindingsGenerator.Read(reader, Type); + // TODO: convert into flags + IsReadOnly = reader.ReadBoolean(); + NoArray = reader.ReadBoolean(); + DefaultValue = BindingsGenerator.Read(reader, DefaultValue); + + base.Read(reader); + } + public override string ToString() { var result = string.Empty; diff --git a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs index 953fd7ed4..a78c9242a 100644 --- a/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FunctionInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.IO; namespace Flax.Build.Bindings { @@ -9,7 +10,7 @@ namespace Flax.Build.Bindings /// public class FunctionInfo : MemberInfo { - public struct ParameterInfo + public struct ParameterInfo : IBindingsCache { public string Name; public TypeInfo Type; @@ -25,6 +26,28 @@ namespace Flax.Build.Bindings return Attributes != null && Attributes.Contains(name); } + public void Write(BinaryWriter writer) + { + writer.Write(Name); + BindingsGenerator.Write(writer, Type); + BindingsGenerator.Write(writer, DefaultValue); + BindingsGenerator.Write(writer, Attributes); + // TODO: convert into flags + writer.Write(IsRef); + writer.Write(IsOut); + } + + public void Read(BinaryReader reader) + { + Name = reader.ReadString(); + Type = BindingsGenerator.Read(reader, Type); + DefaultValue = BindingsGenerator.Read(reader, DefaultValue); + Attributes = BindingsGenerator.Read(reader, Attributes); + // TODO: convert into flags + IsRef = reader.ReadBoolean(); + IsOut = reader.ReadBoolean(); + } + public override string ToString() { var result = Type + " " + Name; @@ -42,12 +65,36 @@ namespace Flax.Build.Bindings public string UniqueName; public TypeInfo ReturnType; - public List Parameters; + public List Parameters = new List(); public bool IsVirtual; public bool IsConst; public bool NoProxy; public GlueInfo Glue; + public override void Write(BinaryWriter writer) + { + BindingsGenerator.Write(writer, ReturnType); + BindingsGenerator.Write(writer, Parameters); + // TODO: convert into flags + writer.Write(IsVirtual); + writer.Write(IsConst); + writer.Write(NoProxy); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + ReturnType = BindingsGenerator.Read(reader, ReturnType); + Parameters = BindingsGenerator.Read(reader, Parameters); + // TODO: convert into flags + IsVirtual = reader.ReadBoolean(); + IsConst = reader.ReadBoolean(); + NoProxy = reader.ReadBoolean(); + + base.Read(reader); + } + public override string ToString() { var result = string.Empty; diff --git a/Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs b/Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs index dfdb551f6..0bb53fc99 100644 --- a/Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/InjectCppCodeInfo.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.IO; + namespace Flax.Build.Bindings { /// @@ -9,6 +11,20 @@ namespace Flax.Build.Bindings { public string Code; + public override void Write(BinaryWriter writer) + { + writer.Write(Code); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + Code = reader.ReadString(); + + base.Read(reader); + } + /// public override string ToString() { diff --git a/Source/Tools/Flax.Build/Bindings/MemberInfo.cs b/Source/Tools/Flax.Build/Bindings/MemberInfo.cs index 3e09b9f0b..49187c101 100644 --- a/Source/Tools/Flax.Build/Bindings/MemberInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/MemberInfo.cs @@ -1,11 +1,13 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.IO; + namespace Flax.Build.Bindings { /// /// The native member information for bindings generator. /// - public class MemberInfo + public class MemberInfo : IBindingsCache { public string Name; public string[] Comment; @@ -17,5 +19,23 @@ namespace Flax.Build.Bindings { return Attributes != null && Attributes.Contains(name); } + + public virtual void Write(BinaryWriter writer) + { + writer.Write(Name); + BindingsGenerator.Write(writer, Comment); + writer.Write(IsStatic); + writer.Write((byte)Access); + BindingsGenerator.Write(writer, Attributes); + } + + public virtual void Read(BinaryReader reader) + { + Name = reader.ReadString(); + Comment = BindingsGenerator.Read(reader, Comment); + IsStatic = reader.ReadBoolean(); + Access = (AccessLevel)reader.ReadByte(); + Attributes = BindingsGenerator.Read(reader, Attributes); + } } } diff --git a/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs b/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs index 7bb6d1608..dd83af9d9 100644 --- a/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs @@ -1,5 +1,8 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; +using System.IO; + namespace Flax.Build.Bindings { /// @@ -22,5 +25,26 @@ namespace Flax.Build.Bindings // Sort module files to prevent bindings rebuild due to order changes (list might be created in async) Children.Sort(); } + + public override void Write(BinaryWriter writer) + { + writer.Write(Module.Name); + writer.Write(Module.FilePath); + BindingsGenerator.Write(writer, Module.BinaryModuleName); + writer.Write(Module.BuildNativeCode); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + if (reader.ReadString() != Module.Name || + reader.ReadString() != Module.FilePath || + BindingsGenerator.Read(reader, Module.BinaryModuleName) != Module.BinaryModuleName || + reader.ReadBoolean() != Module.BuildNativeCode) + throw new Exception(); + + base.Read(reader); + } } } diff --git a/Source/Tools/Flax.Build/Bindings/PropertyInfo.cs b/Source/Tools/Flax.Build/Bindings/PropertyInfo.cs index f53578b4e..96b0f8b93 100644 --- a/Source/Tools/Flax.Build/Bindings/PropertyInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/PropertyInfo.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.IO; + namespace Flax.Build.Bindings { /// @@ -11,6 +13,24 @@ namespace Flax.Build.Bindings public FunctionInfo Getter; public FunctionInfo Setter; + public override void Write(BinaryWriter writer) + { + BindingsGenerator.Write(writer, Type); + BindingsGenerator.Write(writer, Getter); + BindingsGenerator.Write(writer, Setter); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + Type = BindingsGenerator.Read(reader, Type); + Getter = BindingsGenerator.Read(reader, Getter); + Setter = BindingsGenerator.Read(reader, Setter); + + base.Read(reader); + } + public override string ToString() { return Type + " " + Name; diff --git a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs index be24bb1f0..882ed6295 100644 --- a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.IO; namespace Flax.Build.Bindings { @@ -10,8 +11,8 @@ namespace Flax.Build.Bindings /// public class StructureInfo : ClassStructInfo { - public List Fields; - public List Functions; + public List Fields = new List(); + public List Functions = new List(); public bool IsAutoSerialization; public bool ForceNoPod; @@ -43,6 +44,26 @@ namespace Flax.Build.Bindings } } + public override void Write(BinaryWriter writer) + { + BindingsGenerator.Write(writer, Fields); + BindingsGenerator.Write(writer, Functions); + writer.Write(IsAutoSerialization); + writer.Write(ForceNoPod); + + base.Write(writer); + } + + public override void Read(BinaryReader reader) + { + Fields = BindingsGenerator.Read(reader, Fields); + Functions = BindingsGenerator.Read(reader, Functions); + IsAutoSerialization = reader.ReadBoolean(); + ForceNoPod = reader.ReadBoolean(); + + base.Read(reader); + } + public override void AddChild(ApiTypeInfo apiTypeInfo) { if (apiTypeInfo is EnumInfo) diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index f2d7ff387..2d15f6651 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; @@ -10,7 +11,7 @@ namespace Flax.Build.Bindings /// /// The native type information for bindings generator. /// - public class TypeInfo : IEquatable + public class TypeInfo : IEquatable, IBindingsCache { public string Type; public bool IsConst; @@ -51,6 +52,34 @@ namespace Flax.Build.Bindings return true; } + public void Write(BinaryWriter writer) + { + BindingsGenerator.Write(writer, Type); + // TODO: pack as flags + writer.Write(IsConst); + writer.Write(IsRef); + writer.Write(IsPtr); + writer.Write(IsArray); + writer.Write(IsBitField); + writer.Write(ArraySize); + writer.Write(BitSize); + BindingsGenerator.Write(writer, GenericArgs); + } + + public void Read(BinaryReader reader) + { + Type = BindingsGenerator.Read(reader, Type); + // TODO: convert into flags + IsConst = reader.ReadBoolean(); + IsRef = reader.ReadBoolean(); + IsPtr = reader.ReadBoolean(); + IsArray = reader.ReadBoolean(); + IsBitField = reader.ReadBoolean(); + ArraySize = reader.ReadInt32(); + BitSize = reader.ReadInt32(); + GenericArgs = BindingsGenerator.Read(reader, GenericArgs); + } + public override string ToString() { var sb = new StringBuilder(64); diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index 9d415beed..236348032 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -64,6 +64,7 @@ + diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index c37faa678..accf86209 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -14,6 +14,16 @@ namespace Flax.Build /// public static class Utilities { + /// + /// Gets the empty array of the given type (shared one). + /// + /// The type. + /// The empty array object. + public static T[] GetEmptyArray() + { + return Enumerable.Empty() as T[]; + } + /// /// Gets the size of the file as a readable string. /// From fa332a3785b419c3510fe1bb2687eba97f3d0f53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 10:42:56 +0100 Subject: [PATCH 409/419] Optimize scripting API bindings generation if loaded API from valid cache --- .../Bindings/BindingsGenerator.CSharp.cs | 9 --- .../Bindings/BindingsGenerator.Cache.cs | 2 +- .../Bindings/BindingsGenerator.Cpp.cs | 17 +---- .../Flax.Build/Bindings/BindingsGenerator.cs | 73 +++++++++++++++---- .../Tools/Flax.Build/Bindings/ModuleInfo.cs | 1 + 5 files changed, 63 insertions(+), 39 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 2fd6a6edd..d09d21bbd 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1028,9 +1028,6 @@ namespace Flax.Build.Bindings private static void GenerateCSharp(BuildData buildData, ModuleInfo moduleInfo, ref BindingsResult bindings) { - if (!bindings.UseBindings) - return; - var contents = new StringBuilder(); buildData.Modules.TryGetValue(moduleInfo.Module, out var moduleBuildInfo); @@ -1081,12 +1078,6 @@ namespace Flax.Build.Bindings // Save generated file contents.AppendLine("#endif"); Utilities.WriteFileIfChanged(bindings.GeneratedCSharpFilePath, contents.ToString()); - - // Ensure that generated file is included into build - if (!moduleBuildInfo.SourceFiles.Contains(bindings.GeneratedCSharpFilePath)) - { - moduleBuildInfo.SourceFiles.Add(bindings.GeneratedCSharpFilePath); - } } internal struct GuidInterop diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs index bc141759c..bd3cd2c2e 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs @@ -245,7 +245,7 @@ namespace Flax.Build.Bindings Module = moduleInfo.Module, Name = moduleInfo.Name, Namespace = moduleInfo.Namespace, - Children = new List(), + IsFromCache = true, }; try { diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index d62a53144..58f1edaae 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1656,10 +1656,10 @@ namespace Flax.Build.Bindings } } - private static bool GenerateCppType(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, object type) + private static void GenerateCppType(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, object type) { if (type is ApiTypeInfo apiTypeInfo && apiTypeInfo.IsInBuild) - return false; + return; try { @@ -1671,16 +1671,12 @@ namespace Flax.Build.Bindings GenerateCppInterface(buildData, contents, moduleInfo, interfaceInfo); else if (type is InjectCppCodeInfo injectCppCodeInfo) contents.AppendLine(injectCppCodeInfo.Code); - else - return false; } catch { Log.Error($"Failed to generate C++ bindings for {type}."); throw; } - - return true; } private static void GenerateCppCppUsedNonPodTypes(BuildData buildData, ApiTypeInfo apiType) @@ -1728,21 +1724,16 @@ namespace Flax.Build.Bindings CppReferencesFiles.Add(fileInfo); } } - var headerPos = contents.Length; foreach (var child in moduleInfo.Children) { foreach (var apiTypeInfo in child.Children) { - if (GenerateCppType(buildData, contents, moduleInfo, apiTypeInfo)) - bindings.UseBindings = true; + GenerateCppType(buildData, contents, moduleInfo, apiTypeInfo); } } - if (!bindings.UseBindings) - return; - GenerateCppModuleSource?.Invoke(buildData, moduleInfo, contents); { @@ -2036,7 +2027,7 @@ namespace Flax.Build.Bindings var binaryModuleSourcePath = Path.Combine(project.ProjectFolderPath, "Source", binaryModuleName + ".Gen.cpp"); contents.AppendLine("// This code was auto-generated. Do not modify it."); contents.AppendLine(); - contents.AppendLine($"#include \"Engine/Scripting/BinaryModule.h\""); + contents.AppendLine("#include \"Engine/Scripting/BinaryModule.h\""); contents.AppendLine($"#include \"{binaryModuleName}.Gen.h\""); contents.AppendLine(); contents.AppendLine($"StaticallyLinkedBinaryModuleInitializer StaticallyLinkedBinaryModule{binaryModuleName}(GetBinaryModule{binaryModuleName});"); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index 0135f1130..a7e1508eb 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Flax.Build.NativeCpp; @@ -65,7 +66,6 @@ namespace Flax.Build.Bindings Module = module, Name = module.BinaryModuleName, Namespace = string.Empty, - Children = new List(), }; if (string.IsNullOrEmpty(moduleInfo.Name)) throw new Exception("Module name cannot be empty."); @@ -479,41 +479,82 @@ namespace Flax.Build.Bindings throw new Exception($"Invalid #if/endif pairing in file '{fileInfo.Name}'. Failed to generate API bindings for it."); } + private static bool UseBindings(object type) + { + var apiTypeInfo = type as ApiTypeInfo; + if (apiTypeInfo != null && apiTypeInfo.IsInBuild) + return false; + if ((type is ModuleInfo || type is FileInfo) && apiTypeInfo != null) + { + foreach (var child in apiTypeInfo.Children) + { + if (UseBindings(child)) + return true; + } + } + return type is ClassInfo || + type is StructureInfo || + type is InterfaceInfo || + type is InjectCppCodeInfo; + } + /// /// The API bindings generation utility that can produce scripting bindings for another languages to the native code. /// public static void GenerateBindings(BuildData buildData, Module module, ref BuildOptions moduleOptions, out BindingsResult bindings) { // Parse module (or load from cache) + var moduleInfo = ParseModule(buildData, module, moduleOptions); bindings = new BindingsResult { + UseBindings = UseBindings(moduleInfo), GeneratedCppFilePath = Path.Combine(moduleOptions.IntermediateFolder, module.Name + ".Bindings.Gen.cpp"), GeneratedCSharpFilePath = Path.Combine(moduleOptions.IntermediateFolder, module.Name + ".Bindings.Gen.cs"), }; - var moduleInfo = ParseModule(buildData, module, moduleOptions); + + if (bindings.UseBindings) + { + buildData.Modules.TryGetValue(moduleInfo.Module, out var moduleBuildInfo); + + // Ensure that generated files are included into build + if (!moduleBuildInfo.SourceFiles.Contains(bindings.GeneratedCSharpFilePath)) + moduleBuildInfo.SourceFiles.Add(bindings.GeneratedCSharpFilePath); + } + + // Skip if module is cached (no scripting API changed) + if (moduleInfo.IsFromCache) + return; // Process parsed API - foreach (var child in moduleInfo.Children) + using (new ProfileEventScope("Process")) { - try + foreach (var child in moduleInfo.Children) { - foreach (var apiTypeInfo in child.Children) - ProcessAndValidate(buildData, apiTypeInfo); - } - catch (Exception) - { - if (child is FileInfo fileInfo) - Log.Error($"Failed to validate '{fileInfo.Name}' file to generate bindings."); - throw; + try + { + foreach (var apiTypeInfo in child.Children) + ProcessAndValidate(buildData, apiTypeInfo); + } + catch (Exception) + { + if (child is FileInfo fileInfo) + Log.Error($"Failed to validate '{fileInfo.Name}' file to generate bindings."); + throw; + } } } // Generate bindings for scripting - Log.Verbose($"Generating API bindings for {module.Name} ({moduleInfo.Name})"); - GenerateCpp(buildData, moduleInfo, ref bindings); - GenerateCSharp(buildData, moduleInfo, ref bindings); + if (bindings.UseBindings) + { + Log.Verbose($"Generating API bindings for {module.Name} ({moduleInfo.Name})"); + using (new ProfileEventScope("Cpp")) + GenerateCpp(buildData, moduleInfo, ref bindings); + using (new ProfileEventScope("CSharp")) + GenerateCSharp(buildData, moduleInfo, ref bindings); - // TODO: add support for extending this code and support generating bindings for other scripting languages + // TODO: add support for extending this code and support generating bindings for other scripting languages + } } /// diff --git a/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs b/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs index dd83af9d9..68742b554 100644 --- a/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ModuleInfo.cs @@ -11,6 +11,7 @@ namespace Flax.Build.Bindings public class ModuleInfo : ApiTypeInfo { public Module Module; + public bool IsFromCache; public override string ToString() { From 7221f7167fa00386a505470b22d3143e18733b6f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 10:59:17 +0100 Subject: [PATCH 410/419] Add profiler events to LocalExecutor thread workers --- Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs b/Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs index 9b4a571f1..391b6952f 100644 --- a/Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs +++ b/Source/Tools/Flax.Build/Build/Graph/LocalExecutor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Threading; using Flax.Build.Graph; @@ -178,7 +179,12 @@ namespace Flax.Build.BuildSystem.Graph private int ExecuteTask(Task task) { - ProcessStartInfo startInfo = new ProcessStartInfo + string name = "Task"; + if (task.ProducedFiles != null && task.ProducedFiles.Count != 0) + name = Path.GetFileName(task.ProducedFiles[0]); + var profilerEvent = Profiling.Begin(name); + + var startInfo = new ProcessStartInfo { WorkingDirectory = task.WorkingDirectory, FileName = task.CommandPath, @@ -230,10 +236,13 @@ namespace Flax.Build.BuildSystem.Graph // Hang until process end process.WaitForExit(); + Profiling.End(profilerEvent); return process.ExitCode; } finally { + Profiling.End(profilerEvent); + // Ensure to cleanup data process?.Close(); } From 537dde594df682a6a41f364c56ede3a73e149773 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 11:00:08 +0100 Subject: [PATCH 411/419] Update Actor docs for GetScripts to be more precise about search method --- Source/Engine/Level/Actor.cs | 6 +++--- Source/Engine/Level/Actor.h | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Level/Actor.cs b/Source/Engine/Level/Actor.cs index cfb454354..2d9c27b9a 100644 --- a/Source/Engine/Level/Actor.cs +++ b/Source/Engine/Level/Actor.cs @@ -203,7 +203,7 @@ namespace FlaxEngine } /// - /// Finds the script of the given type. + /// Finds the script of the given type from this actor. /// /// Type of the script to search for. Includes any scripts derived from the type. /// The script or null if failed to find. @@ -213,7 +213,7 @@ namespace FlaxEngine } /// - /// Tries to find the script of the given type. + /// Tries to find the script of the given type from this actor. /// /// Type of the script to search for. Includes any scripts derived from the type. /// The returned script, valid only if method returns true. @@ -240,7 +240,7 @@ namespace FlaxEngine } /// - /// Searches for all scripts of a specific type. + /// Searches for all scripts of a specific type from this actor. /// /// Type of the scripts to search for. Includes any scripts derived from the type. /// All scripts matching the specified type. diff --git a/Source/Engine/Level/Actor.h b/Source/Engine/Level/Actor.h index dea13cd4f..04377fd4f 100644 --- a/Source/Engine/Level/Actor.h +++ b/Source/Engine/Level/Actor.h @@ -272,14 +272,14 @@ public: } /// - /// Gets the script of the given type. + /// Gets the script of the given type from this actor. /// /// Type of the script to search for. Includes any scripts derived from the type. /// The script or null. API_FUNCTION() Script* GetScript(const MClass* type) const; /// - /// Gets the script of the given type. + /// Gets the script of the given type from this actor. /// /// The script or null. template @@ -289,14 +289,14 @@ public: } /// - /// Gets the scripts of the given type. + /// Gets the scripts of the given type from this actor. /// /// Type of the script to search for. Includes any scripts derived from the type. /// The scripts. API_FUNCTION() Array GetScripts(const MClass* type) const; /// - /// Gets the scripts of the given type. + /// Gets the scripts of the given type from this actor. /// /// The scripts. template From 3658611e0fdd8d5731ece4ceb99fbb4782b119d8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 11:23:47 +0100 Subject: [PATCH 412/419] Fix compilation --- .../Engine/Physics/Colliders/SplineCollider.cpp | 17 ++--------------- .../Engine/Physics/Colliders/SplineCollider.h | 4 ---- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index f003971ae..e44d986cc 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -35,16 +35,12 @@ void SplineCollider::SetPreTransform(const Transform& value) UpdateGeometry(); } -#if USE_EDITOR - void SplineCollider::ExtractGeometry(Array& vertexBuffer, Array& indexBuffer) const { vertexBuffer.Add(_vertexBuffer); indexBuffer.Add(_indexBuffer); } -#endif - void SplineCollider::OnCollisionDataChanged() { // This should not be called during physics simulation, if it happened use write lock on physx scene @@ -316,11 +312,9 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry) return; } -#if USE_EDITOR - // Transform vertices back to world space for debug shapes drawing + // Transform vertices back to world space for debug shapes drawing and navmesh building for (int32 i = 0; i < _vertexBuffer.Count(); i++) _vertexBuffer[i] = colliderTransform.LocalToWorld(_vertexBuffer[i]); -#endif // Update bounds _box = P2C(_triangleMesh->getLocalBounds()); @@ -335,14 +329,7 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry) triangleMesh.triangleMesh = _triangleMesh; geometry.storeAny(triangleMesh); -#if !USE_EDITOR - // Free memory for static splines (if editor collision preview is not needed) - if (IsTransformStatic()) - { - _vertexBuffer.Resize(0); - _indexBuffer.Resize(0); - } -#endif + // TODO: find a way of releasing _vertexBuffer and _indexBuffer for static colliders (note: ExtractGeometry usage for navmesh generation at runtime) return; } diff --git a/Source/Engine/Physics/Colliders/SplineCollider.h b/Source/Engine/Physics/Colliders/SplineCollider.h index 42cc25e09..c1cdab7b7 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.h +++ b/Source/Engine/Physics/Colliders/SplineCollider.h @@ -42,8 +42,6 @@ public: /// API_PROPERTY() void SetPreTransform(const Transform& value); -#if USE_EDITOR - /// /// Extracts the collision data geometry into list of triangles. /// @@ -51,8 +49,6 @@ public: /// The output index buffer. void ExtractGeometry(Array& vertexBuffer, Array& indexBuffer) const; -#endif - private: void OnCollisionDataChanged(); From a98f867bd8cbae93b66e22b2abb95077f0560650 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 11:47:39 +0100 Subject: [PATCH 413/419] Fix compiler warning --- Source/Engine/Platform/Windows/WindowsWindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsWindow.cpp b/Source/Engine/Platform/Windows/WindowsWindow.cpp index 61c8737b8..2d60cc40a 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.cpp @@ -426,8 +426,8 @@ void WindowsWindow::GetScreenInfo(int32& x, int32& y, int32& width, int32& heigh // Calculate result x = monitorInfo.rcMonitor.left; y = monitorInfo.rcMonitor.top; - width = monitorInfo.rcMonitor.right - x; - height = monitorInfo.rcMonitor.bottom - y; + width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; + height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top; } float WindowsWindow::GetOpacity() const @@ -481,7 +481,7 @@ void WindowsWindow::StartTrackingMouse(bool useMouseScreenOffset) int32 x = 0 , y = 0, width = 0, height = 0; GetScreenInfo(x, y, width, height); - _mouseOffsetScreenSize = Rectangle(x, y, width, height); + _mouseOffsetScreenSize = Rectangle((float)x, (float)y, (float)width, (float)height); SetCapture(_handle); } From b742d0c326a23dfe3df1624251ec78c2748634f2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 13:09:50 +0100 Subject: [PATCH 414/419] Add support for custom defines for build system via `Flax.Build` command line --- Source/Tools/Flax.Build/Configuration.cs | 7 +++++++ Source/Tools/Flax.Build/Program.cs | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/Source/Tools/Flax.Build/Configuration.cs b/Source/Tools/Flax.Build/Configuration.cs index baa4e30b1..28970f289 100644 --- a/Source/Tools/Flax.Build/Configuration.cs +++ b/Source/Tools/Flax.Build/Configuration.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.Collections.Generic; + namespace Flax.Build { /// @@ -198,5 +200,10 @@ namespace Flax.Build /// [CommandLine("customProjectFormat", "", "Generates code project files for a custom project format type. Valid only with -genproject option.")] public static string ProjectFormatCustom = null; + + /// + /// Custom configuration defines provided via command line for the build tool. + /// + public static List CustomDefines = new List(); } } diff --git a/Source/Tools/Flax.Build/Program.cs b/Source/Tools/Flax.Build/Program.cs index fc6d86b3b..89e71ace0 100644 --- a/Source/Tools/Flax.Build/Program.cs +++ b/Source/Tools/Flax.Build/Program.cs @@ -32,6 +32,16 @@ namespace Flax.Build { // Setup CommandLine.Configure(typeof(Configuration)); + foreach (var option in CommandLine.GetOptions()) + { + if (option.Name.Length > 1 && option.Name[0] == 'D') + { + var define = option.Name.Substring(1); + if (!string.IsNullOrEmpty(option.Value)) + define += "=" + option.Value; + Configuration.CustomDefines.Add(define); + } + } if (Configuration.CurrentDirectory != null) Environment.CurrentDirectory = Configuration.CurrentDirectory; Globals.Root = Directory.GetCurrentDirectory(); From f66b7ff785d69633e4d1abaf97215d3c8aaf704f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 13:54:57 +0100 Subject: [PATCH 415/419] Add support for Custom Define in Game Cooker for build scripts configuration --- Source/Editor/Content/Settings/BuildSettings.cs | 16 +--------------- Source/Editor/Content/Settings/BuildTarget.cs | 12 ++++++------ Source/Editor/Cooker/CookingData.h | 5 +++++ Source/Editor/Cooker/GameCooker.cpp | 3 ++- Source/Editor/Cooker/GameCooker.h | 3 ++- .../Editor/Cooker/Steps/CompileScriptsStep.cpp | 5 +++++ Source/Editor/Windows/GameCookerWindow.cs | 7 +++++-- 7 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Source/Editor/Content/Settings/BuildSettings.cs b/Source/Editor/Content/Settings/BuildSettings.cs index 87530f1fb..5dc6310c3 100644 --- a/Source/Editor/Content/Settings/BuildSettings.cs +++ b/Source/Editor/Content/Settings/BuildSettings.cs @@ -25,13 +25,6 @@ namespace FlaxEditor.Content.Settings Platform = BuildPlatform.Windows64, Mode = BuildConfiguration.Development, }, - new BuildTarget - { - Name = "Windows 32bit", - Output = "Output\\Win32", - Platform = BuildPlatform.Windows32, - Mode = BuildConfiguration.Development, - }, } }, new BuildPreset @@ -44,14 +37,7 @@ namespace FlaxEditor.Content.Settings Name = "Windows 64bit", Output = "Output\\Win64", Platform = BuildPlatform.Windows64, - Mode = BuildConfiguration.Development, - }, - new BuildTarget - { - Name = "Windows 32bit", - Output = "Output\\Win32", - Platform = BuildPlatform.Windows32, - Mode = BuildConfiguration.Development, + Mode = BuildConfiguration.Release, }, } }, diff --git a/Source/Editor/Content/Settings/BuildTarget.cs b/Source/Editor/Content/Settings/BuildTarget.cs index 7cdc500f6..97c0b8c3d 100644 --- a/Source/Editor/Content/Settings/BuildTarget.cs +++ b/Source/Editor/Content/Settings/BuildTarget.cs @@ -35,6 +35,12 @@ namespace FlaxEditor.Content.Settings [EditorOrder(30), Tooltip("Configuration build mode")] public BuildConfiguration Mode; + /// + /// The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines). + /// + [EditorOrder(90), Tooltip("The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).")] + public string[] CustomDefines; + /// /// The pre-build action command line. /// @@ -46,11 +52,5 @@ namespace FlaxEditor.Content.Settings /// [EditorOrder(110)] public string PostBuildAction; - - /// - /// Gets the build options computed from the target configuration. - /// - [HideInEditor, NoSerialize] - public virtual BuildOptions Options => BuildOptions.None; } } diff --git a/Source/Editor/Cooker/CookingData.h b/Source/Editor/Cooker/CookingData.h index b46b8ff72..56b15af23 100644 --- a/Source/Editor/Cooker/CookingData.h +++ b/Source/Editor/Cooker/CookingData.h @@ -174,6 +174,11 @@ struct FLAXENGINE_API CookingData /// BuildOptions Options; + /// + /// The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines). + /// + Array CustomDefines; + /// /// The original output path (actual OutputPath could be modified by the Platform Tools or a plugin for additional layout customizations or packaging). This path is preserved. /// diff --git a/Source/Editor/Cooker/GameCooker.cpp b/Source/Editor/Cooker/GameCooker.cpp index d68ca587a..efd6763d8 100644 --- a/Source/Editor/Cooker/GameCooker.cpp +++ b/Source/Editor/Cooker/GameCooker.cpp @@ -257,7 +257,7 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform) return result; } -void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options) +void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options, const Array& customDefines) { if (IsRunning()) { @@ -281,6 +281,7 @@ void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration, data.Platform = platform; data.Configuration = configuration; data.Options = options; + data.CustomDefines = customDefines; data.OutputPath = outputPath; FileSystem::NormalizePath(data.OutputPath); data.OutputPath = data.OriginalOutputPath = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, data.OutputPath); diff --git a/Source/Editor/Cooker/GameCooker.h b/Source/Editor/Cooker/GameCooker.h index 3defe51ff..f1bfc4ad3 100644 --- a/Source/Editor/Cooker/GameCooker.h +++ b/Source/Editor/Cooker/GameCooker.h @@ -84,7 +84,8 @@ public: /// The build configuration. /// The output path (output directory). /// The build options. - API_FUNCTION() static void Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options); + /// The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines). + API_FUNCTION() static void Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options, const Array& customDefines); /// /// Sends a cancel event to the game building service. diff --git a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp index 3bb2e1eb6..dd7c77a94 100644 --- a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp +++ b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp @@ -217,6 +217,11 @@ bool CompileScriptsStep::Perform(CookingData& data) args += TEXT(" -SkipTargets=FlaxGame"); } #endif + for (auto& define : data.CustomDefines) + { + args += TEXT(" -D"); + args += define; + } if (ScriptsBuilder::RunBuildTool(args)) { data.Error(TEXT("Failed to compile game scripts.")); diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index 442cc4c34..278ce69d3 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -73,6 +73,9 @@ namespace FlaxEditor.Windows [EditorOrder(20), Tooltip("Configuration build mode")] public BuildConfiguration ConfigurationMode = BuildConfiguration.Development; + [EditorOrder(90), Tooltip("The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).")] + public string[] CustomDefines; + protected abstract BuildPlatform BuildPlatform { get; } protected virtual BuildOptions Options @@ -126,7 +129,7 @@ namespace FlaxEditor.Windows public virtual void Build() { var output = StringUtils.ConvertRelativePathToAbsolute(Globals.ProjectFolder, StringUtils.NormalizePath(Output)); - GameCooker.Build(BuildPlatform, ConfigurationMode, output, Options); + GameCooker.Build(BuildPlatform, ConfigurationMode, output, Options, CustomDefines); } } @@ -793,7 +796,7 @@ namespace FlaxEditor.Windows _preBuildAction = target.PreBuildAction; _postBuildAction = target.PostBuildAction; - GameCooker.Build(target.Platform, target.Mode, target.Output, target.Options); + GameCooker.Build(target.Platform, target.Mode, target.Output, BuildOptions.None, target.CustomDefines); } else if (_exitOnBuildEnd) { From 28a67f65b4b2390c6e38ee35f162d9ac727b0647 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 13:55:19 +0100 Subject: [PATCH 416/419] Fix build tool log file access --- Source/Tools/Flax.Build/Log.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Log.cs b/Source/Tools/Flax.Build/Log.cs index ba948d7ca..3bd972f42 100644 --- a/Source/Tools/Flax.Build/Log.cs +++ b/Source/Tools/Flax.Build/Log.cs @@ -36,7 +36,7 @@ namespace Flax.Build var path = Path.GetDirectoryName(Configuration.LogFile); if (!string.IsNullOrEmpty(path) && !Directory.Exists(path)) Directory.CreateDirectory(path); - _logFile = new FileStream(Configuration.LogFile, FileMode.Create); + _logFile = new FileStream(Configuration.LogFile, FileMode.Create, FileAccess.Write, FileShare.Read); _logFileWriter = new StreamWriter(_logFile); } } From c902e33d8af955aff7321e629e63e832bb140b57 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 15:54:22 +0100 Subject: [PATCH 417/419] Add Build Actions option to editor settings for build button configuration --- Source/Editor/Editor.cs | 17 -- Source/Editor/Modules/UIModule.cs | 2 +- Source/Editor/Options/GeneralOptions.cs | 49 ++++++ Source/Editor/States/BuildingScenesState.cs | 159 +++++++++++++++--- Source/Editor/States/EditingSceneState.cs | 22 +++ Source/Editor/States/ReloadingScriptsState.cs | 22 +++ Source/Engine/Utilities/State.cs | 3 - Source/Engine/Utilities/StateMachine.cs | 5 + 8 files changed, 233 insertions(+), 46 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index f81f81ddd..3aa8346c8 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -242,8 +242,6 @@ namespace FlaxEditor StateMachine = new EditorStateMachine(this); Undo = new EditorUndo(this); - ScriptsBuilder.ScriptsReloadBegin += ScriptsBuilder_ScriptsReloadBegin; - ScriptsBuilder.ScriptsReloadEnd += ScriptsBuilder_ScriptsReloadEnd; UIControl.FallbackParentGetDelegate += OnUIControlFallbackParentGet; } @@ -260,18 +258,6 @@ namespace FlaxEditor return null; } - private void ScriptsBuilder_ScriptsReloadBegin() - { - EnsureState(); - StateMachine.GoToState(); - } - - private void ScriptsBuilder_ScriptsReloadEnd() - { - EnsureState(); - StateMachine.GoToState(); - } - internal void RegisterModule(EditorModule module) { Log("Register Editor module " + module); @@ -497,9 +483,6 @@ namespace FlaxEditor Surface.VisualScriptSurface.NodesCache.Clear(); Instance = null; - ScriptsBuilder.ScriptsReloadBegin -= ScriptsBuilder_ScriptsReloadBegin; - ScriptsBuilder.ScriptsReloadEnd -= ScriptsBuilder_ScriptsReloadEnd; - // Invoke new instance if need to open a project if (!string.IsNullOrEmpty(_projectToOpen)) { diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 6b01986a9..99d8f6e2a 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -507,7 +507,7 @@ namespace FlaxEditor.Modules _toolStripRotate = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Rotate32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate).LinkTooltip("Change Gizmo tool mode to Rotate (2)"); _toolStripScale = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Scale32, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale).LinkTooltip("Change Gizmo tool mode to Scale (3)"); ToolStrip.AddSeparator(); - _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build32, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes (Ctrl+F10)"); + _toolStripBuildScenes = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Build32, Editor.BuildScenesOrCancel).LinkTooltip("Build scenes data - CSG, navmesh, static lighting, env probes - configurable via Build Actions in editor options (Ctrl+F10)"); ToolStrip.AddSeparator(); _toolStripPlay = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Play32, Editor.Simulation.RequestPlayOrStopPlay).LinkTooltip("Start/Stop game (F5)"); _toolStripPause = (ToolStripButton)ToolStrip.AddButton(Editor.Icons.Pause32, Editor.Simulation.RequestResumeOrPause).LinkTooltip("Pause/Resume game(F6)"); diff --git a/Source/Editor/Options/GeneralOptions.cs b/Source/Editor/Options/GeneralOptions.cs index 9b5450f45..85174030f 100644 --- a/Source/Editor/Options/GeneralOptions.cs +++ b/Source/Editor/Options/GeneralOptions.cs @@ -32,6 +32,42 @@ namespace FlaxEditor.Options LastOpened, } + /// + /// The build actions. + /// + public enum BuildAction + { + /// + /// Builds Constructive Solid Geometry brushes into meshes. + /// + [Tooltip("Builds Constructive Solid Geometry brushes into meshes.")] + CSG, + + /// + /// Builds Env Probes and Sky Lights to prerendered cube textures. + /// + [Tooltip("Builds Env Probes and Sky Lights to prerendered cube textures.")] + EnvProbes, + + /// + /// Builds static lighting into lightmaps. + /// + [Tooltip("Builds static lighting into lightmaps.")] + StaticLighting, + + /// + /// Builds navigation meshes. + /// + [Tooltip("Builds navigation meshes.")] + NavMesh, + + /// + /// Compiles the scripts. + /// + [Tooltip("Compiles the scripts.")] + CompileScripts, + } + /// /// Gets or sets the scene to load on editor startup. /// @@ -53,6 +89,19 @@ namespace FlaxEditor.Options [EditorDisplay("General", "Editor FPS"), EditorOrder(110), Tooltip("Limit for the editor draw/update frames per second rate (FPS). Use higher values if you need more responsive interface or lower values to use less device power. Value 0 disables any limits.")] public float EditorFPS { get; set; } = 60.0f; + /// + /// Gets or sets the sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh). + /// + [EditorDisplay("General"), EditorOrder(200), Tooltip("The sequence of actions to perform when using Build Scenes button. Can be used to configure this as button (eg. compile code or just update navmesh).")] + public BuildAction[] BuildActions { get; set; } = + { + BuildAction.CSG, + BuildAction.EnvProbes, + BuildAction.StaticLighting, + BuildAction.EnvProbes, + BuildAction.NavMesh, + }; + /// /// Gets or sets a value indicating whether perform automatic scripts reload on main window focus. /// diff --git a/Source/Editor/States/BuildingScenesState.cs b/Source/Editor/States/BuildingScenesState.cs index 998292626..40ccff829 100644 --- a/Source/Editor/States/BuildingScenesState.cs +++ b/Source/Editor/States/BuildingScenesState.cs @@ -1,6 +1,9 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using System.Collections.Generic; +using System.Linq; +using FlaxEditor.Options; using FlaxEditor.SceneGraph.Actors; using FlaxEngine; using FlaxEngine.Utilities; @@ -16,6 +19,9 @@ namespace FlaxEditor.States { private sealed class SubStateMachine : StateMachine { + public int ActionIndex = -1; + public readonly List Actions = new List(); + protected override void SwitchState(State nextState) { if (CurrentState != null && nextState != null) @@ -27,10 +33,40 @@ namespace FlaxEditor.States private abstract class SubState : State { + public virtual bool DirtyScenes => true; + + public virtual bool CanReloadScripts => false; + + public virtual void Before() + { + } + public virtual void Update() { } + public virtual void Done() + { + var stateMachine = (SubStateMachine)StateMachine; + stateMachine.ActionIndex++; + if (stateMachine.ActionIndex < stateMachine.Actions.Count) + { + var action = stateMachine.Actions[stateMachine.ActionIndex]; + var state = stateMachine.States.FirstOrDefault(x => x is ActionState a && a.Action == action); + if (state != null) + { + StateMachine.GoToState(state); + } + else + { + Editor.LogError($"Missing or invalid build scene action {action}."); + } + return; + } + + StateMachine.GoToState(); + } + public virtual void Cancel() { StateMachine.GoToState(); @@ -45,18 +81,31 @@ namespace FlaxEditor.States { public override void OnEnter() { - var editor = Editor.Instance; - foreach (var scene in Level.Scenes) + var stateMachine = (SubStateMachine)StateMachine; + var scenesDirty = false; + foreach (var state in stateMachine.States) { - scene.ClearLightmaps(); - editor.Scene.MarkSceneEdited(scene); + ((SubState)state).Before(); + scenesDirty |= ((SubState)state).DirtyScenes; } - StateMachine.GoToState(); + if (scenesDirty) + { + foreach (var scene in Level.Scenes) + Editor.Instance.Scene.MarkSceneEdited(scene); + } + Done(); } } - private sealed class CSGState : SubState + private abstract class ActionState : SubState { + public abstract GeneralOptions.BuildAction Action { get; } + } + + private sealed class CSGState : ActionState + { + public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.CSG; + public override void OnEnter() { foreach (var scene in Level.Scenes) @@ -68,13 +117,14 @@ namespace FlaxEditor.States public override void Update() { if (!Editor.Internal_GetIsCSGActive()) - StateMachine.GoToState(); + Done(); } } - - private class EnvProbesNoGIState : SubState + private class EnvProbesState : ActionState { + public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.EnvProbes; + public override void OnEnter() { Editor.Instance.Scene.ExecuteOnGraph(node => @@ -94,12 +144,20 @@ namespace FlaxEditor.States public override void Update() { if (!Editor.Instance.ProgressReporting.BakeEnvProbes.IsActive) - StateMachine.GoToState(); + Done(); } } - private sealed class StaticLightingState : SubState + private sealed class StaticLightingState : ActionState { + public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.StaticLighting; + + public override void Before() + { + foreach (var scene in Level.Scenes) + scene.ClearLightmaps(); + } + public override void OnEnter() { Editor.LightmapsBakeEnd += OnLightmapsBakeEnd; @@ -110,7 +168,6 @@ namespace FlaxEditor.States OnLightmapsBakeEnd(false); } - /// public override void Cancel() { Editor.Internal_BakeLightmaps(true); @@ -125,21 +182,14 @@ namespace FlaxEditor.States private void OnLightmapsBakeEnd(bool failed) { - StateMachine.GoToState(); + Done(); } } - private sealed class EnvProbesWithGIState : EnvProbesNoGIState + private sealed class NavMeshState : ActionState { - public override void Update() - { - if (!Editor.Instance.ProgressReporting.BakeEnvProbes.IsActive) - StateMachine.GoToState(); - } - } + public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.NavMesh; - private sealed class NavMeshState : SubState - { public override void OnEnter() { foreach (var scene in Level.Scenes) @@ -151,7 +201,58 @@ namespace FlaxEditor.States public override void Update() { if (!Navigation.IsBuildingNavMesh) - StateMachine.GoToState(); + Done(); + } + } + + private sealed class CompileScriptsState : ActionState + { + private bool _compiled, _reloaded; + + public override GeneralOptions.BuildAction Action => GeneralOptions.BuildAction.CompileScripts; + + public override bool DirtyScenes => false; + + public override bool CanReloadScripts => true; + + public override void OnEnter() + { + _compiled = _reloaded = false; + ScriptsBuilder.Compile(); + + ScriptsBuilder.CompilationSuccess += OnCompilationSuccess; + ScriptsBuilder.CompilationFailed += OnCompilationFailed; + ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd; + } + + public override void OnExit(State nextState) + { + ScriptsBuilder.CompilationSuccess -= OnCompilationSuccess; + ScriptsBuilder.CompilationFailed -= OnCompilationFailed; + ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd; + + base.OnExit(nextState); + } + + private void OnCompilationSuccess() + { + _compiled = true; + } + + private void OnCompilationFailed() + { + Cancel(); + } + + private void OnScriptsReloadEnd() + { + _reloaded = true; + } + + public override void Update() + { + if (_compiled && _reloaded) + Done(); } } @@ -173,10 +274,10 @@ namespace FlaxEditor.States _stateMachine.AddState(new BeginState()); _stateMachine.AddState(new SetupState()); _stateMachine.AddState(new CSGState()); - _stateMachine.AddState(new EnvProbesNoGIState()); + _stateMachine.AddState(new EnvProbesState()); _stateMachine.AddState(new StaticLightingState()); - _stateMachine.AddState(new EnvProbesWithGIState()); _stateMachine.AddState(new NavMeshState()); + _stateMachine.AddState(new CompileScriptsState()); _stateMachine.AddState(new EndState()); _stateMachine.GoToState(); } @@ -192,6 +293,9 @@ namespace FlaxEditor.States /// public override bool CanEditContent => false; + /// + public override bool CanReloadScripts => ((SubState)_stateMachine.CurrentState).CanReloadScripts; + /// public override bool IsPerformanceHeavy => true; @@ -215,6 +319,11 @@ namespace FlaxEditor.States { Editor.Log("Starting scenes build..."); _startTime = DateTime.Now; + _stateMachine.ActionIndex = -1; + _stateMachine.Actions.Clear(); + var actions = (GeneralOptions.BuildAction[])Editor.Options.Options.General.BuildActions?.Clone(); + if (actions != null) + _stateMachine.Actions.AddRange(actions); _stateMachine.GoToState(); } diff --git a/Source/Editor/States/EditingSceneState.cs b/Source/Editor/States/EditingSceneState.cs index 7aa9ce13d..176039948 100644 --- a/Source/Editor/States/EditingSceneState.cs +++ b/Source/Editor/States/EditingSceneState.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using FlaxEngine; +using FlaxEngine.Utilities; namespace FlaxEditor.States { @@ -39,5 +40,26 @@ namespace FlaxEditor.States { UpdateFPS(); } + + /// + public override void OnEnter() + { + base.OnEnter(); + + ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; + } + + /// + public override void OnExit(State nextState) + { + ScriptsBuilder.ScriptsReloadBegin -= OnScriptsReloadBegin; + + base.OnExit(nextState); + } + + private void OnScriptsReloadBegin() + { + StateMachine.GoToState(); + } } } diff --git a/Source/Editor/States/ReloadingScriptsState.cs b/Source/Editor/States/ReloadingScriptsState.cs index 17f1abdab..1fb34317d 100644 --- a/Source/Editor/States/ReloadingScriptsState.cs +++ b/Source/Editor/States/ReloadingScriptsState.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using FlaxEngine; +using FlaxEngine.Utilities; namespace FlaxEditor.States { @@ -18,5 +19,26 @@ namespace FlaxEditor.States : base(editor) { } + + /// + public override void OnEnter() + { + base.OnEnter(); + + ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd; + } + + /// + public override void OnExit(State nextState) + { + ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd; + + base.OnExit(nextState); + } + + private void OnScriptsReloadEnd() + { + StateMachine.GoToState(); + } } } diff --git a/Source/Engine/Utilities/State.cs b/Source/Engine/Utilities/State.cs index f77308878..a0797ec0e 100644 --- a/Source/Engine/Utilities/State.cs +++ b/Source/Engine/Utilities/State.cs @@ -12,9 +12,6 @@ namespace FlaxEngine.Utilities /// /// Gets the state machine. /// - /// - /// The state machine. - /// public StateMachine StateMachine => owner; /// diff --git a/Source/Engine/Utilities/StateMachine.cs b/Source/Engine/Utilities/StateMachine.cs index 9966e3378..250d2fbe6 100644 --- a/Source/Engine/Utilities/StateMachine.cs +++ b/Source/Engine/Utilities/StateMachine.cs @@ -35,6 +35,11 @@ namespace FlaxEngine.Utilities /// public event Action StateChanged; + /// + /// Gets the states (read-only). + /// + public IReadOnlyList States => states; + /// /// Gets state of given type. /// From d05204cb9153850be6e7985d3d4129e38bae5fd8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 15:55:10 +0100 Subject: [PATCH 418/419] Fix missing lightmap uvs calc in vertex shader --- Content/Editor/MaterialTemplates/Features/Lightmap.hlsl | 1 + Content/Editor/MaterialTemplates/Surface.shader | 2 +- Source/Engine/Graphics/Materials/MaterialShader.h | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl b/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl index 33ef567ca..f4583fa7f 100644 --- a/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl +++ b/Content/Editor/MaterialTemplates/Features/Lightmap.hlsl @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. @0// Lightmap: Defines +#define CAN_USE_LIGHTMAP 1 @1// Lightmap: Includes @2// Lightmap: Constants float4 LightmapArea; diff --git a/Content/Editor/MaterialTemplates/Surface.shader b/Content/Editor/MaterialTemplates/Surface.shader index 1038a73a3..4eee27691 100644 --- a/Content/Editor/MaterialTemplates/Surface.shader +++ b/Content/Editor/MaterialTemplates/Surface.shader @@ -350,7 +350,7 @@ VertexOutput VS(ModelInput input) output.Geometry.LightmapUV = input.LightmapUV * input.InstanceLightmapArea.zw + input.InstanceLightmapArea.xy; output.Geometry.InstanceParams = float2(input.InstanceOrigin.w, input.InstanceTransform1.w); #else -#if USE_LIGHTMAP +#if CAN_USE_LIGHTMAP output.Geometry.LightmapUV = input.LightmapUV * LightmapArea.zw + LightmapArea.xy; #else output.Geometry.LightmapUV = input.LightmapUV; diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index 102ac4d41..8c49389a7 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -9,7 +9,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 147 +#define MATERIAL_GRAPH_VERSION 148 class Material; class GPUShader; From 1530f91944256798021e85a5195ce87bacc8bf53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Feb 2021 15:55:18 +0100 Subject: [PATCH 419/419] Update material assets --- Content/Editor/Camera/M_Camera.flax | 4 ++-- Content/Editor/CubeTexturePreviewMaterial.flax | 4 ++-- Content/Editor/DefaultFontMaterial.flax | 4 ++-- Content/Editor/Gizmo/FoliageBrushMaterial.flax | 4 ++-- Content/Editor/Gizmo/Material.flax | 4 ++-- Content/Editor/Gizmo/MaterialWire.flax | 4 ++-- Content/Editor/Gizmo/SelectionOutlineMaterial.flax | 2 +- Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax | 4 ++-- Content/Editor/Highlight Material.flax | 4 ++-- Content/Editor/Icons/IconsMaterial.flax | 4 ++-- Content/Editor/IesProfilePreviewMaterial.flax | 2 +- Content/Editor/Particles/Particle Material Color.flax | 2 +- Content/Editor/Particles/Smoke Material.flax | 2 +- Content/Editor/Terrain/Circle Brush Material.flax | 4 ++-- Content/Editor/Terrain/Highlight Terrain Material.flax | 4 ++-- Content/Editor/TexturePreviewMaterial.flax | 2 +- Content/Editor/Wires Debug Material.flax | 4 ++-- Content/Engine/DefaultDeformableMaterial.flax | 2 +- Content/Engine/DefaultMaterial.flax | 4 ++-- Content/Engine/DefaultTerrainMaterial.flax | 4 ++-- Content/Engine/SingleColorMaterial.flax | 4 ++-- Content/Engine/SkyboxMaterial.flax | 4 ++-- 22 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index 768cf1986..94890c96c 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d62539b39925798a3d526d20b3fc4e30c268629427c3920e1ece9f4c446ec45 -size 29995 +oid sha256:7b4752ca9986784f54afbad450edacdb3f87ed93d6e904c76ba40bb467e58d25 +size 30027 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index f4047b652..9825995fb 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33afbfa5c2b3e24499de14fdabf87bdc809ce703f859b718241e203115db3961 -size 31552 +oid sha256:6740a5d3ecc88c95f296dc0b77d8e177fcf3cc303dbaf9d1602aaf7008eb8b65 +size 31584 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index 3cb4ff8dd..f464ad768 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdca03c927766e9d4bb5409e2507b3a9d22f13342fad159182fdd8da122312cb -size 30076 +oid sha256:c9c2e7cc6393d6f2079d4d064daa0a2663ec84c25be7778b73da22e9c66c226e +size 30108 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index 8c87ae3b3..af6e065a2 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5129d65fa3a24af617b25b9058294b89e65290581b2992e77ba46a12ade7a8ee -size 34537 +oid sha256:15e7562cb1f6b58f9030fbd66477b131b577f9d7c08f0d6b67078f02d05e6015 +size 34541 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index 6bc4e8f93..64145ecec 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e470f838ed5ff78b311169f974a3832b387730be2030dc67e3ddf0d601dc3c0 -size 31063 +oid sha256:26a9333914f7ddf750e8d33be03c739bd3cc4fb455ebecbe83018b63ab8277b2 +size 31067 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index 2b85a3941..4e85879a0 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:083633783ea5733720803b285a2378a6130fec506b9a2abe93b146cdf736d0ed -size 30276 +oid sha256:405fdb8843f7836807a0efc40564c0ab46eab62072d04b6e209d140e4bedab66 +size 30280 diff --git a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax index 6a082e985..f3b1474d9 100644 --- a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax +++ b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae4775c4ade6c4e50f81b68a8a8546696f58bb1d701f2d8397a47d757707782f +oid sha256:74ab675aa70e0bfa43e7eedf8b813c8e0a1c792fa59289ef50cfe2cf2505b900 size 15863 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index 1314e5596..574773030 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:770a2498f0a712ae1705fd74204c62ded862b73613ed8cec70974bee0802350a -size 31010 +oid sha256:4e79a6d2c67d7dab5a6ef26e433c21db3533c54605627baf16d17cd33e998bb3 +size 31042 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index 824fbf090..6fb6aa10d 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50fcefc75caa48fe73ed23bf3fe2c023f9a6ac146d3a3bc16b4386a39aaa56f7 -size 28871 +oid sha256:f1a371e03bfc916068cbc2863f3c92dcab3ac0ba3a4839ac0efa5e10e43807b2 +size 28875 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index e7b91ff38..2cf1d5cb7 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aae6e991a3623c9c84165c30fceab674d4173bb27303a164bd3dcef61f30654d -size 28803 +oid sha256:df1097033d79b531c8f09c2cb95bbb85a8190c6cd0649b77f05dd617410a0b8e +size 28807 diff --git a/Content/Editor/IesProfilePreviewMaterial.flax b/Content/Editor/IesProfilePreviewMaterial.flax index 7dbe080c9..5466742ea 100644 --- a/Content/Editor/IesProfilePreviewMaterial.flax +++ b/Content/Editor/IesProfilePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58bba43288fb4b2dbcc8a6401ac765fd269bb231c1b19c64aa2bb9ca72e9c106 +oid sha256:b02d31b97be388836c675b7d5814fc5f46623446c9ffd7cff837901694141ad8 size 18415 diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index 81fcbea72..30b136e26 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e44da38b26f222f7081278dc64b7fa932c6f9bfd0b79694da784bd4d944e838a +oid sha256:de2cfa951e89d51dbe8453525c94a527cb9d1ad9fbd77166b56e6e4eb35f2806 size 29232 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index 66c3466e8..a0c1124e0 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:586e1e4620df43c638ca5a059dc1b705a82b47f398c973dd526e3f58570cf645 +oid sha256:d55de18eb0aba44d084546b4a5aabf3482219c9b2c14930e9446fe86b711e61c size 35331 diff --git a/Content/Editor/Terrain/Circle Brush Material.flax b/Content/Editor/Terrain/Circle Brush Material.flax index 30093ee5e..1a9236d73 100644 --- a/Content/Editor/Terrain/Circle Brush Material.flax +++ b/Content/Editor/Terrain/Circle Brush Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ce01e5aeaf46ed0ed2285b2b6c5b0d8f0bebdb0a7f5fbd246eef22a8ead21fa -size 27520 +oid sha256:b6cd8240338f9b506ebb6ef42646958d72f6ed10ceae1ec1fa49291e2f4deeb4 +size 27548 diff --git a/Content/Editor/Terrain/Highlight Terrain Material.flax b/Content/Editor/Terrain/Highlight Terrain Material.flax index a942d7848..34de2594f 100644 --- a/Content/Editor/Terrain/Highlight Terrain Material.flax +++ b/Content/Editor/Terrain/Highlight Terrain Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f8b40c2622b1422ae062bafec002cd120e160f2ed56aab10e3390ba3dbd3a55 -size 21199 +oid sha256:ec51e766fb690f614663e39dc466c38d23b44ed08eec927c49f49cd46795e07e +size 21227 diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax index cc3b32eb9..7a6c1e659 100644 --- a/Content/Editor/TexturePreviewMaterial.flax +++ b/Content/Editor/TexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f92a749ea24fc5d7b45e0e4f2be5619402163f4292d565e5fe014268a46a1275 +oid sha256:d36c699f69d20d9d9aaf03011bdcaa2cbdfd42e6ceed7defe3dd70fc8b9e9a78 size 10653 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index 0b90a7474..b1af939b3 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9563e2a8c60bac96093c614dde7a581bc5eb1ce6f7da00339f4b8f2f1607c2a4 -size 28871 +oid sha256:da9378517b42e1c7c4e9c7a79d1f86d41f979655d6f8d2900708ae668517e486 +size 28875 diff --git a/Content/Engine/DefaultDeformableMaterial.flax b/Content/Engine/DefaultDeformableMaterial.flax index 3b2e45a87..197d82b3f 100644 --- a/Content/Engine/DefaultDeformableMaterial.flax +++ b/Content/Engine/DefaultDeformableMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dab7eb4f71a407eae7d6b051cbad701f234742b0d96646811c8a615de80e94eb +oid sha256:d9835db8c0994006b43042b26164ace74c8a2027a9586edc3ec80d16ca75f680 size 18800 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index a746a0e4e..4212fadbd 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77076bc1e501bc2f630f559dfb699c965d2d95c5a611b4619a2819bc1f6eb8ca -size 31774 +oid sha256:757da751c813fd60224d2000faa2de848f36b8555163791b9c852ac7e1c6b59a +size 31806 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index c7fbfec0f..2a98d9c36 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:909a9b45c735f3356d68885f6d294cb0627f5107316edef32b4b56328e8a0353 -size 23279 +oid sha256:c0a0cd43b9fb5a84e85c4376049db1ef3d2c64dd0cca338ff8f790e6aa1f86a9 +size 23307 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index 2aceb44be..a22fabbb8 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:075200d4b5a1353b463aff9c019212bea19aaee6ff38e34a3ddefceb816c00ec -size 30075 +oid sha256:889439f17a4523cf18a17c46f4348654cce5bc25c1ab1928e411405d8eacfd99 +size 30130 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index 7a7822ac7..46e0c9304 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b58ddef0b73414d33f88a4e15df151b0a95a1093116b3d1e194d9b781ff09051 -size 31296 +oid sha256:1a5bfb7e11eeccf5c44ee2a284b3d0479a752cb8af470b5b93fd8fe8213947be +size 31328