Compare commits

...

78 Commits

Author SHA1 Message Date
mafiesto4 c328d2b559 Increase maximum window width limit to 8k 2021-08-26 20:22:10 +02:00
mafiesto4 65747c9ddf Fix issue with MapTrack for actors 2021-08-26 20:22:02 +02:00
mafiesto4 d4a4ae996e Add vehicle telemetry logging debug option 2021-08-21 13:48:18 +02:00
mafiesto4 e312485a34 Bump up build number 2021-08-21 13:35:32 +02:00
mafiesto4 7dfe709196 Fix suspension force offset location 2021-08-21 13:16:19 +02:00
mafiesto4 759845c37b Add wheeled vehicle tire options 2021-08-21 13:16:14 +02:00
mafiesto4 5e3fe56aa7 Add digital steering option for a vehicle 2021-08-21 13:16:07 +02:00
mafiesto4 4b506560e9 Fix WheeledVehicle driving and suspension raycasts 2021-08-21 13:15:25 +02:00
mafiesto4 330c46b949 Add SuspensionForceOffset to vehicle wheel config 2021-08-21 13:15:10 +02:00
mafiesto4 b29d1f9902 Fix rare crashes due to selection not being empty on play mode start 2021-08-21 13:13:32 +02:00
mafiesto4 15f60b33b1 Optimize ticking disabled vehicles 2021-08-21 13:13:11 +02:00
mafiesto4 1a54ea82b5 Fix invalid rigidbody center of mass location 2021-08-21 13:12:47 +02:00
mafiesto4 949bc67354 Fix decal material normal vector blending 2021-08-21 13:12:36 +02:00
mafiesto4 d816af41b4 Fix invalid navmesh build for triggers 2021-08-21 13:12:26 +02:00
mafiesto4 a7880d73f3 Fix 2021-08-21 13:12:09 +02:00
mafiesto4 ac21c4d1e7 Adjust scene node context menu 2021-08-21 13:11:10 +02:00
mafiesto4 ed8a10f276 Add option to select scene asset from Scene tree window 2021-08-21 13:11:04 +02:00
mafiesto4 8b8a83eb20 Fix materials parameters display issues after editing 2021-08-21 13:10:44 +02:00
mafiesto4 890ea38470 Add -new command line arg to create a new project 2021-08-21 13:10:08 +02:00
mafiesto4 07d06c74b5 Fix context menu keyboard arrows navigation to skip disabled items 2021-08-21 13:09:49 +02:00
Erdroy ec18b01ef7 Fix ENet driver crash 2021-08-21 13:09:27 +02:00
mafiesto4 7a1817b099 Fix Custom Editor UI regression
3cf3f58db1
2021-08-21 13:09:09 +02:00
mafiesto4 a2782515a2 Fix compilation 2021-08-21 13:08:55 +02:00
mafiesto4 9deb021a5e Refactor Tracy source locations to be static
#597
2021-08-21 13:08:19 +02:00
mafiesto4 0bbb72e24d Fix some memory leaks 2021-08-21 13:07:29 +02:00
mafiesto4 7656cf541d Fix snap to the ground to use scene graph query instead of physics only raycast 2021-08-21 13:07:03 +02:00
mafiesto4 d77ad33583 Increase fog properties limits 2021-08-21 13:06:45 +02:00
mafiesto4 6f5605b7ca Fix window focus issue when removing actors 2021-08-21 13:06:26 +02:00
mafiesto4 718ae78036 Fix UI with list of null items when layout gets rebuilt at item level 2021-08-21 13:06:04 +02:00
mafiesto4 f6eef82864 Fix LocalizedString undo bug 2021-08-21 13:05:49 +02:00
mafiesto4 26d37103ba Fix some problems with LocalizedString serialization in C# 2021-08-21 13:05:25 +02:00
mafiesto4 d85c65bfc5 Add check to prevent double-free from native event in C# 2021-08-21 13:03:29 +02:00
mafiesto4 1943c64a33 Fix timeline background stops drawing to be ore readable
#519
2021-08-21 13:02:39 +02:00
mafiesto4 9c1fa7c0ce Add Zoom value box to timeline view context menu 2021-08-21 13:01:56 +02:00
mafiesto4 14068307d1 Add support for changing scenes via Editor during play mode 2021-08-21 13:01:42 +02:00
mafiesto4 670e48ec1c Add option to Scene context menu to unload it 2021-08-21 13:01:11 +02:00
mafiesto4 e288104472 Add helper tooltips for Scene Animation actions UI 2021-08-21 12:59:59 +02:00
mafiesto4 99b0cf71a8 Add better timeline UI positioning for improved viewport navigation 2021-08-21 12:59:47 +02:00
mafiesto4 2b7e6944e8 Add timeline view panning with right mouse button 2021-08-21 12:59:15 +02:00
mafiesto4 9e7ce69e9c Add Engine.HasGameViewportFocus to C# API 2021-08-21 12:57:57 +02:00
mafiesto4 8c97a645e9 Fix car wheel location by applying compression of the suspension spring 2021-08-21 12:57:31 +02:00
mafiesto4 d194a06e59 Add options for vehicle wheel suspension configuration and state 2021-08-21 12:56:47 +02:00
mafiesto4 783372c787 Fix pasting actors if cannot spawn a object (eg. type missing) to still paste valid objects 2021-08-21 12:56:13 +02:00
mafiesto4 51016949b8 Fix actor renaming on paste to keep previous name if can 2021-08-21 12:55:17 +02:00
mafiesto4 128cad49b9 Bump up version number 2021-08-08 21:46:24 +02:00
mafiesto4 65f9e9d0aa Fix error 2021-08-08 20:58:36 +02:00
mafiesto4 36df9757b1 Add creating Scene Graph nodes for actors inheriting from some shared custom types 2021-08-08 20:58:19 +02:00
mafiesto4 a0e1c7c37e Add support for creating custom BoxVolume type in C# 2021-08-08 20:19:43 +02:00
mafiesto4 847641f655 Fix crash when spawning C# object that inherits from abstract C++ class 2021-08-08 19:10:51 +02:00
mafiesto4 cdca5b4a28 Fix undo for missing actor children and scripts when doing Convert action 2021-08-08 18:38:26 +02:00
mafiesto4 6609425880 Fix creating prefab in Editor from selection in Prefab window 2021-08-08 17:25:18 +02:00
mafiesto4 6ac0d5d3f4 Merge branch 'blep-bugfix_crash_2riderinstall' 2021-08-08 16:40:56 +02:00
mafiesto4 bc46259286 Merge branch 'bugfix_crash_2riderinstall' of git://github.com/blep/FlaxEngine into blep-bugfix_crash_2riderinstall 2021-08-08 16:40:41 +02:00
mafiesto4 50bccd52e7 Fix startup failure on older CPUs that don't support invariant TSC
#580
2021-08-08 16:38:28 +02:00
mafiesto4 84cca1ae98 Fix missing mesh collider vertices transformation for navmesh building 2021-08-08 16:20:48 +02:00
mafiesto4 71cf758ccf Fix crash when cooking collision data for multi-mesh models 2021-08-08 16:18:29 +02:00
mafiesto4 edf98acae2 Undo unwanted change 2021-08-08 16:04:07 +02:00
mafiesto4 a085531fda Add build preset and target names to access for Game Cooker extending 2021-08-08 16:02:48 +02:00
mafiesto4 6b532d2fbc Add CookingData for C# editor to extend the cooking process 2021-08-08 16:01:58 +02:00
Baptiste Lepilleur 7862ff4670 Fixed crash on startup when 2 Rider installations are detected 2021-08-07 21:43:58 +02:00
mafiesto4 bc27890818 Merge branch 'marynate-PR-global-local-crash' 2021-08-07 16:52:48 +02:00
mafiesto4 1c5754beff Merge branch 'PR-global-local-crash' of git://github.com/marynate/FlaxEngine into marynate-PR-global-local-crash 2021-08-07 16:52:39 +02:00
mafiesto4 054def3d13 Fix crash in build tool if there is no valid project to pick 2021-08-07 16:46:39 +02:00
mafiesto4 e6d5d5330e Fix CharacterController minimum size checks to prevent crashes if values are too small 2021-08-07 15:52:49 +02:00
mafiesto4 f430803ecc Merge branch 'thallard-dev' 2021-08-07 15:33:12 +02:00
mafiesto4 fc762fdbdb Merge branch 'dev' of git://github.com/thallard/FlaxEngine into thallard-dev 2021-08-07 15:29:01 +02:00
marynate 6908aa4a8a Catch potential runtime error from locale::global 2021-08-07 14:20:38 +08:00
Erdroy 6c70f2e14f Fix editor crash on startup when Rider 2021.1 is installed 2021-08-06 18:58:19 +02:00
thallard 3bc489a7b5 Replace string variable check by an enum 2021-08-06 15:50:30 +02:00
mafiesto4 d22380c7cf Add warnings when cooking collision with empty mesh data 2021-08-06 15:07:50 +02:00
mafiesto4 c4102ba884 Fix compiling C#-only game scripts without native platform tools installed 2021-08-06 13:17:23 +02:00
thallard baee3a60a6 Added tick when a button is selected 2021-08-06 11:47:50 +02:00
thallard 5e2b4adff3 Removed useless static path thanks to @jb-perrier 2021-08-06 00:21:27 +02:00
thallard 78d668c599 Change enum and variable name 2021-08-06 00:07:44 +02:00
thallard e8b867430f Remplace string variable by enum 2021-08-05 23:38:08 +02:00
thallard 1426b4ca49 Removed useless debug logs 2021-08-05 23:05:27 +02:00
thallard 9682ac4643 Merge branch 'master' of https://github.com/FlaxEngine/FlaxEngine into dev 2021-08-05 22:37:22 +02:00
thallard d95cd24b76 Added functionality to order items by name 2021-08-05 22:24:01 +02:00
103 changed files with 1411 additions and 400 deletions
@@ -194,16 +194,16 @@ void PS_Decal(
Out2 = float4(material.Emissive, material.Opacity); Out2 = float4(material.Emissive, material.Opacity);
#if USE_NORMAL #if USE_NORMAL
// GBuffer1 // GBuffer1
Out3 = float4(material.WorldNormal * 0.5f + 0.5f, 1); Out3 = float4(material.WorldNormal * 0.5f + 0.5f, material.Opacity);
#endif #endif
#elif USE_NORMAL #elif USE_NORMAL
// GBuffer1 // GBuffer1
Out2 = float4(material.WorldNormal * 0.5f + 0.5f, 1); Out2 = float4(material.WorldNormal * 0.5f + 0.5f, material.Opacity);
#endif #endif
#elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_STAIN #elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_STAIN
Out0 = float4(material.Color, material.Opacity); Out0 = float4(material.Color, material.Opacity);
#elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_NORMAL #elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_NORMAL
Out0 = float4(material.WorldNormal * 0.5f + 0.5f, 1); Out0 = float4(material.WorldNormal * 0.5f + 0.5f, material.Opacity);
#elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_EMISSIVE #elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_EMISSIVE
Out0 = float4(material.Emissive * material.Opacity, material.Opacity); Out0 = float4(material.Emissive * material.Opacity, material.Opacity);
#else #else
+1 -1
View File
@@ -3,7 +3,7 @@
"Version": { "Version": {
"Major": 1, "Major": 1,
"Minor": 2, "Minor": 2,
"Build": 6222 "Build": 6224
}, },
"Company": "Flax", "Company": "Flax",
"Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.", "Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.",
@@ -7,7 +7,8 @@
void RegisterGameCookingStart(GameCooker::EventType type) void RegisterGameCookingStart(GameCooker::EventType type)
{ {
auto platform = ToString(GameCooker::GetCurrentData().Platform); auto& data = *GameCooker::GetCurrentData();
auto platform = ToString(data.Platform);
if (type == GameCooker::EventType::BuildStarted) if (type == GameCooker::EventType::BuildStarted)
{ {
EditorAnalytics::SendEvent("Actions", "GameCooker.Start", platform); EditorAnalytics::SendEvent("Actions", "GameCooker.Start", platform);
+36 -4
View File
@@ -27,6 +27,22 @@ namespace FlaxEditor.Content.GUI
List, List,
} }
/// <summary>
/// The method sort for items.
/// </summary>
public enum SortType
{
/// <summary>
/// The classic alphabetic sort method (A-Z).
/// </summary>
AlphabeticOrder,
/// <summary>
/// The reverse alphabetic sort method (Z-A).
/// </summary>
AlphabeticReverse
}
/// <summary> /// <summary>
/// Main control for <see cref="ContentWindow"/> used to present collection of <see cref="ContentItem"/>. /// Main control for <see cref="ContentWindow"/> used to present collection of <see cref="ContentItem"/>.
/// </summary> /// </summary>
@@ -218,8 +234,9 @@ namespace FlaxEditor.Content.GUI
/// Shows the items collection in the view. /// Shows the items collection in the view.
/// </summary> /// </summary>
/// <param name="items">The items to show.</param> /// <param name="items">The items to show.</param>
/// <param name="sortType">The sort method for items.</param>
/// <param name="additive">If set to <c>true</c> items will be added to the current selection. Otherwise selection will be cleared before.</param> /// <param name="additive">If set to <c>true</c> items will be added to the current selection. Otherwise selection will be cleared before.</param>
public void ShowItems(List<ContentItem> items, bool additive = false) public void ShowItems(List<ContentItem> items, SortType sortType, bool additive = false)
{ {
if (items == null) if (items == null)
throw new ArgumentNullException(); throw new ArgumentNullException();
@@ -249,9 +266,24 @@ namespace FlaxEditor.Content.GUI
items[i].AddReference(this); items[i].AddReference(this);
} }
// Sort items // Sort items depending on sortMethod parameter
_children.Sort(); _children.Sort(((control, control1) =>
{
if (sortType == SortType.AlphabeticReverse)
{
if (control.CompareTo(control1) > 0)
{
return -1;
}
if (control.CompareTo(control1) == 0)
{
return 0;
}
return 1;
}
return control.CompareTo(control1);
}));
// Unload and perform UI layout // Unload and perform UI layout
IsLayoutLocked = wasLayoutLocked; IsLayoutLocked = wasLayoutLocked;
PerformLayout(); PerformLayout();
+26 -20
View File
@@ -3,11 +3,11 @@
#pragma once #pragma once
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/Enums.h" #include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/HashSet.h" #include "Engine/Core/Collections/HashSet.h"
#include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Core/Types/Guid.h" #include "Engine/Scripting/ScriptingObject.h"
class GameCooker; class GameCooker;
class PlatformTools; class PlatformTools;
@@ -125,47 +125,60 @@ extern FLAXENGINE_API const Char* ToString(const BuildConfiguration configuratio
/// <summary> /// <summary>
/// Game cooking temporary data. /// Game cooking temporary data.
/// </summary> /// </summary>
struct FLAXENGINE_API CookingData API_CLASS(Sealed, Namespace="FlaxEditor") class FLAXENGINE_API CookingData : public PersistentScriptingObject
{ {
DECLARE_SCRIPTING_TYPE(CookingData);
public:
/// <summary> /// <summary>
/// The platform. /// The platform.
/// </summary> /// </summary>
BuildPlatform Platform; API_FIELD(ReadOnly) BuildPlatform Platform;
/// <summary> /// <summary>
/// The configuration. /// The configuration.
/// </summary> /// </summary>
BuildConfiguration Configuration; API_FIELD(ReadOnly) BuildConfiguration Configuration;
/// <summary> /// <summary>
/// The options. /// The options.
/// </summary> /// </summary>
BuildOptions Options; API_FIELD(ReadOnly) BuildOptions Options;
/// <summary>
/// The name of build preset used for cooking (can be used by editor and game plugins).
/// </summary>
API_FIELD(ReadOnly) String Preset;
/// <summary>
/// The name of build preset target used for cooking (can be used by editor and game plugins).
/// </summary>
API_FIELD(ReadOnly) String PresetTarget;
/// <summary> /// <summary>
/// The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines). /// The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).
/// </summary> /// </summary>
Array<String> CustomDefines; API_FIELD(ReadOnly) Array<String> CustomDefines;
/// <summary> /// <summary>
/// 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. /// 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.
/// </summary> /// </summary>
String OriginalOutputPath; API_FIELD(ReadOnly) String OriginalOutputPath;
/// <summary> /// <summary>
/// The output path for data files (Content, Mono, etc.). /// The output path for data files (Content, Mono, etc.).
/// </summary> /// </summary>
String DataOutputPath; API_FIELD(ReadOnly) String DataOutputPath;
/// <summary> /// <summary>
/// The output path for binaries (native executable and native code libraries). /// The output path for binaries (native executable and native code libraries).
/// </summary> /// </summary>
String NativeCodeOutputPath; API_FIELD(ReadOnly) String NativeCodeOutputPath;
/// <summary> /// <summary>
/// The output path for binaries (C# code libraries). /// The output path for binaries (C# code libraries).
/// </summary> /// </summary>
String ManagedCodeOutputPath; API_FIELD(ReadOnly) String ManagedCodeOutputPath;
/// <summary> /// <summary>
/// The platform tools. /// The platform tools.
@@ -248,7 +261,7 @@ public:
/// </summary> /// </summary>
HashSet<Guid> Assets; HashSet<Guid> Assets;
struct BinaryModule struct BinaryModuleInfo
{ {
String Name; String Name;
String NativePath; String NativePath;
@@ -258,17 +271,10 @@ public:
/// <summary> /// <summary>
/// The binary modules used in the build. Valid after scripts compilation step. This list includes game, all plugins modules and engine module. /// The binary modules used in the build. Valid after scripts compilation step. This list includes game, all plugins modules and engine module.
/// </summary> /// </summary>
Array<BinaryModule, InlinedAllocation<64>> BinaryModules; Array<BinaryModuleInfo, InlinedAllocation<64>> BinaryModules;
public: public:
void Init()
{
RootAssets.Clear();
Assets.Clear();
Stats = Statistics();
}
/// <summary> /// <summary>
/// Gets the absolute path to the Platform Data folder that contains the binary files used by the current build configuration. /// Gets the absolute path to the Platform Data folder that contains the binary files used by the current build configuration.
/// </summary> /// </summary>
+18 -9
View File
@@ -68,7 +68,7 @@ namespace GameCookerImpl
String ProgressMsg; String ProgressMsg;
float ProgressValue; float ProgressValue;
CookingData Data; CookingData* Data = nullptr;
Array<GameCooker::BuildStep*> Steps; Array<GameCooker::BuildStep*> Steps;
Dictionary<BuildPlatform, PlatformTools*> Tools; Dictionary<BuildPlatform, PlatformTools*> Tools;
@@ -154,6 +154,11 @@ CookingData::Statistics::Statistics()
ContentSizeMB = 0; ContentSizeMB = 0;
} }
CookingData::CookingData(const SpawnParams& params)
: PersistentScriptingObject(params)
{
}
String CookingData::GetGameBinariesPath() const String CookingData::GetGameBinariesPath() const
{ {
const Char* archDir; const Char* archDir;
@@ -179,7 +184,7 @@ String CookingData::GetGameBinariesPath() const
return String::Empty; return String::Empty;
} }
return GetPlatformBinariesRoot() / TEXT("Game") / archDir / ToString(Configuration); return GetPlatformBinariesRoot() / TEXT("Game") / archDir / ::ToString(Configuration);
} }
String CookingData::GetPlatformBinariesRoot() const String CookingData::GetPlatformBinariesRoot() const
@@ -239,7 +244,7 @@ public:
GameCookerService GameCookerServiceInstance; GameCookerService GameCookerServiceInstance;
const CookingData& GameCooker::GetCurrentData() CookingData* GameCooker::GetCurrentData()
{ {
return Data; return Data;
} }
@@ -313,7 +318,7 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform)
return result; return result;
} }
void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options, const Array<String>& customDefines) void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options, const Array<String>& customDefines, const StringView& preset, const StringView& presetTarget)
{ {
if (IsRunning()) if (IsRunning())
{ {
@@ -331,12 +336,14 @@ void GameCooker::Build(BuildPlatform platform, BuildConfiguration configuration,
CancelFlag = 0; CancelFlag = 0;
ProgressMsg.Clear(); ProgressMsg.Clear();
ProgressValue = 1.0f; ProgressValue = 1.0f;
CookingData& data = Data; Data = New<CookingData>();
data.Init(); CookingData& data = *Data;
data.Tools = tools; data.Tools = tools;
data.Platform = platform; data.Platform = platform;
data.Configuration = configuration; data.Configuration = configuration;
data.Options = options; data.Options = options;
data.Preset = preset;
data.PresetTarget = presetTarget;
data.CustomDefines = customDefines; data.CustomDefines = customDefines;
data.OriginalOutputPath = outputPath; data.OriginalOutputPath = outputPath;
FileSystem::NormalizePath(data.OriginalOutputPath); FileSystem::NormalizePath(data.OriginalOutputPath);
@@ -448,7 +455,7 @@ void GameCookerImpl::OnCollectAssets(HashSet<Guid>& assets)
bool GameCookerImpl::Build() bool GameCookerImpl::Build()
{ {
CookingData& data = Data; CookingData& data = *Data;
LOG(Info, "Starting Game Cooker..."); LOG(Info, "Starting Game Cooker...");
LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration)); LOG(Info, "Platform: {0}, Configuration: {2}, Options: {1}", ::ToString(data.Platform), (int32)data.Options, ::ToString(data.Configuration));
LOG(Info, "Output Path: {0}", data.OriginalOutputPath); LOG(Info, "Output Path: {0}", data.OriginalOutputPath);
@@ -472,7 +479,7 @@ bool GameCookerImpl::Build()
CallEvent(GameCooker::EventType::BuildStarted); CallEvent(GameCooker::EventType::BuildStarted);
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildStarted(data); Steps[stepIndex]->OnBuildStarted(data);
Data.Tools->OnBuildStarted(data); data.Tools->OnBuildStarted(data);
data.InitProgress(Steps.Count()); data.InitProgress(Steps.Count());
// Execute all steps in a sequence // Execute all steps in a sequence
@@ -511,10 +518,12 @@ bool GameCookerImpl::Build()
} }
IsRunning = false; IsRunning = false;
CancelFlag = 0; CancelFlag = 0;
Data.Tools->OnBuildEnded(data, failed); data.Tools->OnBuildEnded(data, failed);
for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++) for (int32 stepIndex = 0; stepIndex < Steps.Count(); stepIndex++)
Steps[stepIndex]->OnBuildEnded(data, failed); Steps[stepIndex]->OnBuildEnded(data, failed);
CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone); CallEvent(failed ? GameCooker::EventType::BuildFailed : GameCooker::EventType::BuildDone);
Delete(Data);
Data = nullptr;
return failed; return failed;
} }
+4 -2
View File
@@ -56,7 +56,7 @@ public:
/// <summary> /// <summary>
/// Gets the current build data. Valid only during active build process. /// Gets the current build data. Valid only during active build process.
/// </summary> /// </summary>
static const CookingData& GetCurrentData(); API_PROPERTY() static CookingData* GetCurrentData();
/// <summary> /// <summary>
/// Determines whether game building is running. /// Determines whether game building is running.
@@ -83,7 +83,9 @@ public:
/// <param name="outputPath">The output path (output directory).</param> /// <param name="outputPath">The output path (output directory).</param>
/// <param name="options">The build options.</param> /// <param name="options">The build options.</param>
/// <param name="customDefines">The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).</param> /// <param name="customDefines">The list of custom defines passed to the build tool when compiling project scripts. Can be used in build scripts for configuration (Configuration.CustomDefines).</param>
API_FUNCTION() static void Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options, const Array<String>& customDefines); /// <param name="preset">The name of build preset used for cooking (can be used by editor and game plugins).</param>
/// <param name="presetTarget">The name of build preset target used for cooking (can be used by editor and game plugins).</param>
API_FUNCTION() static void Build(BuildPlatform platform, BuildConfiguration configuration, const StringView& outputPath, BuildOptions options, const Array<String>& customDefines, const StringView& preset = StringView::Empty, const StringView& presetTarget = StringView::Empty);
/// <summary> /// <summary>
/// Sends a cancel event to the game building service. /// Sends a cancel event to the game building service.
+7 -9
View File
@@ -101,11 +101,6 @@ namespace FlaxEditor.CustomEditors
/// </summary> /// </summary>
protected bool IsSetBlocked => _isSetBlocked; protected bool IsSetBlocked => _isSetBlocked;
/// <summary>
/// Gets a value indicating whether this editor needs value propagation up (value synchronization when one of the child editors changes value, used by the struct types).
/// </summary>
protected virtual bool NeedsValuePropagationUp => Values.HasValueType;
/// <summary> /// <summary>
/// The linked label used to show this custom editor. Can be null if not used (eg. editor is inlined or is using a very customized UI layout). /// The linked label used to show this custom editor. Can be null if not used (eg. editor is inlined or is using a very customized UI layout).
/// </summary> /// </summary>
@@ -281,7 +276,7 @@ namespace FlaxEditor.CustomEditors
// Propagate values up (eg. when member of structure gets modified, also structure should be updated as a part of the other object) // Propagate values up (eg. when member of structure gets modified, also structure should be updated as a part of the other object)
var obj = _parent; var obj = _parent;
while (obj._parent != null && !(obj._parent is SyncPointEditor)) // && obj.NeedsValuePropagationUp) while (obj._parent != null && !(obj._parent is SyncPointEditor))
{ {
obj.Values.Set(obj._parent.Values, obj.Values); obj.Values.Set(obj._parent.Values, obj.Values);
obj = obj._parent; obj = obj._parent;
@@ -301,12 +296,15 @@ namespace FlaxEditor.CustomEditors
_isSetBlocked = false; _isSetBlocked = false;
// Update children // Update children
if (_skipChildrenRefresh)
{
_skipChildrenRefresh = false;
return;
}
try try
{ {
var childrenCount = _skipChildrenRefresh ? 0 : _children.Count; for (int i = 0; i < _children.Count; i++)
for (int i = 0; i < childrenCount; i++)
_children[i].RefreshInternal(); _children[i].RefreshInternal();
_skipChildrenRefresh = false;
} }
catch (TargetException ex) catch (TargetException ex)
{ {
@@ -154,6 +154,10 @@ namespace FlaxEditor.CustomEditors.Editors
var panel = layout.VerticalPanel(); var panel = layout.VerticalPanel();
panel.Panel.BackgroundColor = _background; panel.Panel.BackgroundColor = _background;
var elementType = ElementType; var elementType = ElementType;
// Use separate layout cells for each collection items to improve layout updates for them in separation
var useSharedLayout = elementType.IsPrimitive || elementType.IsEnum;
if (_canReorderItems) if (_canReorderItems)
{ {
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
@@ -178,7 +182,9 @@ namespace FlaxEditor.CustomEditors.Editors
} }
var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null;
panel.Object(new CollectionItemLabel(this, i), new ListValueContainer(elementType, i, Values), overrideEditor); var property = panel.AddPropertyItem(new CollectionItemLabel(this, i));
var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel();
itemLayout.Object(new ListValueContainer(elementType, i, Values), overrideEditor);
} }
} }
else else
@@ -194,7 +200,9 @@ namespace FlaxEditor.CustomEditors.Editors
} }
var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null;
panel.Object("Element " + i, new ListValueContainer(elementType, i, Values), overrideEditor); var property = panel.AddPropertyItem("Element " + i);
var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel();
itemLayout.Object(new ListValueContainer(elementType, i, Values), overrideEditor);
} }
} }
} }
@@ -216,6 +216,11 @@ namespace FlaxEditor.CustomEditors.Editors
panel.Panel.BackgroundColor = _background; panel.Panel.BackgroundColor = _background;
var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType<object>(); var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType<object>();
var keys = keysEnumerable as object[] ?? keysEnumerable.ToArray(); var keys = keysEnumerable as object[] ?? keysEnumerable.ToArray();
var valuesType = new ScriptType(valueType);
// Use separate layout cells for each collection items to improve layout updates for them in separation
var useSharedLayout = valueType.IsPrimitive || valueType.IsEnum;
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
if (i != 0 && spacing > 0f) if (i != 0 && spacing > 0f)
@@ -239,7 +244,9 @@ namespace FlaxEditor.CustomEditors.Editors
var key = keys.ElementAt(i); var key = keys.ElementAt(i);
var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null;
panel.Object(new DictionaryItemLabel(this, key), new DictionaryValueContainer(new ScriptType(valueType), key, Values), overrideEditor); var property = panel.AddPropertyItem(new DictionaryItemLabel(this, key));
var itemLayout = useSharedLayout ? (LayoutElementsContainer)property : property.VerticalPanel();
itemLayout.Object(new DictionaryValueContainer(valuesType, key, Values), overrideEditor);
} }
} }
_elementsCount = size; _elementsCount = size;
@@ -96,14 +96,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Update tooltip // Update tooltip
if (_value is SceneObject sceneObject) if (_value is SceneObject sceneObject)
{ {
var str = sceneObject is Actor actor ? actor.Name : type.Name; TooltipText = Utilities.Utils.GetTooltip(sceneObject);
var o = sceneObject.Parent;
while (o)
{
str = o.Name + " -> " + str;
o = o.Parent;
}
TooltipText = str;
} }
else else
{ {
@@ -228,6 +228,7 @@ namespace FlaxEditor.CustomEditors.Editors
} }
private VisibleIfCache[] _visibleIfCaches; private VisibleIfCache[] _visibleIfCaches;
private bool _isNull;
/// <summary> /// <summary>
/// Gets the items for the type /// Gets the items for the type
@@ -264,7 +265,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Skip properties without getter or setter // Skip properties without getter or setter
if (!p.HasGet || (!p.HasSet && !showInEditor)) if (!p.HasGet || (!p.HasSet && !showInEditor))
continue; continue;
// Skip hidden fields, handle special attributes // Skip hidden fields, handle special attributes
if ((!p.IsPublic && !showInEditor) || attributes.Any(x => x is HideInEditorAttribute)) if ((!p.IsPublic && !showInEditor) || attributes.Any(x => x is HideInEditorAttribute))
continue; continue;
@@ -418,6 +419,14 @@ namespace FlaxEditor.CustomEditors.Editors
} }
} }
/// <inheritdoc />
internal override void Initialize(CustomEditorPresenter presenter, LayoutElementsContainer layout, ValueContainer values)
{
_isNull = values != null && values.IsNull;
base.Initialize(presenter, layout, values);
}
/// <inheritdoc /> /// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout) public override void Initialize(LayoutElementsContainer layout)
{ {
@@ -446,12 +455,7 @@ namespace FlaxEditor.CustomEditors.Editors
Parent = layout.ContainerControl, Parent = layout.ContainerControl,
Location = new Vector2(layout.ContainerControl.Width - ButtonSize - 4, (layout.ContainerControl.Height - ButtonSize) * 0.5f), Location = new Vector2(layout.ContainerControl.Width - ButtonSize - 4, (layout.ContainerControl.Height - ButtonSize) * 0.5f),
}; };
button.Clicked += () => button.Clicked += () => SetValue(Values.Type.CreateInstance());
{
var newType = Values.Type;
SetValue(newType.CreateInstance());
RebuildLayoutOnRefresh();
};
} }
layout.Label("<null>"); layout.Label("<null>");
@@ -558,6 +562,13 @@ namespace FlaxEditor.CustomEditors.Editors
/// <inheritdoc /> /// <inheritdoc />
public override void Refresh() public override void Refresh()
{ {
// Automatic refresh when value nullability changed
if (_isNull != Values.IsNull)
{
RebuildLayout();
return;
}
if (_visibleIfCaches != null) if (_visibleIfCaches != null)
{ {
try try
@@ -76,14 +76,7 @@ namespace FlaxEditor.CustomEditors.Editors
if (_value) if (_value)
{ {
_valueName = _value.Name; _valueName = _value.Name;
TooltipText = _value.TypeName; TooltipText = Surface.SurfaceUtils.GetVisualScriptTypeDescription(_value);
var attributes = _value.GetAttributes(false);
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
if (tooltipAttribute != null)
{
TooltipText += "\n" + tooltipAttribute.Text;
}
} }
else else
{ {
@@ -30,6 +30,11 @@ namespace FlaxEditor.CustomEditors
/// </summary> /// </summary>
public readonly List<LayoutElement> Children = new List<LayoutElement>(); public readonly List<LayoutElement> Children = new List<LayoutElement>();
/// <summary>
/// The child custom editors.
/// </summary>
public readonly List<CustomEditor> Editors = new List<CustomEditor>();
/// <summary> /// <summary>
/// Gets the control represented by this element. /// Gets the control represented by this element.
/// </summary> /// </summary>
@@ -722,6 +727,7 @@ namespace FlaxEditor.CustomEditors
var customEditor = CustomEditor.CurrentCustomEditor; var customEditor = CustomEditor.CurrentCustomEditor;
Assert.IsNotNull(customEditor); Assert.IsNotNull(customEditor);
customEditor.OnChildCreated(editor); customEditor.OnChildCreated(editor);
Editors.Add(editor);
} }
/// <summary> /// <summary>
@@ -730,6 +736,7 @@ namespace FlaxEditor.CustomEditors
public virtual void ClearLayout() public virtual void ClearLayout()
{ {
Children.Clear(); Children.Clear();
Editors.Clear();
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -101,6 +101,22 @@ namespace FlaxEditor.CustomEditors
} }
} }
/// <summary>
/// Gets a value indicating whether all values in the collection are null. Returns true if collection is empty.
/// </summary>
public bool IsNull
{
get
{
for (int i = 0; i < Count; i++)
{
if (this[i] != null)
return false;
}
return true;
}
}
/// <summary> /// <summary>
/// Gets a value indicating whether this any value in the collection is of value type (eg. a structure, not a class type). Returns false if collection is empty. /// Gets a value indicating whether this any value in the collection is of value type (eg. a structure, not a class type). Returns false if collection is empty.
/// </summary> /// </summary>
+99
View File
@@ -388,6 +388,105 @@ int32 Editor::LoadProduct()
projectPath.Clear(); projectPath.Clear();
} }
// Create new project option
if (CommandLine::Options.NewProject)
{
if (projectPath.IsEmpty())
projectPath = Platform::GetWorkingDirectory();
else if (!FileSystem::DirectoryExists(projectPath))
FileSystem::CreateDirectory(projectPath);
FileSystem::NormalizePath(projectPath);
String folderName = StringUtils::GetFileName(projectPath);
String tmpName;
for (int32 i = 0; i < folderName.Length(); i++)
{
Char c = folderName[i];
if (StringUtils::IsAlnum(c) && c != ' ' && c != '.')
tmpName += c;
}
// Create project file
ProjectInfo newProject;
newProject.Name = MoveTemp(tmpName);
newProject.ProjectPath = projectPath / newProject.Name + TEXT(".flaxproj");
newProject.ProjectFolderPath = projectPath;
newProject.Version = Version(1, 0);
newProject.Company = TEXT("My Company");
newProject.MinEngineVersion = FLAXENGINE_VERSION;
newProject.GameTarget = TEXT("GameTarget");
newProject.EditorTarget = TEXT("GameEditorTarget");
auto& flaxRef = newProject.References.AddOne();
flaxRef.Name = TEXT("$(EnginePath)/Flax.flaxproj");
flaxRef.Project = nullptr;
if (newProject.SaveProject())
return 10;
// Generate source files
if (FileSystem::CreateDirectory(projectPath / TEXT("Content")))
return 11;
if (FileSystem::CreateDirectory(projectPath / TEXT("Source/Game")))
return 11;
bool failed = File::WriteAllText(projectPath / TEXT("Source/GameTarget.Build.cs"),TEXT(
"using Flax.Build;\n"
"\n"
"public class GameTarget : GameProjectTarget\n"
"{\n"
" /// <inheritdoc />\n"
" public override void Init()\n"
" {\n"
" base.Init();\n"
"\n"
" // Reference the modules for game\n"
" Modules.Add(\"Game\");\n"
" }\n"
"}\n"), Encoding::Unicode);
failed |= File::WriteAllText(projectPath / TEXT("Source/GameEditorTarget.Build.cs"),TEXT(
"using Flax.Build;\n"
"\n"
"public class GameEditorTarget : GameProjectEditorTarget\n"
"{\n"
" /// <inheritdoc />\n"
" public override void Init()\n"
" {\n"
" base.Init();\n"
"\n"
" // Reference the modules for editor\n"
" Modules.Add(\"Game\");\n"
" }\n"
"}\n"), Encoding::Unicode);
failed |= File::WriteAllText(projectPath / TEXT("Source/Game/Game.Build.cs"),TEXT(
"using Flax.Build;\n"
"using Flax.Build.NativeCpp;\n"
"\n"
"public class Game : GameModule\n"
"{\n"
" /// <inheritdoc />\n"
" public override void Init()\n"
" {\n"
" base.Init();\n"
"\n"
" // C#-only scripting\n"
" BuildNativeCode = false;\n"
" }\n"
"\n"
" /// <inheritdoc />\n"
" public override void Setup(BuildOptions options)\n"
" {\n"
" base.Setup(options);\n"
"\n"
" options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n"
"\n"
" // Here you can modify the build options for your game module\n"
" // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n"
" // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n"
" // To learn more see scripting documentation.\n"
" }\n"
"}\n"), Encoding::Unicode);
if (failed)
return 12;
}
// Missing project case // Missing project case
if (projectPath.IsEmpty()) if (projectPath.IsEmpty())
{ {
+31 -2
View File
@@ -272,8 +272,10 @@ namespace FlaxEditor
module.OnEndInit(); module.OnEndInit();
} }
internal void Init(bool isHeadless, bool skipCompile, Guid startupScene) internal void Init(bool isHeadless, bool skipCompile, bool newProject, Guid startupScene)
{ {
if (newProject)
InitProject();
EnsureState<LoadingState>(); EnsureState<LoadingState>();
_isHeadlessMode = isHeadless; _isHeadlessMode = isHeadless;
_startupSceneCmdLine = startupScene; _startupSceneCmdLine = startupScene;
@@ -473,6 +475,33 @@ namespace FlaxEditor
} }
} }
private void InitProject()
{
// Initialize empty project with default game configuration
Log("Initialize new project");
var project = GameProject;
var gameSettings = new GameSettings
{
ProductName = project.Name,
CompanyName = project.Company,
};
GameSettings.Save(gameSettings);
GameSettings.Save(new TimeSettings());
GameSettings.Save(new AudioSettings());
GameSettings.Save(new PhysicsSettings());
GameSettings.Save(new LayersAndTagsSettings());
GameSettings.Save(new InputSettings());
GameSettings.Save(new GraphicsSettings());
GameSettings.Save(new NavigationSettings());
GameSettings.Save(new LocalizationSettings());
GameSettings.Save(new BuildSettings());
GameSettings.Save(new StreamingSettings());
GameSettings.Save(new WindowsPlatformSettings());
GameSettings.Save(new LinuxPlatformSettings());
GameSettings.Save(new AndroidPlatformSettings());
GameSettings.Save(new UWPPlatformSettings());
}
internal void OnPlayBeginning() internal void OnPlayBeginning()
{ {
for (int i = 0; i < _modules.Count; i++) for (int i = 0; i < _modules.Count; i++)
@@ -1189,7 +1218,7 @@ namespace FlaxEditor
return true; return true;
} }
Windows.GameCookerWin.Build(target); Windows.GameCookerWin.Build(preset, target);
} }
Windows.GameCookerWin.ExitOnBuildQueueEnd(); Windows.GameCookerWin.ExitOnBuildQueueEnd();
+15 -3
View File
@@ -456,18 +456,30 @@ namespace FlaxEditor.GUI.ContextMenu
if (base.OnKeyDown(key)) if (base.OnKeyDown(key))
return true; return true;
// Se;ect the first item switch (key)
if (key == KeyboardKeys.ArrowDown)
{ {
case KeyboardKeys.ArrowDown:
for (int i = 0; i < _panel.Children.Count; i++) for (int i = 0; i < _panel.Children.Count; i++)
{ {
if (_panel.Children[i] is ContextMenuButton item && item.Visible) if (_panel.Children[i] is ContextMenuButton item && item.Visible && item.Enabled)
{ {
item.Focus(); item.Focus();
_panel.ScrollViewTo(item); _panel.ScrollViewTo(item);
return true; return true;
} }
} }
break;
case KeyboardKeys.ArrowUp:
for (int i = _panel.Children.Count - 1; i >= 0; i--)
{
if (_panel.Children[i] is ContextMenuButton item && item.Visible && item.Enabled)
{
item.Focus();
_panel.ScrollViewTo(item);
return true;
}
}
break;
} }
return false; return false;
@@ -183,7 +183,7 @@ namespace FlaxEditor.GUI.ContextMenu
case KeyboardKeys.ArrowUp: case KeyboardKeys.ArrowUp:
for (int i = IndexInParent - 1; i >= 0; i--) for (int i = IndexInParent - 1; i >= 0; i--)
{ {
if (ParentContextMenu.ItemsContainer.Children[i] is ContextMenuButton item && item.Visible) if (ParentContextMenu.ItemsContainer.Children[i] is ContextMenuButton item && item.Visible && item.Enabled)
{ {
item.Focus(); item.Focus();
ParentContextMenu.ItemsContainer.ScrollViewTo(item); ParentContextMenu.ItemsContainer.ScrollViewTo(item);
@@ -194,7 +194,7 @@ namespace FlaxEditor.GUI.ContextMenu
case KeyboardKeys.ArrowDown: case KeyboardKeys.ArrowDown:
for (int i = IndexInParent + 1; i < ParentContextMenu.ItemsContainer.Children.Count; i++) for (int i = IndexInParent + 1; i < ParentContextMenu.ItemsContainer.Children.Count; i++)
{ {
if (ParentContextMenu.ItemsContainer.Children[i] is ContextMenuButton item && item.Visible) if (ParentContextMenu.ItemsContainer.Children[i] is ContextMenuButton item && item.Visible && item.Enabled)
{ {
item.Focus(); item.Focus();
ParentContextMenu.ItemsContainer.ScrollViewTo(item); ParentContextMenu.ItemsContainer.ScrollViewTo(item);
+16 -24
View File
@@ -74,23 +74,21 @@ namespace FlaxEditor.GUI.Timeline.GUI
} }
// Setup time axis ticks // Setup time axis ticks
int minDistanceBetweenTicks = 4000; var minDistanceBetweenTicks = 50.0f;
int maxDistanceBetweenTicks = 6000; var maxDistanceBetweenTicks = 100.0f;
var zoom = Timeline.UnitsPerSecond * _timeline.Zoom; var zoom = Timeline.UnitsPerSecond * _timeline.Zoom;
var left = Vector2.Min(leftSideMin, rightSideMax).X; var left = Vector2.Min(leftSideMin, rightSideMax).X;
var right = Vector2.Max(leftSideMin, rightSideMax).X; var right = Vector2.Max(leftSideMin, rightSideMax).X;
var pixelRange = (right - left) * zoom;
var leftFrame = Mathf.Floor((left - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond; var leftFrame = Mathf.Floor((left - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond;
var rightFrame = Mathf.Ceil((right - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond; var rightFrame = Mathf.Ceil((right - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond;
var min = leftFrame; var min = leftFrame;
var max = rightFrame; var max = rightFrame;
var range = max - min;
int smallestTick = 0; int smallestTick = 0;
int biggestTick = _tickSteps.Length - 1; int biggestTick = _tickSteps.Length - 1;
for (int i = _tickSteps.Length - 1; i >= 0; i--) for (int i = _tickSteps.Length - 1; i >= 0; i--)
{ {
// Calculate how far apart these modulo tick steps are spaced // Calculate how far apart these modulo tick steps are spaced
float tickSpacing = _tickSteps[i] * pixelRange / range; float tickSpacing = _tickSteps[i] * _timeline.Zoom;
// Calculate the strength of the tick markers based on the spacing // Calculate the strength of the tick markers based on the spacing
_tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks)); _tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks));
@@ -117,14 +115,16 @@ namespace FlaxEditor.GUI.Timeline.GUI
// Draw all ticks // Draw all ticks
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1); int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
int startTick = Mathf.FloorToInt(min / _tickSteps[l]); var lStep = _tickSteps[l];
int endTick = Mathf.CeilToInt(max / _tickSteps[l]); var lNextStep = _tickSteps[l + 1];
int startTick = Mathf.FloorToInt(min / lStep);
int endTick = Mathf.CeilToInt(max / lStep);
Color lineColor = style.ForegroundDisabled.RGBMultiplied(0.7f).AlphaMultiplied(strength); Color lineColor = style.ForegroundDisabled.RGBMultiplied(0.7f).AlphaMultiplied(strength);
for (int i = startTick; i <= endTick; i++) for (int i = startTick; i <= endTick; i++)
{ {
if (l < biggestTick && (i % Mathf.RoundToInt(_tickSteps[l + 1] / _tickSteps[l]) == 0)) if (l < biggestTick && (i % Mathf.RoundToInt(lNextStep / lStep) == 0))
continue; continue;
var tick = i * _tickSteps[l]; var tick = i * lStep;
var time = tick / _timeline.FramesPerSecond; var time = tick / _timeline.FramesPerSecond;
var x = time * zoom + Timeline.StartOffset; var x = time * zoom + Timeline.StartOffset;
@@ -163,15 +163,17 @@ namespace FlaxEditor.GUI.Timeline.GUI
// Draw all ticks // Draw all ticks
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1); int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
int startTick = Mathf.FloorToInt(min / _tickSteps[l]); var lStep = _tickSteps[l];
int endTick = Mathf.CeilToInt(max / _tickSteps[l]); var lNextStep = _tickSteps[l + 1];
int startTick = Mathf.FloorToInt(min / lStep);
int endTick = Mathf.CeilToInt(max / lStep);
Color lineColor = style.Foreground.RGBMultiplied(0.8f).AlphaMultiplied(strength); Color lineColor = style.Foreground.RGBMultiplied(0.8f).AlphaMultiplied(strength);
Color labelColor = style.ForegroundDisabled.AlphaMultiplied(strength); Color labelColor = style.ForegroundDisabled.AlphaMultiplied(strength);
for (int i = startTick; i <= endTick; i++) for (int i = startTick; i <= endTick; i++)
{ {
if (l < biggestTick && (i % Mathf.RoundToInt(_tickSteps[l + 1] / _tickSteps[l]) == 0)) if (l < biggestTick && (i % Mathf.RoundToInt(lNextStep / lStep) == 0))
continue; continue;
var tick = i * _tickSteps[l]; var tick = i * lStep;
var time = tick / _timeline.FramesPerSecond; var time = tick / _timeline.FramesPerSecond;
var x = time * zoom + Timeline.StartOffset; var x = time * zoom + Timeline.StartOffset;
@@ -195,17 +197,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
default: throw new ArgumentOutOfRangeException(); default: throw new ArgumentOutOfRangeException();
} }
var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend); var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend);
Render2D.DrawText( Render2D.DrawText(style.FontSmall, labelText, labelRect, labelColor, TextAlignment.Near, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.8f);
style.FontSmall,
labelText,
labelRect,
labelColor,
TextAlignment.Near,
TextAlignment.Center,
TextWrapping.NoWrap,
1.0f,
0.8f
);
} }
} }
} }
@@ -0,0 +1,95 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Timeline.GUI
{
/// <summary>
/// The timeline background area control.
/// </summary>
class BackgroundArea : Panel
{
private Timeline _timeline;
internal bool _rightMouseButtonDown;
private Vector2 _rightMouseButtonLastPos;
private float _rightMouseButtonMovement;
public BackgroundArea(Timeline timeline)
: base(ScrollBars.Both)
{
_timeline = timeline;
}
/// <inheritdoc />
public override bool OnMouseDown(Vector2 location, MouseButton button)
{
if (base.OnMouseDown(location, button))
return true;
if (button == MouseButton.Right)
{
_rightMouseButtonDown = true;
_rightMouseButtonLastPos = location;
_rightMouseButtonMovement = 0;
Focus();
StartMouseCapture();
return true;
}
return false;
}
/// <inheritdoc />
public override void OnMouseMove(Vector2 location)
{
// Panning timeline view with a right-mouse button
if (_rightMouseButtonDown)
{
var movePos = location + ViewOffset;
var delta = _rightMouseButtonLastPos - movePos;
_rightMouseButtonLastPos = movePos;
_rightMouseButtonMovement += delta.Length;
var hScroll = HScrollBar.Visible && HScrollBar.Enabled;
var vScroll = VScrollBar.Visible && VScrollBar.Enabled;
if (vScroll && hScroll)
Cursor = CursorType.SizeAll;
else if (vScroll)
Cursor = CursorType.SizeNS;
else if (hScroll)
Cursor = CursorType.SizeWE;
bool wasLocked = IsLayoutLocked;
IsLayoutLocked = true;
if (hScroll)
HScrollBar.TargetValue += delta.X;
if (vScroll)
VScrollBar.TargetValue += delta.Y;
IsLayoutLocked = wasLocked;
PerformLayout();
return;
}
base.OnMouseMove(location);
}
/// <inheritdoc />
public override bool OnMouseUp(Vector2 location, MouseButton button)
{
if (button == MouseButton.Right && _rightMouseButtonDown)
{
EndMouseCapture();
_rightMouseButtonDown = false;
Cursor = CursorType.Default;
if (_rightMouseButtonMovement < 1.0f)
{
_timeline.ShowContextMenu(PointToParent(_timeline, location));
}
return true;
}
return base.OnMouseUp(location, button);
}
}
}
@@ -27,7 +27,8 @@ namespace FlaxEditor.GUI.Timeline.GUI
{ {
var style = Style.Current; var style = Style.Current;
var icon = Editor.Instance.Icons.VisjectArrowClosed32; var icon = Editor.Instance.Icons.VisjectArrowClosed32;
var timeAxisHeaderOffset = -_timeline.MediaBackground.ViewOffset.Y; var timeAxisOverlap = Timeline.HeaderTopAreaHeight * 0.5f;
var timeAxisHeaderOffset = -_timeline.MediaBackground.ViewOffset.Y - timeAxisOverlap;
Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1); Matrix3x3.RotationZ(Mathf.PiOverTwo, out var m1);
var m2 = Matrix3x3.Translation2D(0, timeAxisHeaderOffset); var m2 = Matrix3x3.Translation2D(0, timeAxisHeaderOffset);
@@ -36,7 +37,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
Render2D.DrawSprite(icon, new Rectangle(new Vector2(4, -Width), Size), _timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground); Render2D.DrawSprite(icon, new Rectangle(new Vector2(4, -Width), Size), _timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground);
Render2D.PopTransform(); Render2D.PopTransform();
Render2D.FillRectangle(new Rectangle(Width * 0.5f, Height + timeAxisHeaderOffset, 1, _timeline.MediaPanel.Height - timeAxisHeaderOffset), _timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground.RGBMultiplied(0.8f)); Render2D.FillRectangle(new Rectangle(Width * 0.5f, Height + timeAxisHeaderOffset, 1, _timeline.MediaPanel.Height - timeAxisHeaderOffset - timeAxisOverlap), _timeline.IsMovingPositionHandle ? style.ProgressNormal : style.Foreground.RGBMultiplied(0.8f));
base.Draw(); base.Draw();
} }
@@ -37,7 +37,8 @@ namespace FlaxEditor.GUI.Timeline.GUI
public override void Draw() public override void Draw()
{ {
var style = Style.Current; var style = Style.Current;
var timeAxisHeaderOffset = -_timeline.MediaBackground.ViewOffset.Y; var timeAxisOverlap = Timeline.HeaderTopAreaHeight * 0.5f;
var timeAxisHeaderOffset = -_timeline.MediaBackground.ViewOffset.Y - timeAxisOverlap;
var moveColor = style.ProgressNormal; var moveColor = style.ProgressNormal;
var thickness = 2.0f; var thickness = 2.0f;
+53 -43
View File
@@ -288,7 +288,7 @@ namespace FlaxEditor.GUI.Timeline
private TimeIntervalsHeader _timeIntervalsHeader; private TimeIntervalsHeader _timeIntervalsHeader;
private ContainerControl _backgroundScroll; private ContainerControl _backgroundScroll;
private Background _background; private Background _background;
private Panel _backgroundArea; private BackgroundArea _backgroundArea;
private TimelineEdge _leftEdge; private TimelineEdge _leftEdge;
private TimelineEdge _rightEdge; private TimelineEdge _rightEdge;
private Button _addTrackButton; private Button _addTrackButton;
@@ -305,6 +305,7 @@ namespace FlaxEditor.GUI.Timeline
private PositionHandle _positionHandle; private PositionHandle _positionHandle;
private bool _isRightMouseButtonDown; private bool _isRightMouseButtonDown;
private Vector2 _rightMouseButtonDownPos; private Vector2 _rightMouseButtonDownPos;
private Vector2 _rightMouseButtonMovePos;
private float _zoom = 1.0f; private float _zoom = 1.0f;
private bool _isMovingPositionHandle; private bool _isMovingPositionHandle;
private bool _canPlayPauseStop = true; private bool _canPlayPauseStop = true;
@@ -579,7 +580,7 @@ namespace FlaxEditor.GUI.Timeline
get => _zoom; get => _zoom;
set set
{ {
value = Mathf.Clamp(value, 0.0001f, 1000.0f); value = Mathf.Clamp(value, 0.00001f, 1000.0f);
if (Mathf.NearEqual(_zoom, value)) if (Mathf.NearEqual(_zoom, value))
return; return;
@@ -912,7 +913,7 @@ namespace FlaxEditor.GUI.Timeline
Offsets = new Margin(0, 0, 0, HeaderTopAreaHeight), Offsets = new Margin(0, 0, 0, HeaderTopAreaHeight),
Parent = _splitter.Panel2 Parent = _splitter.Panel2
}; };
_backgroundArea = new Panel(ScrollBars.Both) _backgroundArea = new BackgroundArea(this)
{ {
AutoFocus = false, AutoFocus = false,
ClipChildren = false, ClipChildren = false,
@@ -958,12 +959,12 @@ namespace FlaxEditor.GUI.Timeline
private void UpdatePositionHandle() private void UpdatePositionHandle()
{ {
var handleWidth = 12.0f; var handleWidth = 12.0f;
_positionHandle.Bounds = new Rectangle( var bounds = new Rectangle();
StartOffset * 2.0f - handleWidth * 0.5f + _currentFrame / _framesPerSecond * UnitsPerSecond * Zoom, bounds.Location.X = StartOffset * 2.0f - handleWidth * 0.5f + _currentFrame / _framesPerSecond * UnitsPerSecond * Zoom;
HeaderTopAreaHeight * -0.5f, bounds.Location.Y = 0;
handleWidth, bounds.Size.X = handleWidth;
HeaderTopAreaHeight * 0.5f bounds.Size.Y = HeaderTopAreaHeight * 0.5f;
); _positionHandle.Bounds = bounds;
} }
private void OnFpsPopupShowing(ComboBox comboBox) private void OnFpsPopupShowing(ComboBox comboBox)
@@ -1039,6 +1040,13 @@ namespace FlaxEditor.GUI.Timeline
menu.AddButton("Show preview values", () => ShowPreviewValues = !ShowPreviewValues).Checked = ShowPreviewValues; menu.AddButton("Show preview values", () => ShowPreviewValues = !ShowPreviewValues).Checked = ShowPreviewValues;
{
var zoom = menu.AddButton("Zoom");
var zoomValue = new FloatValueBox(Zoom, 140, 2, 50.0f, 0.00001f, 1000.0f, 0.001f);
zoomValue.Parent = zoom;
zoomValue.ValueChanged += () => Zoom = zoomValue.Value;
}
OnShowViewContextMenu(menu); OnShowViewContextMenu(menu);
menu.Show(_viewButton.Parent, _viewButton.BottomLeft); menu.Show(_viewButton.Parent, _viewButton.BottomLeft);
@@ -1935,8 +1943,8 @@ namespace FlaxEditor.GUI.Timeline
_background.Bounds = new Rectangle(StartOffset, 0, Duration * UnitsPerSecond * Zoom, height); _background.Bounds = new Rectangle(StartOffset, 0, Duration * UnitsPerSecond * Zoom, height);
var edgeWidth = 6.0f; var edgeWidth = 6.0f;
_leftEdge.Bounds = new Rectangle(_background.Left - edgeWidth * 0.5f + StartOffset, HeaderTopAreaHeight * -0.5f, edgeWidth, height + HeaderTopAreaHeight * 0.5f); _leftEdge.Bounds = new Rectangle(_background.Left - edgeWidth * 0.5f + StartOffset, 0, edgeWidth, height);
_rightEdge.Bounds = new Rectangle(_background.Right - edgeWidth * 0.5f + StartOffset, HeaderTopAreaHeight * -0.5f, edgeWidth, height + HeaderTopAreaHeight * 0.5f); _rightEdge.Bounds = new Rectangle(_background.Right - edgeWidth * 0.5f + StartOffset, 0, edgeWidth, height);
_backgroundScroll.Bounds = new Rectangle(0, 0, _background.Width + 5 * StartOffset, height); _backgroundScroll.Bounds = new Rectangle(0, 0, _background.Width + 5 * StartOffset, height);
} }
@@ -1954,6 +1962,38 @@ namespace FlaxEditor.GUI.Timeline
} }
} }
internal void ShowContextMenu(Vector2 location)
{
if (!ContainsFocus)
Focus();
var controlUnderMouse = GetChildAtRecursive(location);
var mediaUnderMouse = controlUnderMouse;
while (mediaUnderMouse != null && !(mediaUnderMouse is Media))
{
mediaUnderMouse = mediaUnderMouse.Parent;
}
var menu = new ContextMenu.ContextMenu();
if (mediaUnderMouse is Media media)
{
media.OnTimelineShowContextMenu(menu, controlUnderMouse);
if (media.PropertiesEditObject != null)
{
menu.AddButton("Edit media", () => ShowEditPopup(media.PropertiesEditObject, ref location, media.Track));
}
}
if (PropertiesEditObject != null)
{
menu.AddButton("Edit timeline", () => ShowEditPopup(PropertiesEditObject, ref location, this));
}
menu.AddSeparator();
menu.AddButton("Reset zoom", () => Zoom = 1.0f);
menu.AddButton("Show whole timeline", ShowWholeTimeline);
OnShowContextMenu(menu);
menu.Show(this, location);
}
/// <inheritdoc /> /// <inheritdoc />
protected override void PerformLayoutBeforeChildren() protected override void PerformLayoutBeforeChildren()
{ {
@@ -1986,8 +2026,8 @@ namespace FlaxEditor.GUI.Timeline
{ {
_isRightMouseButtonDown = true; _isRightMouseButtonDown = true;
_rightMouseButtonDownPos = location; _rightMouseButtonDownPos = location;
_rightMouseButtonMovePos = location;
Focus(); Focus();
return true; return true;
} }
@@ -2006,38 +2046,8 @@ namespace FlaxEditor.GUI.Timeline
if (button == MouseButton.Right && _isRightMouseButtonDown) if (button == MouseButton.Right && _isRightMouseButtonDown)
{ {
_isRightMouseButtonDown = false; _isRightMouseButtonDown = false;
if (Vector2.Distance(ref location, ref _rightMouseButtonDownPos) < 4.0f) if (Vector2.Distance(ref location, ref _rightMouseButtonDownPos) < 4.0f)
{ ShowContextMenu(location);
if (!ContainsFocus)
Focus();
var controlUnderMouse = GetChildAtRecursive(location);
var mediaUnderMouse = controlUnderMouse;
while (mediaUnderMouse != null && !(mediaUnderMouse is Media))
{
mediaUnderMouse = mediaUnderMouse.Parent;
}
var menu = new ContextMenu.ContextMenu();
if (mediaUnderMouse is Media media)
{
media.OnTimelineShowContextMenu(menu, controlUnderMouse);
if (media.PropertiesEditObject != null)
{
menu.AddButton("Edit media", () => ShowEditPopup(media.PropertiesEditObject, ref location, media.Track));
}
}
if (PropertiesEditObject != null)
{
menu.AddButton("Edit timeline", () => ShowEditPopup(PropertiesEditObject, ref location, this));
}
menu.AddSeparator();
menu.AddButton("Reset zoom", () => Zoom = 1.0f);
menu.AddButton("Show whole timeline", ShowWholeTimeline);
OnShowContextMenu(menu);
menu.Show(this, location);
}
} }
return base.OnMouseUp(location, button); return base.OnMouseUp(location, button);
@@ -103,7 +103,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
{ {
if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.Actor && IsActorValid(actorNode.Actor)) if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.Actor && IsActorValid(actorNode.Actor))
{ {
menu.AddButton("Select " + actorNode.Actor, OnClickedSelectActor); menu.AddButton("Select " + actorNode.Actor, OnClickedSelectActor).TooltipText = Utilities.Utils.GetTooltip(actorNode.Actor);
} }
else else
{ {
@@ -7,6 +7,7 @@ using System.Reflection;
using System.Text; using System.Text;
using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors;
using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Scripting;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
@@ -197,8 +198,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks
AddMemberTag tag; AddMemberTag tag;
tag.Member = m; tag.Member = m;
tag.Archetype = EventTrack.GetArchetype(); tag.Archetype = EventTrack.GetArchetype();
var tooltip = Surface.SurfaceUtils.GetVisualScriptMemberInfoDescription(new ScriptMemberInfo(m));
menu.AddButton(sb.ToString(), OnAddMemberTrack).Tag = tag; menu.AddButton(sb.ToString(), OnAddMemberTrack).LinkTooltip(tooltip).Tag = tag;
count++; count++;
} }
@@ -295,7 +296,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks
AddMemberTag tag; AddMemberTag tag;
tag.Member = m; tag.Member = m;
tag.Archetype = archetype; tag.Archetype = archetype;
menu.AddButton(name + " " + m.Name, OnAddMemberTrack).Tag = tag; var tooltip = Surface.SurfaceUtils.GetVisualScriptMemberInfoDescription(new ScriptMemberInfo(m));
menu.AddButton(name + " " + m.Name, OnAddMemberTrack).LinkTooltip(tooltip).Tag = tag;
count++; count++;
} }
+7
View File
@@ -80,6 +80,13 @@ namespace FlaxEditor.Gizmo
{ {
} }
/// <summary>
/// Performs scene objects snapping to the ground.
/// </summary>
public virtual void SnapToGround()
{
}
/// <summary> /// <summary>
/// Draws the gizmo. /// Draws the gizmo.
/// </summary> /// </summary>
+5
View File
@@ -89,5 +89,10 @@ namespace FlaxEditor.Gizmo
/// Gets a <see cref="FlaxEditor.Undo"/> object used by the gizmo owner. /// Gets a <see cref="FlaxEditor.Undo"/> object used by the gizmo owner.
/// </summary> /// </summary>
Undo Undo { get; } Undo Undo { get; }
/// <summary>
/// Gets the root tree node for the scene graph.
/// </summary>
SceneGraph.RootNode SceneGraphRoot { get; }
} }
} }
+6
View File
@@ -218,6 +218,12 @@ namespace FlaxEditor.Gizmo
} }
} }
/// <inheritdoc />
protected override bool IsSelected(SceneGraphNode obj)
{
return _selection.Contains(obj);
}
/// <inheritdoc /> /// <inheritdoc />
protected override void OnApplyTransformation(ref Vector3 translationDelta, ref Quaternion rotationDelta, ref Vector3 scaleDelta) protected override void OnApplyTransformation(ref Vector3 translationDelta, ref Quaternion rotationDelta, ref Vector3 scaleDelta)
{ {
+42 -9
View File
@@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using FlaxEditor.SceneGraph;
using FlaxEngine; using FlaxEngine;
namespace FlaxEditor.Gizmo namespace FlaxEditor.Gizmo
@@ -398,15 +399,7 @@ namespace FlaxEditor.Gizmo
// Snap to ground // Snap to ground
if (_activeAxis == Axis.None && SelectionCount != 0 && Owner.SnapToGround) if (_activeAxis == Axis.None && SelectionCount != 0 && Owner.SnapToGround)
{ {
if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, uint.MaxValue, false)) SnapToGround();
{
StartTransforming();
var translationDelta = hit.Point - Position;
var rotationDelta = Quaternion.Identity;
var scaleDelta = Vector3.Zero;
OnApplyTransformation(ref translationDelta, ref rotationDelta, ref scaleDelta);
EndTransforming();
}
} }
// Only when is active // Only when is active
else if (_isActive) else if (_isActive)
@@ -517,6 +510,39 @@ namespace FlaxEditor.Gizmo
UpdateMatrices(); UpdateMatrices();
} }
/// <inheritdoc />
public override void SnapToGround()
{
if (Owner.SceneGraphRoot == null)
return;
var ray = new Ray(Position, Vector3.Down);
while (true)
{
var view = new Ray(Owner.ViewPosition, Owner.ViewDirection);
var rayCastFlags = SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives;
var hit = Owner.SceneGraphRoot.RayCast(ref ray, ref view, out var distance, out _, rayCastFlags);
if (hit != null)
{
// Skip snapping selection to itself
if (IsSelected(hit))
{
GetSelectedObjectsBounds(out var selectionBounds, out _);
ray.Position = ray.GetPoint(selectionBounds.Size.Y * 0.5f);
continue;
}
// Snap
StartTransforming();
var translationDelta = ray.GetPoint(distance) - Position;
var rotationDelta = Quaternion.Identity;
var scaleDelta = Vector3.Zero;
OnApplyTransformation(ref translationDelta, ref rotationDelta, ref scaleDelta);
EndTransforming();
}
break;
}
}
/// <summary> /// <summary>
/// Gets a value indicating whether this tool can transform objects. /// Gets a value indicating whether this tool can transform objects.
/// </summary> /// </summary>
@@ -545,6 +571,13 @@ namespace FlaxEditor.Gizmo
/// <param name="navigationDirty">True if editing the selected objects transformations marks the navigation system area dirty (for auto-rebuild), otherwise skip update.</param> /// <param name="navigationDirty">True if editing the selected objects transformations marks the navigation system area dirty (for auto-rebuild), otherwise skip update.</param>
protected abstract void GetSelectedObjectsBounds(out BoundingBox bounds, out bool navigationDirty); protected abstract void GetSelectedObjectsBounds(out BoundingBox bounds, out bool navigationDirty);
/// <summary>
/// Checks if the specified object is selected.
/// </summary>
/// <param name="obj">The object to check.</param>
/// <returns>True if it's selected, otherwise false.</returns>
protected abstract bool IsSelected(SceneGraphNode obj);
/// <summary> /// <summary>
/// Called when user starts transforming selected objects. /// Called when user starts transforming selected objects.
/// </summary> /// </summary>
+4 -2
View File
@@ -175,7 +175,7 @@ ManagedEditor::~ManagedEditor()
void ManagedEditor::Init() void ManagedEditor::Init()
{ {
// Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time // Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time
void* args[3]; void* args[4];
MClass* mclass = GetClass(); MClass* mclass = GetClass();
if (mclass == nullptr) if (mclass == nullptr)
{ {
@@ -194,14 +194,16 @@ void ManagedEditor::Init()
MonoObject* exception = nullptr; MonoObject* exception = nullptr;
bool isHeadless = CommandLine::Options.Headless.IsTrue(); bool isHeadless = CommandLine::Options.Headless.IsTrue();
bool skipCompile = CommandLine::Options.SkipCompile.IsTrue(); bool skipCompile = CommandLine::Options.SkipCompile.IsTrue();
bool newProject = CommandLine::Options.NewProject.IsTrue();
args[0] = &isHeadless; args[0] = &isHeadless;
args[1] = &skipCompile; args[1] = &skipCompile;
args[2] = &newProject;
Guid sceneId; Guid sceneId;
if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId))) if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId)))
{ {
sceneId = Guid::Empty; sceneId = Guid::Empty;
} }
args[2] = &sceneId; args[3] = &sceneId;
initMethod->Invoke(instance, args, &exception); initMethod->Invoke(instance, args, &exception);
if (exception) if (exception)
{ {
+15 -2
View File
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FlaxEditor.Actions; using FlaxEditor.Actions;
using FlaxEditor.Content; using FlaxEditor.Content;
@@ -49,8 +50,20 @@ namespace FlaxEditor.Modules
/// </remarks> /// </remarks>
public void CreatePrefab() public void CreatePrefab()
{ {
// Check selection CreatePrefab(Editor.SceneEditing.Selection);
var selection = Editor.SceneEditing.Selection; }
/// <summary>
/// Starts the creating prefab for the selected actor by showing the new item creation dialog in <see cref="ContentWindow"/>.
/// </summary>
/// <remarks>
/// To create prefab manually (from code) use <see cref="PrefabManager.CreatePrefab"/> method.
/// </remarks>
/// <param name="selection">The scene selection to use.</param>
public void CreatePrefab(List<SceneGraphNode> selection)
{
if (selection == null)
selection = Editor.SceneEditing.Selection;
if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.CanCreatePrefab) if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.CanCreatePrefab)
{ {
CreatePrefab(actorNode.Actor); CreatePrefab(actorNode.Actor);
+25 -27
View File
@@ -216,7 +216,7 @@ namespace FlaxEditor.Modules
} }
} }
private void OnDirty(IEnumerable<SceneGraphNode> objects) private void OnDirty(List<SceneGraphNode> objects)
{ {
var options = Editor.Options.Options; var options = Editor.Options.Options;
var isPlayMode = Editor.StateMachine.IsPlayMode; var isPlayMode = Editor.StateMachine.IsPlayMode;
@@ -236,7 +236,7 @@ namespace FlaxEditor.Modules
{ {
foreach (var obj in objects) foreach (var obj in objects)
{ {
if (obj is ActorNode node && node.Actor.Scene && node.AffectsNavigationWithChildren) if (obj is ActorNode node && node.Actor && node.Actor.Scene && node.AffectsNavigationWithChildren)
{ {
var bounds = node.Actor.BoxWithChildren; var bounds = node.Actor.BoxWithChildren;
Navigation.BuildNavMesh(node.Actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs); Navigation.BuildNavMesh(node.Actor.Scene, bounds, options.General.AutoRebuildNavMeshTimeoutMs);
@@ -303,26 +303,28 @@ namespace FlaxEditor.Modules
{ {
if (!Editor.SceneEditing.HasSthSelected || !(Editor.SceneEditing.Selection[0] is ActorNode)) if (!Editor.SceneEditing.HasSthSelected || !(Editor.SceneEditing.Selection[0] is ActorNode))
return; return;
if (Level.IsAnySceneLoaded == false) if (Level.IsAnySceneLoaded == false)
throw new InvalidOperationException("Cannot spawn actor when no scene is loaded."); throw new InvalidOperationException("Cannot spawn actor when no scene is loaded.");
var actionList = new IUndoAction[4]; var actionList = new IUndoAction[4];
Actor old = ((ActorNode)Editor.SceneEditing.Selection[0]).Actor; var oldNode = (ActorNode)Editor.SceneEditing.Selection[0];
Actor actor = (Actor)FlaxEngine.Object.New(to); var old = oldNode.Actor;
var actor = (Actor)FlaxEngine.Object.New(to);
var parent = old.Parent; var parent = old.Parent;
var orderInParent = old.OrderInParent; var orderInParent = old.OrderInParent;
// Steps:
// - deselect old actor
// - destroy old actor
// - spawn new actor
// - select new actor
SelectionDeleteBegin?.Invoke(); SelectionDeleteBegin?.Invoke();
actionList[0] = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo); actionList[0] = new SelectionChangeAction(Selection.ToArray(), new SceneGraphNode[0], OnSelectionUndo);
actionList[0].Do(); actionList[0].Do();
actionList[1] = new DeleteActorsAction(new List<SceneGraphNode> actionList[1] = new DeleteActorsAction(oldNode.BuildAllNodes().Where(x => x.CanDelete).ToList());
{
Editor.Instance.Scene.GetActorNode(old)
});
actionList[1].Do();
SelectionDeleteEnd?.Invoke(); SelectionDeleteEnd?.Invoke();
@@ -345,17 +347,12 @@ namespace FlaxEditor.Modules
actor.StaticFlags = StaticFlags.None; actor.StaticFlags = StaticFlags.None;
// Move children // Move children
for (var i = old.ScriptsCount - 1; i >= 0; i--) var scripts = old.Scripts;
{ for (var i = scripts.Length - 1; i >= 0; i--)
var script = old.Scripts[i]; scripts[i].Actor = actor;
script.Actor = actor; var children = old.Children;
Guid newid = Guid.NewGuid(); for (var i = children.Length - 1; i >= 0; i--)
FlaxEngine.Object.Internal_ChangeID(FlaxEngine.Object.GetUnmanagedPtr(script), ref newid); children[i].Parent = actor;
}
for (var i = old.Children.Length - 1; i >= 0; i--)
{
old.Children[i].Parent = actor;
}
var actorNode = Editor.Instance.Scene.GetActorNode(actor); var actorNode = Editor.Instance.Scene.GetActorNode(actor);
if (actorNode == null) if (actorNode == null)
@@ -364,16 +361,13 @@ namespace FlaxEditor.Modules
actorNode.PostSpawn(); actorNode.PostSpawn();
Editor.Scene.MarkSceneEdited(actor.Scene); Editor.Scene.MarkSceneEdited(actor.Scene);
actionList[2] = new DeleteActorsAction(new List<SceneGraphNode> actionList[1].Do();
{ actionList[2] = new DeleteActorsAction(actorNode.BuildAllNodes().Where(x => x.CanDelete).ToList(), true);
actorNode
}, true);
actionList[3] = new SelectionChangeAction(new SceneGraphNode[0], new SceneGraphNode[] { actorNode }, OnSelectionUndo); actionList[3] = new SelectionChangeAction(new SceneGraphNode[0], new SceneGraphNode[] { actorNode }, OnSelectionUndo);
actionList[3].Do(); actionList[3].Do();
var actions = new MultiUndoAction(actionList); Undo.AddAction(new MultiUndoAction(actionList, "Convert actor"));
Undo.AddAction(actions);
SpawnEnd?.Invoke(); SpawnEnd?.Invoke();
@@ -389,6 +383,7 @@ namespace FlaxEditor.Modules
var objects = Selection.Where(x => x.CanDelete).ToList().BuildAllNodes().Where(x => x.CanDelete).ToList(); var objects = Selection.Where(x => x.CanDelete).ToList().BuildAllNodes().Where(x => x.CanDelete).ToList();
if (objects.Count == 0) if (objects.Count == 0)
return; return;
var isSceneTreeFocus = Editor.Windows.SceneWin.ContainsFocus;
SelectionDeleteBegin?.Invoke(); SelectionDeleteBegin?.Invoke();
@@ -410,6 +405,9 @@ namespace FlaxEditor.Modules
SelectionDeleteEnd?.Invoke(); SelectionDeleteEnd?.Invoke();
OnDirty(objects); OnDirty(objects);
if (isSceneTreeFocus)
Editor.Windows.SceneWin.Focus();
} }
/// <summary> /// <summary>
+23
View File
@@ -251,6 +251,15 @@ namespace FlaxEditor.Modules
if (!Editor.StateMachine.CurrentState.CanChangeScene) if (!Editor.StateMachine.CurrentState.CanChangeScene)
return; return;
// In play-mode Editor mocks the level streaming script
if (Editor.IsPlayMode)
{
if (!additive)
Level.UnloadAllScenesAsync();
Level.LoadSceneAsync(sceneId);
return;
}
if (!additive) if (!additive)
{ {
// Ensure to save all pending changes // Ensure to save all pending changes
@@ -272,6 +281,13 @@ namespace FlaxEditor.Modules
if (!Editor.StateMachine.CurrentState.CanChangeScene) if (!Editor.StateMachine.CurrentState.CanChangeScene)
return; return;
// In play-mode Editor mocks the level streaming script
if (Editor.IsPlayMode)
{
Level.UnloadSceneAsync(scene);
return;
}
// Ensure to save all pending changes // Ensure to save all pending changes
if (CheckSaveBeforeClose()) if (CheckSaveBeforeClose())
return; return;
@@ -289,6 +305,13 @@ namespace FlaxEditor.Modules
if (!Editor.StateMachine.CurrentState.CanChangeScene) if (!Editor.StateMachine.CurrentState.CanChangeScene)
return; return;
// In play-mode Editor mocks the level streaming script
if (Editor.IsPlayMode)
{
Level.UnloadAllScenesAsync();
return;
}
// Ensure to save all pending changes // Ensure to save all pending changes
if (CheckSaveBeforeClose()) if (CheckSaveBeforeClose())
return; return;
+34 -7
View File
@@ -1,5 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.IO;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.SceneGraph.GUI; using FlaxEditor.SceneGraph.GUI;
using FlaxEngine; using FlaxEngine;
@@ -17,9 +19,6 @@ namespace FlaxEditor.SceneGraph.Actors
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this scene is edited. /// Gets or sets a value indicating whether this scene is edited.
/// </summary> /// </summary>
/// <value>
/// <c>true</c> if this scene is edited; otherwise, <c>false</c>.
/// </value>
public bool IsEdited public bool IsEdited
{ {
get => _isEdited; get => _isEdited;
@@ -28,7 +27,6 @@ namespace FlaxEditor.SceneGraph.Actors
if (_isEdited != value) if (_isEdited != value)
{ {
_isEdited = value; _isEdited = value;
_treeNode.UpdateText(); _treeNode.UpdateText();
} }
} }
@@ -37,9 +35,6 @@ namespace FlaxEditor.SceneGraph.Actors
/// <summary> /// <summary>
/// Gets the scene. /// Gets the scene.
/// </summary> /// </summary>
/// <value>
/// The scene.
/// </value>
public Scene Scene => _actor as Scene; public Scene Scene => _actor as Scene;
/// <summary> /// <summary>
@@ -68,5 +63,37 @@ namespace FlaxEditor.SceneGraph.Actors
/// <inheritdoc /> /// <inheritdoc />
public override SceneNode ParentScene => this; public override SceneNode ParentScene => this;
/// <inheritdoc />
public override void OnContextMenu(ContextMenu contextMenu)
{
contextMenu.AddSeparator();
var path = Scene.Path;
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
var b = contextMenu.AddButton("Show in content window", OnSelect);
b.Icon = Editor.Instance.Icons.Search12;
b.TooltipText = "Finds and selects the scene asset int Content window.";
}
contextMenu.AddButton("Save scene", OnSave).LinkTooltip("Saves this scene.").Enabled = IsEdited && !Editor.IsPlayMode;
contextMenu.AddButton("Unload scene", OnUnload).LinkTooltip("Unloads this scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
base.OnContextMenu(contextMenu);
}
private void OnSelect()
{
Editor.Instance.Windows.ContentWin.Select(Editor.Instance.ContentDatabase.Find(Scene.Path));
}
private void OnSave()
{
Editor.Instance.Scene.SaveScene(this);
}
private void OnUnload()
{
Editor.Instance.Scene.CloseScene(Scene);
}
} }
} }
+16 -2
View File
@@ -134,7 +134,8 @@ namespace FlaxEditor.SceneGraph
try try
{ {
// Try to pick custom node type for that actor object // Try to pick custom node type for that actor object
if (CustomNodesTypes.TryGetValue(actor.GetType(), out customType)) var type = actor.GetType();
if (CustomNodesTypes.TryGetValue(type, out customType))
{ {
// Use custom type // Use custom type
_sharedArgsContainer[0] = actor; _sharedArgsContainer[0] = actor;
@@ -142,8 +143,21 @@ namespace FlaxEditor.SceneGraph
} }
else else
{ {
// Check if the actor type inherits from one of the custom types but doesn't provide own node type
foreach (var e in CustomNodesTypes)
{
if (e.Key.IsAssignableFrom(type))
{
// Use custom type
_sharedArgsContainer[0] = actor;
result = (ActorNode)Activator.CreateInstance(e.Value, _sharedArgsContainer);
break;
}
}
// Use default type for actors // Use default type for actors
result = new ActorNode(actor); if (result == null)
result = new ActorNode(actor);
} }
// Build children // Build children
@@ -125,5 +125,33 @@ namespace FlaxEditor.SceneGraph
FillTree(target, result); FillTree(target, result);
} }
} }
/// <summary>
/// Builds the list made of all nodes in the input scene tree root and child tree. The result collection contains all nodes in the tree.
/// </summary>
/// <param name="node">The node.</param>
/// <returns>The result.</returns>
public static List<SceneGraphNode> BuildAllNodes<T>(this T node)
where T : SceneGraphNode
{
var list = new List<SceneGraphNode>();
BuildAllNodes(node, list);
return list;
}
/// <summary>
/// Builds the list made of all nodes in the input scene tree root and child tree. The result collection contains all nodes in the tree.
/// </summary>
/// <param name="node">The node.</param>
/// <param name="result">The result.</param>
public static void BuildAllNodes<T>(this T node, List<SceneGraphNode> result)
where T : SceneGraphNode
{
if (node == null || result == null)
throw new ArgumentNullException();
result.Clear();
result.Add(node);
FillTree(node, result);
}
} }
} }
@@ -135,10 +135,15 @@ bool sortInstallations(RiderInstallation* const& i1, RiderInstallation* const& i
int32 version2[3] = { 0 }; int32 version2[3] = { 0 };
StringUtils::Parse(values1[0].Get(), &version1[0]); StringUtils::Parse(values1[0].Get(), &version1[0]);
StringUtils::Parse(values1[1].Get(), &version1[1]); StringUtils::Parse(values1[1].Get(), &version1[1]);
StringUtils::Parse(values1[2].Get(), &version1[2]);
if(values1.Count() > 2)
StringUtils::Parse(values1[2].Get(), &version1[2]);
StringUtils::Parse(values2[0].Get(), &version2[0]); StringUtils::Parse(values2[0].Get(), &version2[0]);
StringUtils::Parse(values2[1].Get(), &version2[1]); StringUtils::Parse(values2[1].Get(), &version2[1]);
StringUtils::Parse(values2[2].Get(), &version2[2]);
if(values2.Count() > 2)
StringUtils::Parse(values2[2].Get(), &version2[2]);
// Compare by MAJOR.MINOR.BUILD // Compare by MAJOR.MINOR.BUILD
if (version1[0] == version2[0]) if (version1[0] == version2[0])
+14 -6
View File
@@ -28,6 +28,9 @@ namespace FlaxEditor.States
/// <inheritdoc /> /// <inheritdoc />
public override bool CanEditScene => true; public override bool CanEditScene => true;
/// <inheritdoc />
public override bool CanChangeScene => true;
/// <inheritdoc /> /// <inheritdoc />
public override bool CanUseUndoRedo => false; public override bool CanUseUndoRedo => false;
@@ -89,9 +92,13 @@ namespace FlaxEditor.States
{ {
Profiler.BeginEvent("PlayingState.CacheSelection"); Profiler.BeginEvent("PlayingState.CacheSelection");
_selectedObjects.Clear(); _selectedObjects.Clear();
for (int i = 0; i < Editor.SceneEditing.Selection.Count; i++) var selection = Editor.SceneEditing.Selection;
if (selection.Count != 0)
{ {
_selectedObjects.Add(Editor.SceneEditing.Selection[i].ID); for (int i = 0; i < selection.Count; i++)
_selectedObjects.Add(selection[i].ID);
selection.Clear();
Editor.SceneEditing.OnSelectionChanged();
} }
Profiler.EndEvent(); Profiler.EndEvent();
} }
@@ -99,15 +106,16 @@ namespace FlaxEditor.States
private void RestoreSelection() private void RestoreSelection()
{ {
Profiler.BeginEvent("PlayingState.RestoreSelection"); Profiler.BeginEvent("PlayingState.RestoreSelection");
var count = Editor.SceneEditing.Selection.Count; var selection = Editor.SceneEditing.Selection;
Editor.SceneEditing.Selection.Clear(); var count = selection.Count;
selection.Clear();
for (int i = 0; i < _selectedObjects.Count; i++) for (int i = 0; i < _selectedObjects.Count; i++)
{ {
var node = SceneGraphFactory.FindNode(_selectedObjects[i]); var node = SceneGraphFactory.FindNode(_selectedObjects[i]);
if (node != null) if (node != null)
Editor.SceneEditing.Selection.Add(node); selection.Add(node);
} }
if (Editor.SceneEditing.Selection.Count != count) if (selection.Count != count)
Editor.SceneEditing.OnSelectionChanged(); Editor.SceneEditing.OnSelectionChanged();
Profiler.EndEvent(); Profiler.EndEvent();
} }
+1 -4
View File
@@ -122,10 +122,7 @@ namespace FlaxEditor.Surface.Archetypes
{ {
GetBox(0).CurrentType = type; GetBox(0).CurrentType = type;
Title = (_isUnpacking ? "Unpack " : "Pack ") + type.Name; Title = (_isUnpacking ? "Unpack " : "Pack ") + type.Name;
var attributes = type.GetAttributes(false); TooltipText = SurfaceUtils.GetVisualScriptTypeDescription(type);
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
if (tooltipAttribute != null)
TooltipText += "\n" + tooltipAttribute.Text;
} }
else else
{ {
+17
View File
@@ -92,6 +92,7 @@ namespace FlaxEditor.Surface
case MaterialParameterType.GPUTexture: return typeof(GPUTexture); case MaterialParameterType.GPUTexture: return typeof(GPUTexture);
case MaterialParameterType.Matrix: return typeof(Matrix); case MaterialParameterType.Matrix: return typeof(Matrix);
case MaterialParameterType.ChannelMask: return typeof(ChannelMask); case MaterialParameterType.ChannelMask: return typeof(ChannelMask);
case MaterialParameterType.TextureGroupSampler: return typeof(int);
default: throw new ArgumentOutOfRangeException(nameof(type), type, null); default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
} }
} }
@@ -427,6 +428,22 @@ namespace FlaxEditor.Surface
return !TypeUtils.IsDelegate(managedType); return !TypeUtils.IsDelegate(managedType);
} }
internal static string GetVisualScriptTypeDescription(ScriptType type)
{
var sb = new StringBuilder();
if (type.IsStatic)
sb.Append("static ");
else if (type.IsAbstract)
sb.Append("abstract ");
sb.Append(type.TypeName);
var attributes = type.GetAttributes(false);
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
if (tooltipAttribute != null)
sb.Append("\n").Append(tooltipAttribute.Text);
return sb.ToString();
}
internal static string GetVisualScriptMemberInfoDescription(ScriptMemberInfo member) internal static string GetVisualScriptMemberInfoDescription(ScriptMemberInfo member)
{ {
var name = member.Name; var name = member.Name;
+13 -6
View File
@@ -604,7 +604,7 @@ namespace FlaxEditor.Surface
Parent = this Parent = this
}; };
Presenter = new CustomEditorPresenter(undo); Presenter = new CustomEditorPresenter(undo, "Loading...");
Presenter.Panel.Parent = scrollPanel; Presenter.Panel.Parent = scrollPanel;
} }
@@ -744,7 +744,7 @@ namespace FlaxEditor.Surface
} }
else else
{ {
_propertiesEditor = new CustomEditorPresenter(_undo); _propertiesEditor = new CustomEditorPresenter(_undo, "Loading...");
_propertiesEditor.Panel.Parent = _split2.Panel2; _propertiesEditor.Panel.Parent = _split2.Panel2;
} }
_propertiesEditor.Modified += OnPropertyEdited; _propertiesEditor.Modified += OnPropertyEdited;
@@ -896,6 +896,16 @@ namespace FlaxEditor.Surface
Close(); Close();
} }
/// <summary>
/// Called when surface gets loaded and user can edit it.
/// </summary>
protected virtual void OnSurfaceEditingStart()
{
_undo.Clear();
_surface.Enabled = true;
_propertiesEditor.BuildLayout();
}
/// <summary> /// <summary>
/// Loads the surface from the asset. Called during <see cref="Update"/> when asset is loaded and surface is missing. /// Loads the surface from the asset. Called during <see cref="Update"/> when asset is loaded and surface is missing.
/// </summary> /// </summary>
@@ -939,10 +949,7 @@ namespace FlaxEditor.Surface
return; return;
} }
// Setup OnSurfaceEditingStart();
_undo.Clear();
_surface.Enabled = true;
_propertiesEditor.BuildLayout();
ClearEditedFlag(); ClearEditedFlag();
if (_showWholeGraphOnLoad) if (_showWholeGraphOnLoad)
{ {
@@ -2,6 +2,7 @@
using System; using System;
using FlaxEditor.Gizmo; using FlaxEditor.Gizmo;
using FlaxEditor.SceneGraph;
using FlaxEditor.Tools.Foliage.Undo; using FlaxEditor.Tools.Foliage.Undo;
using FlaxEngine; using FlaxEngine;
@@ -87,6 +88,12 @@ namespace FlaxEditor.Tools.Foliage
navigationDirty = false; navigationDirty = false;
} }
/// <inheritdoc />
protected override bool IsSelected(SceneGraphNode obj)
{
return false;
}
/// <inheritdoc /> /// <inheritdoc />
protected override void OnStartTransforming() protected override void OnStartTransforming()
{ {
@@ -231,6 +238,21 @@ namespace FlaxEditor.Tools.Foliage
Owner.Undo?.AddAction(action); Owner.Undo?.AddAction(action);
} }
/// <inheritdoc />
public override void SnapToGround()
{
if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, uint.MaxValue, false))
{
// Snap
StartTransforming();
var translationDelta = hit.Point - Position;
var rotationDelta = Quaternion.Identity;
var scaleDelta = Vector3.Zero;
OnApplyTransformation(ref translationDelta, ref rotationDelta, ref scaleDelta);
EndTransforming();
}
}
/// <inheritdoc /> /// <inheritdoc />
public override void OnActivated() public override void OnActivated()
{ {
@@ -135,6 +135,7 @@ namespace FlaxEditor.Actions
node.Delete(); node.Delete();
} }
_nodeParents.Clear(); _nodeParents.Clear();
FlaxEngine.Scripting.FlushRemovedObjects();
} }
/// <summary> /// <summary>
@@ -141,15 +141,20 @@ namespace FlaxEditor.Actions
for (int i = 0; i < nodeParents.Count; i++) for (int i = 0; i < nodeParents.Count; i++)
{ {
var node = nodeParents[i]; var node = nodeParents[i];
var parent = node.Actor?.Parent; var actor = node.Actor;
var parent = actor?.Parent;
if (parent != null) if (parent != null)
{ {
// Fix name collisions // Fix name collisions
string name = node.Name; var name = actor.Name;
Actor[] children = parent.Children; for (int j = 0; j < parent.ChildrenCount; j++)
if (children.Any(x => x.Name == name))
{ {
node.Actor.Name = StringUtils.IncrementNameNumber(name, x => children.All(y => y.Name != x)); var child = parent.Children[j];
if (child != actor && child.Name == actor.Name)
{
var children = parent.Children;
actor.Name = StringUtils.IncrementNameNumber(name, x => children.All(y => y.Name != x));
}
} }
} }
+12
View File
@@ -76,6 +76,18 @@ namespace FlaxEditor.Utilities
return string.Format("{0:0.##} {1}", bytes, MemorySizePostfixes[order]); return string.Format("{0:0.##} {1}", bytes, MemorySizePostfixes[order]);
} }
internal static string GetTooltip(SceneObject obj)
{
var str = obj is Actor actor ? actor.Name : TypeUtils.GetObjectType(obj).Name;
var o = obj.Parent;
while (o)
{
str = o.Name + " -> " + str;
o = o.Parent;
}
return str;
}
/// <summary> /// <summary>
/// The colors for the keyframes used by the curve editor. /// The colors for the keyframes used by the curve editor.
/// </summary> /// </summary>
@@ -21,10 +21,12 @@ namespace FlaxEditor.Viewport
/// </summary> /// </summary>
/// <param name="task">The task.</param> /// <param name="task">The task.</param>
/// <param name="undo">The undo.</param> /// <param name="undo">The undo.</param>
public EditorGizmoViewport(SceneRenderTask task, Undo undo) /// <param name="sceneGraphRoot">The scene graph root.</param>
public EditorGizmoViewport(SceneRenderTask task, Undo undo, SceneGraph.RootNode sceneGraphRoot)
: base(task, new FPSCamera(), true) : base(task, new FPSCamera(), true)
{ {
Undo = undo; Undo = undo;
SceneGraphRoot = sceneGraphRoot;
SetUpdate(ref _update, OnUpdate); SetUpdate(ref _update, OnUpdate);
} }
@@ -73,6 +75,9 @@ namespace FlaxEditor.Viewport
/// <inheritdoc /> /// <inheritdoc />
public Undo Undo { get; } public Undo Undo { get; }
/// <inheritdoc />
public SceneGraph.RootNode SceneGraphRoot { get; }
/// <inheritdoc /> /// <inheritdoc />
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
@@ -184,7 +184,7 @@ namespace FlaxEditor.Viewport
/// </summary> /// </summary>
/// <param name="editor">Editor instance.</param> /// <param name="editor">Editor instance.</param>
public MainEditorGizmoViewport(Editor editor) public MainEditorGizmoViewport(Editor editor)
: base(Object.New<SceneRenderTask>(), editor.Undo) : base(Object.New<SceneRenderTask>(), editor.Undo, editor.Scene.Root)
{ {
_editor = editor; _editor = editor;
_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem); _dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem);
@@ -336,6 +336,9 @@ namespace FlaxEditor.Viewport
/// <inheritdoc /> /// <inheritdoc />
public Undo Undo { get; } public Undo Undo { get; }
/// <inheritdoc />
public RootNode SceneGraphRoot => _window.Graph.Root;
/// <inheritdoc /> /// <inheritdoc />
public EditorViewport Viewport => this; public EditorViewport Viewport => this;
@@ -186,7 +186,6 @@ namespace FlaxEditor.Windows.Assets
// Asset properties proxy // Asset properties proxy
_properties = new PropertiesProxy(); _properties = new PropertiesProxy();
_propertiesEditor.Select(_properties);
// Preview properties editor // Preview properties editor
_previewTab = new Tab("Preview"); _previewTab = new Tab("Preview");
@@ -322,22 +321,17 @@ namespace FlaxEditor.Windows.Assets
{ {
if (value == null) if (value == null)
{ {
// Error
Editor.LogError("Failed to save animation graph surface"); Editor.LogError("Failed to save animation graph surface");
return; return;
} }
// Save data to the temporary asset
if (_asset.SaveSurface(value)) if (_asset.SaveSurface(value))
{ {
// Error
_surface.MarkAsEdited(); _surface.MarkAsEdited();
Editor.LogError("Failed to save animation graph surface data"); Editor.LogError("Failed to save animation graph surface data");
return; return;
} }
_asset.Reload(); _asset.Reload();
_asset.WaitForLoaded();
// Reset any root motion
_preview.PreviewActor.ResetLocalTransform(); _preview.PreviewActor.ResetLocalTransform();
_previewTab.Presenter.BuildLayoutOnUpdate(); _previewTab.Presenter.BuildLayoutOnUpdate();
} }
@@ -376,6 +370,14 @@ namespace FlaxEditor.Windows.Assets
return false; return false;
} }
/// <inheritdoc />
protected override void OnSurfaceEditingStart()
{
_propertiesEditor.Select(_properties);
base.OnSurfaceEditingStart();
}
/// <inheritdoc /> /// <inheritdoc />
protected override void PerformLayoutBeforeChildren() protected override void PerformLayoutBeforeChildren()
{ {
@@ -229,7 +229,7 @@ namespace FlaxEditor.Windows.Assets
layout.Label("No parameters"); layout.Label("No parameters");
return; return;
} }
if (!materialInstance.IsLoaded || materialInstance.BaseMaterial && !materialInstance.BaseMaterial.IsLoaded) if (!materialInstance.IsLoaded || (materialInstance.BaseMaterial && !materialInstance.BaseMaterial.IsLoaded))
{ {
layout.Label("Loading..."); layout.Label("Loading...");
return; return;
@@ -352,10 +352,9 @@ namespace FlaxEditor.Windows.Assets
}; };
// Material properties editor // Material properties editor
_editor = new CustomEditorPresenter(_undo); _editor = new CustomEditorPresenter(_undo, "Loading...");
_editor.Panel.Parent = _split.Panel2; _editor.Panel.Parent = _split.Panel2;
_properties = new PropertiesProxy(); _properties = new PropertiesProxy();
_editor.Select(_properties);
_editor.Modified += OnMaterialPropertyEdited; _editor.Modified += OnMaterialPropertyEdited;
// Setup input actions // Setup input actions
@@ -479,16 +478,11 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc /> /// <inheritdoc />
public override void Update(float deltaTime) public override void Update(float deltaTime)
{ {
// Check if need to load
if (_isWaitingForLoad && _asset.IsLoaded && (_asset.BaseMaterial == null || _asset.BaseMaterial.IsLoaded)) if (_isWaitingForLoad && _asset.IsLoaded && (_asset.BaseMaterial == null || _asset.BaseMaterial.IsLoaded))
{ {
// Clear flag
_isWaitingForLoad = false; _isWaitingForLoad = false;
// Init material properties and parameters proxy
_properties.OnLoad(this); _properties.OnLoad(this);
_editor.Select(_properties);
// Setup
ClearEditedFlag(); ClearEditedFlag();
_undo.Clear(); _undo.Clear();
_editor.BuildLayout(); _editor.BuildLayout();
@@ -222,7 +222,6 @@ namespace FlaxEditor.Windows.Assets
// Asset properties proxy // Asset properties proxy
_properties = new PropertiesProxy(); _properties = new PropertiesProxy();
_propertiesEditor.Select(_properties);
// Surface // Surface
_surface = new MaterialSurface(this, Save, _undo) _surface = new MaterialSurface(this, Save, _undo)
@@ -324,17 +323,14 @@ namespace FlaxEditor.Windows.Assets
get => _asset.LoadSurface(true); get => _asset.LoadSurface(true);
set set
{ {
// Create material info
FillMaterialInfo(out var info); FillMaterialInfo(out var info);
// Save data to the temporary material
if (_asset.SaveSurface(value, info)) if (_asset.SaveSurface(value, info))
{ {
// Error
_surface.MarkAsEdited(); _surface.MarkAsEdited();
Editor.LogError("Failed to save material surface data"); Editor.LogError("Failed to save material surface data");
} }
_asset.Reload(); _asset.Reload();
_asset.WaitForLoaded();
} }
} }
@@ -347,7 +343,6 @@ namespace FlaxEditor.Windows.Assets
// Load surface graph // Load surface graph
if (_surface.Load()) if (_surface.Load())
{ {
// Error
Editor.LogError("Failed to load material surface."); Editor.LogError("Failed to load material surface.");
return true; return true;
} }
@@ -362,6 +357,14 @@ namespace FlaxEditor.Windows.Assets
return false; return false;
} }
/// <inheritdoc />
protected override void OnSurfaceEditingStart()
{
_propertiesEditor.Select(_properties);
base.OnSurfaceEditingStart();
}
/// <inheritdoc /> /// <inheritdoc />
protected override bool CanEditSurfaceOnAssetLoadError => true; protected override bool CanEditSurfaceOnAssetLoadError => true;
@@ -120,7 +120,6 @@ namespace FlaxEditor.Windows.Assets
// Asset properties proxy // Asset properties proxy
_properties = new PropertiesProxy(); _properties = new PropertiesProxy();
_propertiesEditor.Select(_properties);
// Preview properties editor // Preview properties editor
_previewTab = new Tab("Preview"); _previewTab = new Tab("Preview");
@@ -217,14 +216,13 @@ namespace FlaxEditor.Windows.Assets
get => _asset.LoadSurface(true); get => _asset.LoadSurface(true);
set set
{ {
// Save data to the temporary asset
if (_asset.SaveSurface(value)) if (_asset.SaveSurface(value))
{ {
// Error
_surface.MarkAsEdited(); _surface.MarkAsEdited();
Editor.LogError("Failed to save Particle Emitter surface data"); Editor.LogError("Failed to save Particle Emitter surface data");
} }
_asset.Reload(); _asset.Reload();
_asset.WaitForLoaded();
_preview.PreviewActor.ResetSimulation(); _preview.PreviewActor.ResetSimulation();
_previewTab.Presenter.BuildLayoutOnUpdate(); _previewTab.Presenter.BuildLayoutOnUpdate();
} }
@@ -255,6 +253,14 @@ namespace FlaxEditor.Windows.Assets
return false; return false;
} }
/// <inheritdoc />
protected override void OnSurfaceEditingStart()
{
_propertiesEditor.Select(_properties);
base.OnSurfaceEditingStart();
}
/// <inheritdoc /> /// <inheritdoc />
protected override bool SaveToOriginal() protected override bool SaveToOriginal()
{ {
@@ -109,7 +109,7 @@ namespace FlaxEditor.Windows.Assets
contextMenu.AddSeparator(); contextMenu.AddSeparator();
b = contextMenu.AddButton("Create Prefab", Editor.Prefabs.CreatePrefab); b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection));
b.Enabled = isSingleActorSelected && b.Enabled = isSingleActorSelected &&
(Selection[0] as ActorNode).CanCreatePrefab && (Selection[0] as ActorNode).CanCreatePrefab &&
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets; Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
@@ -230,7 +230,7 @@ namespace FlaxEditor.Windows
} }
_view.IsSearching = true; _view.IsSearching = true;
_view.ShowItems(items); _view.ShowItems(items, _sortType);
} }
private void UpdateItemsSearchFilter(ContentFolder folder, List<ContentItem> items, bool[] filters) private void UpdateItemsSearchFilter(ContentFolder folder, List<ContentItem> items, bool[] filters)
+29 -2
View File
@@ -39,6 +39,7 @@ namespace FlaxEditor.Windows
private TextBox _foldersSearchBox; private TextBox _foldersSearchBox;
private TextBox _itemsSearchBox; private TextBox _itemsSearchBox;
private ViewDropdown _viewDropdown; private ViewDropdown _viewDropdown;
private SortType _sortType;
private RootContentTreeNode _root; private RootContentTreeNode _root;
@@ -215,6 +216,20 @@ namespace FlaxEditor.Windows
filterButton.Checked = _viewDropdown.IsSelected(filterButton.Text); filterButton.Checked = _viewDropdown.IsSelected(filterButton.Text);
} }
}; };
var sortBy = menu.AddChildMenu("Sort by");
sortBy.ContextMenu.AddButton("Alphabetic Order", OnSortByButtonClicked).Tag = SortType.AlphabeticOrder;
sortBy.ContextMenu.AddButton("Alphabetic Reverse", OnSortByButtonClicked).Tag = SortType.AlphabeticReverse;
sortBy.ContextMenu.VisibleChanged += control =>
{
if (!control.Visible)
return;
foreach (var item in ((ContextMenu)control).Items)
{
if (item is ContextMenuButton button)
button.Checked = _sortType == (SortType)button.Tag;
}
};
return menu; return menu;
} }
@@ -230,6 +245,18 @@ namespace FlaxEditor.Windows
_viewDropdown.OnClicked(i); _viewDropdown.OnClicked(i);
} }
private void OnSortByButtonClicked(ContextMenuButton button)
{
switch ((SortType)button.Tag)
{
case SortType.AlphabeticOrder: _sortType = SortType.AlphabeticOrder;
break;
case SortType.AlphabeticReverse: _sortType = SortType.AlphabeticReverse;
break;
}
RefreshView(SelectedNode);
}
/// <summary> /// <summary>
/// Shows popup dialog with UI to rename content item. /// Shows popup dialog with UI to rename content item.
/// </summary> /// </summary>
@@ -701,12 +728,12 @@ namespace FlaxEditor.Windows
items.Add(node.Folder); items.Add(node.Folder);
} }
} }
_view.ShowItems(items); _view.ShowItems(items, _sortType);
} }
else else
{ {
// Show folder contents // Show folder contents
_view.ShowItems(target.Folder.Children); _view.ShowItems(target.Folder.Children, _sortType);
} }
} }
+27 -10
View File
@@ -446,12 +446,18 @@ namespace FlaxEditor.Windows
} }
} }
private struct QueueItem
{
public string PresetName;
public BuildTarget Target;
}
private PresetsColumn _presets; private PresetsColumn _presets;
private TargetsColumn _targets; private TargetsColumn _targets;
private int _selectedPresetIndex = -1; private int _selectedPresetIndex = -1;
private int _selectedTargetIndex = -1; private int _selectedTargetIndex = -1;
private CustomEditorPresenter _targetSettings; private CustomEditorPresenter _targetSettings;
private readonly Queue<BuildTarget> _buildingQueue = new Queue<BuildTarget>(); private readonly Queue<QueueItem> _buildingQueue = new Queue<QueueItem>();
private string _preBuildAction; private string _preBuildAction;
private string _postBuildAction; private string _postBuildAction;
private BuildPreset[] _data; private BuildPreset[] _data;
@@ -489,14 +495,14 @@ namespace FlaxEditor.Windows
{ {
// Execute pre-build action // Execute pre-build action
if (!string.IsNullOrEmpty(_preBuildAction)) if (!string.IsNullOrEmpty(_preBuildAction))
ExecueAction(_preBuildAction); ExecuteAction(_preBuildAction);
_preBuildAction = null; _preBuildAction = null;
} }
else if (type == GameCooker.EventType.BuildDone) else if (type == GameCooker.EventType.BuildDone)
{ {
// Execute post-build action // Execute post-build action
if (!string.IsNullOrEmpty(_postBuildAction)) if (!string.IsNullOrEmpty(_postBuildAction))
ExecueAction(_postBuildAction); ExecuteAction(_postBuildAction);
_postBuildAction = null; _postBuildAction = null;
} }
else if (type == GameCooker.EventType.BuildFailed) else if (type == GameCooker.EventType.BuildFailed)
@@ -506,7 +512,7 @@ namespace FlaxEditor.Windows
} }
} }
private void ExecueAction(string action) private void ExecuteAction(string action)
{ {
string command = "echo off\ncd \"" + Globals.ProjectFolder.Replace('/', '\\') + "\"\necho on\n" + action; string command = "echo off\ncd \"" + Globals.ProjectFolder.Replace('/', '\\') + "\"\necho on\n" + action;
command = command.Replace("\n", "\r\n"); command = command.Replace("\n", "\r\n");
@@ -545,21 +551,30 @@ namespace FlaxEditor.Windows
Editor.Log("Building all targets"); Editor.Log("Building all targets");
foreach (var e in preset.Targets) foreach (var e in preset.Targets)
{ {
_buildingQueue.Enqueue(e.DeepClone()); _buildingQueue.Enqueue(new QueueItem
{
PresetName = preset.Name,
Target = e.DeepClone(),
});
} }
} }
/// <summary> /// <summary>
/// Builds the target. /// Builds the target.
/// </summary> /// </summary>
/// <param name="preset">The preset.</param>
/// <param name="target">The target.</param> /// <param name="target">The target.</param>
public void Build(BuildTarget target) public void Build(BuildPreset preset, BuildTarget target)
{ {
if (target == null) if (target == null)
throw new ArgumentNullException(nameof(target)); throw new ArgumentNullException(nameof(target));
Editor.Log("Building target"); Editor.Log("Building target");
_buildingQueue.Enqueue(target.DeepClone()); _buildingQueue.Enqueue(new QueueItem
{
PresetName = preset.Name,
Target = target.DeepClone(),
});
} }
private void BuildTarget() private void BuildTarget()
@@ -569,7 +584,8 @@ namespace FlaxEditor.Windows
if (_data[_selectedPresetIndex].Targets == null || _data[_selectedPresetIndex].Targets.Length <= _selectedTargetIndex) if (_data[_selectedPresetIndex].Targets == null || _data[_selectedPresetIndex].Targets.Length <= _selectedTargetIndex)
return; return;
Build(_data[_selectedPresetIndex].Targets[_selectedTargetIndex]); var preset = _data[_selectedPresetIndex];
Build(preset, preset.Targets[_selectedTargetIndex]);
} }
private void BuildAllTargets() private void BuildAllTargets()
@@ -825,12 +841,13 @@ namespace FlaxEditor.Windows
{ {
if (_buildingQueue.Count > 0) if (_buildingQueue.Count > 0)
{ {
var target = _buildingQueue.Dequeue(); var item = _buildingQueue.Dequeue();
var target = item.Target;
_preBuildAction = target.PreBuildAction; _preBuildAction = target.PreBuildAction;
_postBuildAction = target.PostBuildAction; _postBuildAction = target.PostBuildAction;
GameCooker.Build(target.Platform, target.Mode, target.Output, BuildOptions.None, target.CustomDefines); GameCooker.Build(target.Platform, target.Mode, target.Output, BuildOptions.None, target.CustomDefines, item.PresetName, target.Name);
} }
else if (_exitOnBuildEnd) else if (_exitOnBuildEnd)
{ {
@@ -29,6 +29,7 @@ namespace FlaxEditor.Windows
: base(editor, true, ScrollBars.Vertical) : base(editor, true, ScrollBars.Vertical)
{ {
Title = "Properties"; Title = "Properties";
AutoFocus = true;
Presenter = new CustomEditorPresenter(editor.Undo); Presenter = new CustomEditorPresenter(editor.Undo);
Presenter.Panel.Parent = this; Presenter.Panel.Parent = this;
@@ -104,12 +104,11 @@ namespace FlaxEditor.Windows
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets; Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
bool hasPrefabLink = canEditScene && isSingleActorSelected && (Editor.SceneEditing.Selection[0] as ActorNode).HasPrefabLink; bool hasPrefabLink = canEditScene && isSingleActorSelected && (Editor.SceneEditing.Selection[0] as ActorNode).HasPrefabLink;
if (hasPrefabLink)
b = contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab); {
b.Enabled = hasPrefabLink; contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab);
contextMenu.AddButton("Break Prefab Link", Editor.Prefabs.BreakLinks);
b = contextMenu.AddButton("Break Prefab Link", Editor.Prefabs.BreakLinks); }
b.Enabled = hasPrefabLink;
// Spawning actors options // Spawning actors options
+1 -8
View File
@@ -204,14 +204,7 @@ namespace FlaxEditor.Windows
continue; continue;
var item = _groupSearch.AddChild(CreateActorItem(CustomEditors.CustomEditorsUtil.GetPropertyNameUI(text), actorType)); var item = _groupSearch.AddChild(CreateActorItem(CustomEditors.CustomEditorsUtil.GetPropertyNameUI(text), actorType));
item.TooltipText = actorType.TypeName; item.TooltipText = Surface.SurfaceUtils.GetVisualScriptTypeDescription(actorType);
var attributes = actorType.GetAttributes(false);
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
if (tooltipAttribute != null)
{
item.TooltipText += '\n';
item.TooltipText += tooltipAttribute.Text;
}
var highlights = new List<Rectangle>(ranges.Length); var highlights = new List<Rectangle>(ranges.Length);
var style = Style.Current; var style = Style.Current;
@@ -236,23 +236,24 @@ void SceneAnimationPlayer::MapTrack(const StringView& from, const Guid& to)
{ {
const auto trackData = track.GetData<SceneAnimation::ActorTrack::Data>(); const auto trackData = track.GetData<SceneAnimation::ActorTrack::Data>();
_objectsMapping[trackData->ID] = to; _objectsMapping[trackData->ID] = to;
break; return;
} }
case SceneAnimation::Track::Types::Script: case SceneAnimation::Track::Types::Script:
{ {
const auto trackData = track.GetData<SceneAnimation::ScriptTrack::Data>(); const auto trackData = track.GetData<SceneAnimation::ScriptTrack::Data>();
_objectsMapping[trackData->ID] = to; _objectsMapping[trackData->ID] = to;
break; return;
} }
case SceneAnimation::Track::Types::CameraCut: case SceneAnimation::Track::Types::CameraCut:
{ {
const auto trackData = track.GetData<SceneAnimation::CameraCutTrack::Data>(); const auto trackData = track.GetData<SceneAnimation::CameraCutTrack::Data>();
_objectsMapping[trackData->ID] = to; _objectsMapping[trackData->ID] = to;
break; return;
} }
default: ; default: ;
} }
} }
LOG(Warning, "Missing track '{0}' in scene animation '{1}' to map into object ID={2}", from, anim->ToString(), to);
} }
void SceneAnimationPlayer::Restore(SceneAnimation* anim, int32 stateIndexOffset) void SceneAnimationPlayer::Restore(SceneAnimation* anim, int32 stateIndexOffset)
@@ -744,7 +745,7 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
const auto trackData = track.GetData<SceneAnimation::ActorTrack::Data>(); const auto trackData = track.GetData<SceneAnimation::ActorTrack::Data>();
Guid id = trackData->ID; Guid id = trackData->ID;
_objectsMapping.TryGet(id, id); _objectsMapping.TryGet(id, id);
state.Object = Scripting::FindObject<Actor>(trackData->ID); state.Object = Scripting::FindObject<Actor>(id);
if (!state.Object) if (!state.Object)
{ {
LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", id, track.Name, anim->ToString(), TEXT("actor")); LOG(Warning, "Failed to find {3} of ID={0} for track '{1}' in scene animation '{2}'", id, track.Name, anim->ToString(), TEXT("actor"));
@@ -1222,6 +1222,11 @@ Asset::LoadResult VisualScript::load()
method.ProfilerName.Get()[assetName.Length()] = ':'; method.ProfilerName.Get()[assetName.Length()] = ':';
method.ProfilerName.Get()[assetName.Length() + 1] = ':'; method.ProfilerName.Get()[assetName.Length() + 1] = ':';
Platform::MemoryCopy(method.ProfilerName.Get() + assetName.Length() + 2, method.Name.Get(), method.Name.Length()); Platform::MemoryCopy(method.ProfilerName.Get() + assetName.Length() + 2, method.Name.Get(), method.Name.Length());
method.ProfilerData.name = method.ProfilerName.Get();
method.ProfilerData.function = method.Name.Get();
method.ProfilerData.file = nullptr;
method.ProfilerData.line = 0;
method.ProfilerData.color = 0;
} }
#endif #endif
@@ -2139,7 +2144,7 @@ VisualScriptingBinaryModule* VisualScripting::GetBinaryModule()
Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters) Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters)
{ {
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero); CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
PROFILE_CPU_NAMED(*method->ProfilerName); PROFILE_CPU_SRC_LOC(method->ProfilerData);
// Add to the calling stack // Add to the calling stack
ScopeContext scope; ScopeContext scope;
@@ -5,6 +5,9 @@
#include "../BinaryAsset.h" #include "../BinaryAsset.h"
#include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/BinaryModule.h"
#include "Engine/Visject/VisjectGraph.h" #include "Engine/Visject/VisjectGraph.h"
#if COMPILE_WITH_PROFILER
#include "Engine/Profiler/ProfilerSrcLoc.h"
#endif
#define VISUAL_SCRIPT_GRAPH_MAX_CALL_STACK 250 #define VISUAL_SCRIPT_GRAPH_MAX_CALL_STACK 250
#define VISUAL_SCRIPT_DEBUGGING USE_EDITOR #define VISUAL_SCRIPT_DEBUGGING USE_EDITOR
@@ -118,6 +121,7 @@ public:
Array<StringAnsi, InlinedAllocation<16>> ParamNames; Array<StringAnsi, InlinedAllocation<16>> ParamNames;
#if COMPILE_WITH_PROFILER #if COMPILE_WITH_PROFILER
StringAnsi ProfilerName; StringAnsi ProfilerName;
SourceLocationData ProfilerData;
#endif #endif
}; };
+1 -1
View File
@@ -296,7 +296,7 @@ namespace FlaxEditor.Content.Settings
} }
// Create new settings asset and link it to the game settings // Create new settings asset and link it to the game settings
var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json"); var path = StringUtils.CombinePaths(Globals.ProjectContentFolder, "Settings", CustomEditors.CustomEditorsUtil.GetPropertyNameUI(typeof(T).Name) + ".json");
if (Editor.SaveJsonAsset(path, obj)) if (Editor.SaveJsonAsset(path, obj))
return true; return true;
asset = FlaxEngine.Content.LoadAsync<JsonAsset>(path); asset = FlaxEngine.Content.LoadAsync<JsonAsset>(path);
+1
View File
@@ -150,6 +150,7 @@ bool CommandLine::Parse(const Char* cmdLine)
PARSE_BOOL_SWITCH("-clearcache ", ClearCache); PARSE_BOOL_SWITCH("-clearcache ", ClearCache);
PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache); PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache);
PARSE_ARG_SWITCH("-project ", Project); PARSE_ARG_SWITCH("-project ", Project);
PARSE_BOOL_SWITCH("-new ", NewProject);
PARSE_BOOL_SWITCH("-genprojectfiles ", GenProjectFiles); PARSE_BOOL_SWITCH("-genprojectfiles ", GenProjectFiles);
PARSE_ARG_SWITCH("-build ", Build); PARSE_ARG_SWITCH("-build ", Build);
PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile); PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile);
+5
View File
@@ -134,6 +134,11 @@ public:
/// </summary> /// </summary>
String Project; String Project;
/// <summary>
/// -new (generates the project files inside the specified project folder or uses current workspace folder)
/// </summary>
Nullable<bool> NewProject;
/// <summary> /// <summary>
/// -genprojectfiles (generates the scripts project files) /// -genprojectfiles (generates the scripts project files)
/// </summary> /// </summary>
+1 -2
View File
@@ -156,8 +156,7 @@ public:
/// <summary> /// <summary>
/// Checks whenever the game viewport is focused by the user (eg. can receive input). /// Checks whenever the game viewport is focused by the user (eg. can receive input).
/// </summary> /// </summary>
/// <returns>True if game viewport is focused, otherwise false.</returns> API_PROPERTY() static bool HasGameViewportFocus();
static bool HasGameViewportFocus();
private: private:
+22
View File
@@ -73,6 +73,7 @@ public:
} }
void Update() override; void Update() override;
void Dispose() override;
}; };
InputService InputServiceInstance; InputService InputServiceInstance;
@@ -914,3 +915,24 @@ void InputService::Update()
} }
} }
} }
void InputService::Dispose()
{
// Dispose input devices
if (Input::Mouse)
{
Input::Mouse->DeleteObject();
Input::Mouse = nullptr;
}
if (Input::Keyboard)
{
Input::Keyboard->DeleteObject();
Input::Keyboard = nullptr;
}
for (int32 i = 0; i < Input::Gamepads.Count(); i++)
Input::Gamepads[i]->DeleteObject();
Input::Gamepads.Clear();
for (int32 i = 0; i < Input::CustomDevices.Count(); i++)
Input::CustomDevices[i]->DeleteObject();
Input::CustomDevices.Clear();
}
+8 -4
View File
@@ -1611,21 +1611,22 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
// Create object // Create object
auto obj = SceneObjectsFactory::Spawn(document, modifier); auto obj = SceneObjectsFactory::Spawn(document, modifier);
sceneObjects->At(i) = obj;
if (obj == nullptr) if (obj == nullptr)
{ {
LOG(Warning, "Cannot create object."); LOG(Warning, "Cannot create object.");
return true; continue;
} }
obj->RegisterObject(); obj->RegisterObject();
// Add to results // Add to results
sceneObjects->At(i) = obj;
Actor* actor = dynamic_cast<Actor*>(obj); Actor* actor = dynamic_cast<Actor*>(obj);
if (actor) if (actor)
{ {
output.Add(actor); output.Add(actor);
} }
} }
// TODO: optimize this to call json parsing only once per-object instead of twice (spawn + load)
stream.SetPosition(startPos); stream.SetPosition(startPos);
for (int32 i = 0; i < objectsCount; i++) for (int32 i = 0; i < objectsCount; i++)
{ {
@@ -1639,7 +1640,7 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
int32 orderInParent; int32 orderInParent;
stream.ReadInt32(&orderInParent); stream.ReadInt32(&orderInParent);
// Load JSON // Load JSON
rapidjson_flax::Document document; rapidjson_flax::Document document;
{ {
PROFILE_CPU_NAMED("Json.Parse"); PROFILE_CPU_NAMED("Json.Parse");
@@ -1653,7 +1654,10 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
// Deserialize object // Deserialize object
auto obj = sceneObjects->At(i); auto obj = sceneObjects->At(i);
SceneObjectsFactory::Deserialize(obj, document, modifier); if (obj)
SceneObjectsFactory::Deserialize(obj, document, modifier);
else
SceneObjectsFactory::HandleObjectDeserializationError(document);
} }
Scripting::ObjectsLookupIdMapping.Set(nullptr); Scripting::ObjectsLookupIdMapping.Set(nullptr);
+5
View File
@@ -27,6 +27,11 @@ void BoxVolume::SetSize(const Vector3& value)
#include "Engine/Debug/DebugDraw.h" #include "Engine/Debug/DebugDraw.h"
Color BoxVolume::GetWiresColor()
{
return Color::White;
}
void BoxVolume::OnDebugDraw() void BoxVolume::OnDebugDraw()
{ {
const Color color = GetWiresColor(); const Color color = GetWiresColor();
+2 -2
View File
@@ -10,7 +10,7 @@
/// </summary> /// </summary>
API_CLASS(Abstract) class FLAXENGINE_API BoxVolume : public Actor API_CLASS(Abstract) class FLAXENGINE_API BoxVolume : public Actor
{ {
DECLARE_SCENE_OBJECT_ABSTRACT(BoxVolume); DECLARE_SCENE_OBJECT(BoxVolume);
protected: protected:
Vector3 _size; Vector3 _size;
@@ -47,7 +47,7 @@ protected:
} }
#if USE_EDITOR #if USE_EDITOR
virtual Color GetWiresColor() = 0; virtual Color GetWiresColor();
#endif #endif
public: public:
@@ -25,13 +25,13 @@ public:
/// <summary> /// <summary>
/// The fog density factor. /// The fog density factor.
/// </summary> /// </summary>
API_FIELD(Attributes="EditorOrder(10), DefaultValue(0.02f), Limit(0.000001f, 0.8f, 0.001f), EditorDisplay(\"Exponential Height Fog\")") API_FIELD(Attributes="EditorOrder(10), DefaultValue(0.02f), Limit(0.0000001f, 100.0f, 0.001f), EditorDisplay(\"Exponential Height Fog\")")
float FogDensity = 0.02f; float FogDensity = 0.02f;
/// <summary> /// <summary>
/// The fog height density factor that controls how the density increases as height decreases. The smaller values produce more visible transition larger. /// The fog height density factor that controls how the density increases as height decreases. The smaller values produce more visible transition larger.
/// </summary> /// </summary>
API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.2f), Limit(0.001f, 2.0f, 0.001f), EditorDisplay(\"Exponential Height Fog\")") API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.2f), Limit(0.0001f, 10.0f, 0.001f), EditorDisplay(\"Exponential Height Fog\")")
float FogHeightFalloff = 0.2f; float FogHeightFalloff = 0.2f;
/// <summary> /// <summary>
+1
View File
@@ -46,6 +46,7 @@ typedef Dictionary<Guid, Actor*, HeapAllocation> ActorsLookup;
#define DECLARE_SCENE_OBJECT_ABSTRACT(type) \ #define DECLARE_SCENE_OBJECT_ABSTRACT(type) \
DECLARE_SCRIPTING_TYPE_NO_SPAWN(type); \ DECLARE_SCRIPTING_TYPE_NO_SPAWN(type); \
static type* Spawn(const SpawnParams& params) { return nullptr; } \
explicit type(const SpawnParams& params) explicit type(const SpawnParams& params)
#define DECLARE_SCENE_OBJECT_NO_SPAWN(type) \ #define DECLARE_SCENE_OBJECT_NO_SPAWN(type) \
+6 -1
View File
@@ -189,7 +189,12 @@ void LocalizationService::OnLocalizationChanged()
localeName[currentCulture.Length() + 5] = '8'; localeName[currentCulture.Length() + 5] = '8';
localeName[currentCulture.Length() + 6] = 0; localeName[currentCulture.Length() + 6] = 0;
} }
std::locale::global(std::locale(localeName)); try
{
std::locale::global(std::locale(localeName));
}
catch (std::runtime_error const&) {}
catch (...) {}
} }
#endif #endif
@@ -47,6 +47,11 @@ int32 BoxTrianglesIndicesCache[] =
}; };
#define NAV_MESH_TILE_MAX_EXTENT 100000000 #define NAV_MESH_TILE_MAX_EXTENT 100000000
#define NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY 0
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
#include "Engine/Debug/DebugDraw.h"
#endif
struct OffMeshLink struct OffMeshLink
{ {
@@ -108,6 +113,9 @@ struct NavigationSceneRasterization
auto v0 = vb[ib[i0++]]; auto v0 = vb[ib[i0++]];
auto v1 = vb[ib[i0++]]; auto v1 = vb[ib[i0++]];
auto v2 = vb[ib[i0++]]; auto v2 = vb[ib[i0++]];
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true);
#endif
auto n = Vector3::Cross(v0 - v1, v0 - v2); auto n = Vector3::Cross(v0 - v1, v0 - v2);
n.Normalize(); n.Normalize();
@@ -124,6 +132,9 @@ struct NavigationSceneRasterization
auto v0 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh); auto v0 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh);
auto v1 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh); auto v1 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh);
auto v2 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh); auto v2 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh);
#if NAV_MESH_BUILD_DEBUG_DRAW_GEOMETRY
DEBUG_DRAW_TRIANGLE(v0, v1, v2, Color::Orange.AlphaMultiplied(0.3f), 1.0f, true);
#endif
auto n = Vector3::Cross(v0 - v1, v0 - v2); auto n = Vector3::Cross(v0 - v1, v0 - v2);
n.Normalize(); n.Normalize();
@@ -223,6 +234,8 @@ struct NavigationSceneRasterization
// Extract data from the actor // Extract data from the actor
if (const auto* boxCollider = dynamic_cast<BoxCollider*>(actor)) if (const auto* boxCollider = dynamic_cast<BoxCollider*>(actor))
{ {
if (boxCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("BoxCollider"); PROFILE_CPU_NAMED("BoxCollider");
const OrientedBoundingBox box = boxCollider->GetOrientedBox(); const OrientedBoundingBox box = boxCollider->GetOrientedBox();
@@ -232,6 +245,8 @@ struct NavigationSceneRasterization
} }
else if (const auto* sphereCollider = dynamic_cast<SphereCollider*>(actor)) else if (const auto* sphereCollider = dynamic_cast<SphereCollider*>(actor))
{ {
if (sphereCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("SphereCollider"); PROFILE_CPU_NAMED("SphereCollider");
const BoundingSphere sphere = sphereCollider->GetSphere(); const BoundingSphere sphere = sphereCollider->GetSphere();
@@ -241,6 +256,8 @@ struct NavigationSceneRasterization
} }
else if (const auto* capsuleCollider = dynamic_cast<CapsuleCollider*>(actor)) else if (const auto* capsuleCollider = dynamic_cast<CapsuleCollider*>(actor))
{ {
if (capsuleCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("CapsuleCollider"); PROFILE_CPU_NAMED("CapsuleCollider");
const BoundingBox box = capsuleCollider->GetBox(); const BoundingBox box = capsuleCollider->GetBox();
@@ -250,6 +267,8 @@ struct NavigationSceneRasterization
} }
else if (const auto* meshCollider = dynamic_cast<MeshCollider*>(actor)) else if (const auto* meshCollider = dynamic_cast<MeshCollider*>(actor))
{ {
if (meshCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("MeshCollider"); PROFILE_CPU_NAMED("MeshCollider");
auto collisionData = meshCollider->CollisionData.Get(); auto collisionData = meshCollider->CollisionData.Get();
@@ -258,10 +277,17 @@ struct NavigationSceneRasterization
collisionData->ExtractGeometry(vb, ib); collisionData->ExtractGeometry(vb, ib);
Matrix meshColliderToWorld;
meshCollider->GetLocalToWorldMatrix(meshColliderToWorld);
for (auto& v : vb)
Vector3::Transform(v, meshColliderToWorld, v);
e.RasterizeTriangles(); e.RasterizeTriangles();
} }
else if (const auto* splineCollider = dynamic_cast<SplineCollider*>(actor)) else if (const auto* splineCollider = dynamic_cast<SplineCollider*>(actor))
{ {
if (splineCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("SplineCollider"); PROFILE_CPU_NAMED("SplineCollider");
auto collisionData = splineCollider->CollisionData.Get(); auto collisionData = splineCollider->CollisionData.Get();
@@ -153,7 +153,7 @@ void ENetDriver::Disconnect(const NetworkConnection& connection)
void* peer = nullptr; void* peer = nullptr;
if(_peerMap.TryGet(connectionId, peer)) if(_peerMap.TryGet(connectionId, peer))
{ {
enet_peer_disconnect_now((ENetPeer*)_peer, 0); enet_peer_disconnect_now((ENetPeer*)peer, 0);
_peerMap.Remove(connectionId); _peerMap.Remove(connectionId);
} }
else else
+1 -2
View File
@@ -276,8 +276,7 @@ void RigidBody::UpdateMass()
float raiseMassToPower = 0.75f; float raiseMassToPower = 0.75f;
// TODO: link physical material or expose density parameter // TODO: link physical material or expose density parameter
PxVec3 centerOfMassOffset = C2P(_centerOfMassOffset); PxRigidBodyExt::updateMassAndInertia(*_actor, densityKGPerCubicUU);
PxRigidBodyExt::updateMassAndInertia(*_actor, densityKGPerCubicUU, &centerOfMassOffset);
// Grab old mass so we can apply new mass while maintaining inertia tensor // Grab old mass so we can apply new mass while maintaining inertia tensor
const float oldMass = _actor->getMass(); const float oldMass = _actor->getMass();
+63 -12
View File
@@ -291,7 +291,8 @@ void WheeledVehicle::Setup()
offsets[i] = C2P(wheel.Collider->GetLocalPosition()); offsets[i] = C2P(wheel.Collider->GetLocalPosition());
} }
PxF32 sprungMasses[PX_MAX_NB_WHEELS]; PxF32 sprungMasses[PX_MAX_NB_WHEELS];
PxVehicleComputeSprungMasses(wheels.Count(), offsets, centerOfMassOffset.p, _actor->getMass(), 1, sprungMasses); const float mass = _actor->getMass();
PxVehicleComputeSprungMasses(wheels.Count(), offsets, centerOfMassOffset.p, mass, 1, sprungMasses);
PxVehicleWheelsSimData* wheelsSimData = PxVehicleWheelsSimData::allocate(wheels.Count()); PxVehicleWheelsSimData* wheelsSimData = PxVehicleWheelsSimData::allocate(wheels.Count());
for (int32 i = 0; i < wheels.Count(); i++) for (int32 i = 0; i < wheels.Count(); i++)
{ {
@@ -303,15 +304,17 @@ void WheeledVehicle::Setup()
PxVehicleSuspensionData suspensionData; PxVehicleSuspensionData suspensionData;
const float suspensionFrequency = 7.0f; const float suspensionFrequency = 7.0f;
const float suspensionDampingRatio = 1.0f; suspensionData.mMaxCompression = wheel.SuspensionMaxRaise;
suspensionData.mMaxCompression = 10.0f; suspensionData.mMaxDroop = wheel.SuspensionMaxDrop;
suspensionData.mMaxDroop = 10.0f;
suspensionData.mSprungMass = sprungMasses[i]; suspensionData.mSprungMass = sprungMasses[i];
suspensionData.mSpringStrength = Math::Square(suspensionFrequency) * suspensionData.mSprungMass; suspensionData.mSpringStrength = Math::Square(suspensionFrequency) * suspensionData.mSprungMass;
suspensionData.mSpringDamperRate = suspensionDampingRatio * 2.0f * Math::Sqrt(suspensionData.mSpringStrength * suspensionData.mSprungMass); suspensionData.mSpringDamperRate = wheel.SuspensionDampingRate * 2.0f * Math::Sqrt(suspensionData.mSpringStrength * suspensionData.mSprungMass);
PxVehicleTireData tire; PxVehicleTireData tire;
tire.mType = 0; tire.mType = 0;
tire.mLatStiffX = wheel.TireLateralMax;
tire.mLatStiffY = wheel.TireLateralStiffness;
tire.mLongitudinalStiffnessPerUnitGravity = wheel.TireLongitudinalStiffness;
PxVehicleWheelData wheelData; PxVehicleWheelData wheelData;
wheelData.mMass = wheel.Mass; wheelData.mMass = wheel.Mass;
@@ -324,16 +327,17 @@ void WheeledVehicle::Setup()
wheelData.mMaxHandBrakeTorque = M2ToCm2(wheel.MaxHandBrakeTorque); wheelData.mMaxHandBrakeTorque = M2ToCm2(wheel.MaxHandBrakeTorque);
PxVec3 centreOffset = centerOfMassOffset.transformInv(offsets[i]); PxVec3 centreOffset = centerOfMassOffset.transformInv(offsets[i]);
float suspensionForceOffset = 0.0f; PxVec3 forceAppPointOffset(centreOffset.x, wheel.SuspensionForceOffset, centreOffset.z);
PxVec3 forceAppPointOffset(centreOffset.x, centreOffset.y, centreOffset.z + suspensionForceOffset);
wheelsSimData->setTireData(i, tire); wheelsSimData->setTireData(i, tire);
wheelsSimData->setWheelData(i, wheelData); wheelsSimData->setWheelData(i, wheelData);
wheelsSimData->setSuspensionData(i, suspensionData); wheelsSimData->setSuspensionData(i, suspensionData);
wheelsSimData->setSuspTravelDirection(i, PxVec3(0, -1, 0)); wheelsSimData->setSuspTravelDirection(i, centerOfMassOffset.rotate(PxVec3(0.0f, -1.0f, 0.0f)));
wheelsSimData->setWheelCentreOffset(i, centreOffset); wheelsSimData->setWheelCentreOffset(i, centreOffset);
wheelsSimData->setSuspForceAppPointOffset(i, forceAppPointOffset); wheelsSimData->setSuspForceAppPointOffset(i, forceAppPointOffset);
wheelsSimData->setTireForceAppPointOffset(i, forceAppPointOffset); wheelsSimData->setTireForceAppPointOffset(i, forceAppPointOffset);
wheelsSimData->setSubStepCount(4.0f * 100.0f, 3, 1);
wheelsSimData->setMinLongSlipDenominator(4.0f * 100.0f);
PxShape* wheelShape = wheel.Collider->GetPxShape(); PxShape* wheelShape = wheel.Collider->GetPxShape();
if (wheel.Collider->IsActiveInHierarchy()) if (wheel.Collider->IsActiveInHierarchy())
@@ -346,6 +350,9 @@ void WheeledVehicle::Setup()
wheelShape->setQueryFilterData(filter); wheelShape->setQueryFilterData(filter);
wheelShape->setSimulationFilterData(filter); wheelShape->setSimulationFilterData(filter);
wheelsSimData->setSceneQueryFilterData(i, filter); wheelsSimData->setSceneQueryFilterData(i, filter);
// Remove wheels from the simulation (suspension force hold the vehicle)
wheelShape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, false);
} }
else else
{ {
@@ -508,11 +515,29 @@ void WheeledVehicle::Setup()
void WheeledVehicle::DrawPhysicsDebug(RenderView& view) void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
{ {
// Wheels shapes // Wheels shapes
for (auto& wheel : _wheels) for (auto& data : _wheelsData)
{ {
int32 wheelIndex = 0;
for (; wheelIndex < _wheels.Count(); wheelIndex++)
{
if (_wheels[wheelIndex].Collider == data.Collider)
break;
}
if (wheelIndex == _wheels.Count())
break;
auto& wheel = _wheels[wheelIndex];
if (wheel.Collider && wheel.Collider->GetParent() == this && !wheel.Collider->GetIsTrigger()) if (wheel.Collider && wheel.Collider->GetParent() == this && !wheel.Collider->GetIsTrigger())
{ {
DEBUG_DRAW_WIRE_CYLINDER(wheel.Collider->GetPosition(), wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.8f, 0, true); const Vector3 currentPos = wheel.Collider->GetPosition();
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, true);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, true);
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, true);
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.8f, 0, true);
if (!data.State.IsInAir)
{
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.TireContactPoint, 5.0f), Color::Green, 0, true);
}
} }
} }
} }
@@ -520,11 +545,35 @@ void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
void WheeledVehicle::OnDebugDrawSelected() void WheeledVehicle::OnDebugDrawSelected()
{ {
// Wheels shapes // Wheels shapes
for (auto& wheel : _wheels) for (auto& data : _wheelsData)
{ {
int32 wheelIndex = 0;
for (; wheelIndex < _wheels.Count(); wheelIndex++)
{
if (_wheels[wheelIndex].Collider == data.Collider)
break;
}
if (wheelIndex == _wheels.Count())
break;
auto& wheel = _wheels[wheelIndex];
if (wheel.Collider && wheel.Collider->GetParent() == this && !wheel.Collider->GetIsTrigger()) if (wheel.Collider && wheel.Collider->GetParent() == this && !wheel.Collider->GetIsTrigger())
{ {
DEBUG_DRAW_WIRE_CYLINDER(wheel.Collider->GetPosition(), wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.4f, 0, false); const Vector3 currentPos = wheel.Collider->GetPosition();
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(P2C(_actor->getGlobalPose().transform(wheel.Collider->GetPxShape()->getLocalPose()).p), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, false);
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.4f, 0, false);
if (!data.State.SuspensionTraceStart.IsZero())
{
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.SuspensionTraceStart, 5.0f), Color::AliceBlue, 0, false);
DEBUG_DRAW_LINE(data.State.SuspensionTraceStart, data.State.SuspensionTraceEnd, data.State.IsInAir ? Color::Red : Color::Green, 0, false);
}
if (!data.State.IsInAir)
{
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.TireContactPoint, 5.0f), Color::Green, 0, false);
}
} }
} }
@@ -545,6 +594,7 @@ void WheeledVehicle::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(DriveType, _driveType); SERIALIZE_MEMBER(DriveType, _driveType);
SERIALIZE_MEMBER(Wheels, _wheels); SERIALIZE_MEMBER(Wheels, _wheels);
SERIALIZE(UseReverseAsBrake); SERIALIZE(UseReverseAsBrake);
SERIALIZE(UseAnalogSteering);
SERIALIZE_MEMBER(Engine, _engine); SERIALIZE_MEMBER(Engine, _engine);
SERIALIZE_MEMBER(Differential, _differential); SERIALIZE_MEMBER(Differential, _differential);
SERIALIZE_MEMBER(Gearbox, _gearbox); SERIALIZE_MEMBER(Gearbox, _gearbox);
@@ -557,6 +607,7 @@ void WheeledVehicle::Deserialize(DeserializeStream& stream, ISerializeModifier*
DESERIALIZE_MEMBER(DriveType, _driveType); DESERIALIZE_MEMBER(DriveType, _driveType);
DESERIALIZE_MEMBER(Wheels, _wheels); DESERIALIZE_MEMBER(Wheels, _wheels);
DESERIALIZE(UseReverseAsBrake); DESERIALIZE(UseReverseAsBrake);
DESERIALIZE(UseAnalogSteering);
DESERIALIZE_MEMBER(Engine, _engine); DESERIALIZE_MEMBER(Engine, _engine);
DESERIALIZE_MEMBER(Differential, _differential); DESERIALIZE_MEMBER(Differential, _differential);
DESERIALIZE_MEMBER(Gearbox, _gearbox); DESERIALIZE_MEMBER(Gearbox, _gearbox);
+77 -9
View File
@@ -172,47 +172,82 @@ public:
/// <summary> /// <summary>
/// Wheel placement type. /// Wheel placement type.
/// </summary> /// </summary>
API_FIELD() WheelTypes Type = WheelTypes::FrontLeft; API_FIELD(Attributes="EditorOrder(0)") WheelTypes Type = WheelTypes::FrontLeft;
/// <summary> /// <summary>
/// Combined mass of the wheel and the tire in kg. Typically, a wheel has mass between 20Kg and 80Kg but can be lower and higher depending on the vehicle. /// Combined mass of the wheel and the tire in kg. Typically, a wheel has mass between 20Kg and 80Kg but can be lower and higher depending on the vehicle.
/// </summary> /// </summary>
API_FIELD() float Mass = 20.0f; API_FIELD(Attributes="EditorOrder(1)") float Mass = 20.0f;
/// <summary> /// <summary>
/// Distance in metres between the center of the wheel and the outside rim of the tire. It is important that the value of the radius closely matches the radius of the render mesh of the wheel. Any mismatch will result in the wheels either hovering above the ground or intersecting the ground. /// Distance in metres between the center of the wheel and the outside rim of the tire. It is important that the value of the radius closely matches the radius of the render mesh of the wheel. Any mismatch will result in the wheels either hovering above the ground or intersecting the ground.
/// </summary> /// </summary>
API_FIELD() float Radius = 50.0f; API_FIELD(Attributes="EditorOrder(2)") float Radius = 50.0f;
/// <summary> /// <summary>
/// Full width of the wheel in metres. This parameter has no bearing on the handling but is a very useful parameter to have when trying to render debug data relating to the wheel/tire/suspension. /// Full width of the wheel in metres. This parameter has no bearing on the handling but is a very useful parameter to have when trying to render debug data relating to the wheel/tire/suspension.
/// </summary> /// </summary>
API_FIELD() float Width = 20.0f; API_FIELD(Attributes="EditorOrder(3)") float Width = 20.0f;
/// <summary> /// <summary>
/// Max steer angle that can be achieved by the wheel (in degrees). /// Max steer angle that can be achieved by the wheel (in degrees).
/// </summary> /// </summary>
API_FIELD(Attributes="Limit(0)") float MaxSteerAngle = 0.0f; API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(10)") float MaxSteerAngle = 0.0f;
/// <summary> /// <summary>
/// Damping rate applied to wheel. Specified in kilograms metres-squared per second (kg m^2 s^-1). /// Damping rate applied to wheel. Specified in kilograms metres-squared per second (kg m^2 s^-1).
/// </summary> /// </summary>
API_FIELD(Attributes="Limit(0)") float DampingRate = 0.25f; API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(11)") float DampingRate = 0.25f;
/// <summary> /// <summary>
/// Max brake torque that can be applied to wheel. Specified in kilograms metres-squared per second-squared (kg m^2 s^-2) /// Max brake torque that can be applied to wheel. Specified in kilograms metres-squared per second-squared (kg m^2 s^-2)
/// </summary> /// </summary>
API_FIELD(Attributes="Limit(0)") float MaxBrakeTorque = 1500.0f; API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(12)") float MaxBrakeTorque = 1500.0f;
/// <summary> /// <summary>
/// Max handbrake torque that can be applied to wheel. Specified in kilograms metres-squared per second-squared (kg m^2 s^-2) /// Max handbrake torque that can be applied to wheel. Specified in kilograms metres-squared per second-squared (kg m^2 s^-2)
/// </summary> /// </summary>
API_FIELD(Attributes="Limit(0)") float MaxHandBrakeTorque = 2000.0f; API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(13)") float MaxHandBrakeTorque = 2000.0f;
/// <summary> /// <summary>
/// Collider that represents the wheel shape and it's placement. Has to be attached as a child to the vehicle. Triangle mesh collider is not supported (use convex mesh or basic shapes). /// Collider that represents the wheel shape and it's placement. Has to be attached as a child to the vehicle. Triangle mesh collider is not supported (use convex mesh or basic shapes).
/// </summary> /// </summary>
API_FIELD() ScriptingObjectReference<Collider> Collider; API_FIELD(Attributes="EditorOrder(4)") ScriptingObjectReference<Collider> Collider;
/// <summary>
/// Spring damper rate of suspension unit.
/// </summary>
API_FIELD(Attributes="Limit(0), EditorDisplay(\"Suspension\"), EditorOrder(20)") float SuspensionDampingRate = 1.0f;
/// <summary>
/// The maximum offset for the suspension that wheel can go above resting location.
/// </summary>
API_FIELD(Attributes="Limit(0), EditorDisplay(\"Suspension\"), EditorOrder(21)") float SuspensionMaxRaise = 10.0f;
/// <summary>
/// The maximum offset for the suspension that wheel can go below resting location.
/// </summary>
API_FIELD(Attributes="Limit(0), EditorDisplay(\"Suspension\"), EditorOrder(22)") float SuspensionMaxDrop = 10.0f;
/// <summary>
/// The vertical offset from where suspension forces are applied (relative to the vehicle center of mass). The suspension force is applies on the vertical axis going though the wheel center.
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Suspension\"), EditorOrder(23)") float SuspensionForceOffset = 0.0f;
/// <summary>
/// The tire lateral stiffness to have given lateral slip.
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Tire\"), EditorOrder(30)") float TireLateralStiffness = 17.0f;
/// <summary>
/// The maximum tire load (normalized) at which tire cannot provide more lateral stiffness (no matter how much extra load is applied to it).
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Tire\"), EditorOrder(31)") float TireLateralMax = 2.0f;
/// <summary>
/// The tire longitudinal stiffness to have given longitudinal slip.
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Tire\"), EditorOrder(32)") float TireLongitudinalStiffness = 1000.0f;
}; };
/// <summary> /// <summary>
@@ -246,6 +281,33 @@ public:
/// The friction experienced by the tire for the combination of tire type and surface type after accounting. /// The friction experienced by the tire for the combination of tire type and surface type after accounting.
/// </summary> /// </summary>
API_FIELD() float TireFriction = 0.0f; API_FIELD() float TireFriction = 0.0f;
/// <summary>
/// The steer angle (in degrees) of the wheel about the "up" vector accounting for input steer and toe and, if applicable, Ackermann steer correction.
/// </summary>
API_FIELD() float SteerAngle = 0.0f;
/// <summary>
/// The rotation angle (in degrees) about the rolling axis for the specified wheel.
/// </summary>
API_FIELD() float RotationAngle = 0.0f;
/// <summary>
/// The compression of the suspension spring. Offsets the wheel location.
/// </summary>
API_FIELD() float SuspensionOffset = 0.0f;
#if USE_EDITOR
/// <summary>
/// The start location of the suspension raycast start (Editor only for debugging).
/// </summary>
API_FIELD() Vector3 SuspensionTraceStart = Vector3::Zero;
/// <summary>
/// The start location of the suspension raycast end (Editor only for debugging).
/// </summary>
API_FIELD() Vector3 SuspensionTraceEnd = Vector3::Zero;
#endif
}; };
private: private:
@@ -274,6 +336,12 @@ public:
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Vehicle\")") API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Vehicle\")")
bool UseReverseAsBrake = true; bool UseReverseAsBrake = true;
/// <summary>
/// If checked, the vehicle driving and steering inputs will be used as analog values (from gamepad), otherwise will be used as digital input (from keyboard).
/// </summary>
API_FIELD(Attributes="EditorOrder(1), EditorDisplay(\"Vehicle\")")
bool UseAnalogSteering = false;
/// <summary> /// <summary>
/// Gets the vehicle driving model type. /// Gets the vehicle driving model type.
/// </summary> /// </summary>
@@ -105,7 +105,14 @@ void CharacterController::SetStepOffset(float value)
_stepOffset = value; _stepOffset = value;
if (_controller) if (_controller)
_controller->setStepOffset(value); {
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float contactOffset = Math::Max(_contactOffset, ZeroTolerance);
const float minSize = 0.001f;
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, minSize);
_controller->setStepOffset(Math::Min(value, height + radius * 2.0f - minSize));
}
} }
void CharacterController::SetUpDirection(const Vector3& up) void CharacterController::SetUpDirection(const Vector3& up)
@@ -223,7 +230,6 @@ void CharacterController::CreateController()
desc.slopeLimit = Math::Cos(_slopeLimit * DegreesToRadians); desc.slopeLimit = Math::Cos(_slopeLimit * DegreesToRadians);
desc.nonWalkableMode = static_cast<PxControllerNonWalkableMode::Enum>(_nonWalkableMode); desc.nonWalkableMode = static_cast<PxControllerNonWalkableMode::Enum>(_nonWalkableMode);
desc.climbingMode = PxCapsuleClimbingMode::eEASY; desc.climbingMode = PxCapsuleClimbingMode::eEASY;
desc.stepOffset = _stepOffset;
if (Material && !Material->WaitForLoaded()) if (Material && !Material->WaitForLoaded())
desc.material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial(); desc.material = ((PhysicalMaterial*)Material->Instance)->GetPhysXMaterial();
else else
@@ -233,6 +239,7 @@ void CharacterController::CreateController()
const float minSize = 0.001f; const float minSize = 0.001f;
desc.height = Math::Max(Math::Abs(_height) * scaling, minSize); desc.height = Math::Max(Math::Abs(_height) * scaling, minSize);
desc.radius = Math::Max(Math::Abs(_radius) * scaling - desc.contactOffset, minSize); desc.radius = Math::Max(Math::Abs(_radius) * scaling - desc.contactOffset, minSize);
desc.stepOffset = Math::Min(_stepOffset, desc.height + desc.radius * 2.0f - minSize);
// Create controller // Create controller
_controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc); _controller = (PxCapsuleController*)Physics::GetControllerManager()->createController(desc);
+6 -2
View File
@@ -24,6 +24,8 @@
bool CollisionCooking::CookConvexMesh(CookingInput& input, BytesContainer& output) bool CollisionCooking::CookConvexMesh(CookingInput& input, BytesContainer& output)
{ {
ENSURE_CAN_COOK; ENSURE_CAN_COOK;
if (input.VertexCount == 0)
LOG(Warning, "Empty mesh data for collision cooking.");
// Init options // Init options
PxConvexMeshDesc desc; PxConvexMeshDesc desc;
@@ -62,6 +64,8 @@ bool CollisionCooking::CookConvexMesh(CookingInput& input, BytesContainer& outpu
bool CollisionCooking::CookTriangleMesh(CookingInput& input, BytesContainer& output) bool CollisionCooking::CookTriangleMesh(CookingInput& input, BytesContainer& output)
{ {
ENSURE_CAN_COOK; ENSURE_CAN_COOK;
if (input.VertexCount == 0 || input.IndexCount == 0)
LOG(Warning, "Empty mesh data for collision cooking.");
// Init options // Init options
PxTriangleMeshDesc desc; PxTriangleMeshDesc desc;
@@ -274,11 +278,11 @@ bool CollisionCooking::CookCollision(const Argument& arg, CollisionData::Seriali
// Combine meshes into one // Combine meshes into one
int32 vCount = 0; int32 vCount = 0;
for (int32 i = 0; i < vertexCounts.Count(); i++) for (int32 i = 0; i < vertexCounts.Count(); i++)
vCount += vertexCounts[0]; vCount += vertexCounts[i];
finalVertexData.Allocate(vCount); finalVertexData.Allocate(vCount);
int32 iCount = 0; int32 iCount = 0;
for (int32 i = 0; i < indexCounts.Count(); i++) for (int32 i = 0; i < indexCounts.Count(); i++)
iCount += indexCounts[0]; iCount += indexCounts[i];
finalIndexData.Allocate(iCount); finalIndexData.Allocate(iCount);
int32 vertexCounter = 0, indexCounter = 0; int32 vertexCounter = 0, indexCounter = 0;
for (int32 i = 0; i < meshesCount; i++) for (int32 i = 0; i < meshesCount; i++)
+115 -31
View File
@@ -31,6 +31,12 @@
// Temporary memory size used by the PhysX during the simulation. Must be multiply of 4kB and 16bit aligned. // Temporary memory size used by the PhysX during the simulation. Must be multiply of 4kB and 16bit aligned.
#define SCRATCH_BLOCK_SIZE (1024 * 128) #define SCRATCH_BLOCK_SIZE (1024 * 128)
#define PHYSX_VEHICLE_DEBUG_TELEMETRY 0
#if PHYSX_VEHICLE_DEBUG_TELEMETRY
#include "Engine/Core/Utilities.h"
#endif
class PhysXAllocator : public PxAllocatorCallback class PhysXAllocator : public PxAllocatorCallback
{ {
public: public:
@@ -661,6 +667,8 @@ void Physics::CollectResults()
int32 wheelsCount = 0; int32 wheelsCount = 0;
for (auto wheelVehicle : WheelVehicles) for (auto wheelVehicle : WheelVehicles)
{ {
if (!wheelVehicle->IsActiveInHierarchy())
continue;
auto drive = (PxVehicleWheels*)wheelVehicle->_drive; auto drive = (PxVehicleWheels*)wheelVehicle->_drive;
ASSERT(drive); ASSERT(drive);
WheelVehiclesCache.Add(drive); WheelVehiclesCache.Add(drive);
@@ -742,6 +750,23 @@ void Physics::CollectResults()
5.0f, // fall rate eANALOG_INPUT_STEER_RIGHT 5.0f, // fall rate eANALOG_INPUT_STEER_RIGHT
} }
}; };
PxVehicleKeySmoothingData keySmoothing =
{
{
3.0f, // rise rate eANALOG_INPUT_ACCEL
3.0f, // rise rate eANALOG_INPUT_BRAKE
10.0f, // rise rate eANALOG_INPUT_HANDBRAKE
2.5f, // rise rate eANALOG_INPUT_STEER_LEFT
2.5f, // rise rate eANALOG_INPUT_STEER_RIGHT
},
{
5.0f, // fall rate eANALOG_INPUT__ACCEL
5.0f, // fall rate eANALOG_INPUT__BRAKE
10.0f, // fall rate eANALOG_INPUT__HANDBRAKE
5.0f, // fall rate eANALOG_INPUT_STEER_LEFT
5.0f // fall rate eANALOG_INPUT_STEER_RIGHT
}
};
// Reference: PhysX SDK docs // Reference: PhysX SDK docs
// TODO: expose steer vs forward curve into per-vehicle (up to 8 points, values clamped into 0/1 range) // TODO: expose steer vs forward curve into per-vehicle (up to 8 points, values clamped into 0/1 range)
static constexpr PxF32 steerVsForwardSpeedData[] = static constexpr PxF32 steerVsForwardSpeedData[] =
@@ -757,28 +782,60 @@ void Physics::CollectResults()
}; };
const PxFixedSizeLookupTable<8> steerVsForwardSpeed(steerVsForwardSpeedData, 4); const PxFixedSizeLookupTable<8> steerVsForwardSpeed(steerVsForwardSpeedData, 4);
// @formatter:on // @formatter:on
switch (wheelVehicle->_driveTypeCurrent) if (wheelVehicle->UseAnalogSteering)
{ {
case WheeledVehicle::DriveTypes::Drive4W: switch (wheelVehicle->_driveTypeCurrent)
{ {
PxVehicleDrive4WRawInputData rawInputData; case WheeledVehicle::DriveTypes::Drive4W:
rawInputData.setAnalogAccel(throttle); {
rawInputData.setAnalogBrake(brake); PxVehicleDrive4WRawInputData rawInputData;
rawInputData.setAnalogSteer(wheelVehicle->_steering); rawInputData.setAnalogAccel(throttle);
rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake); rawInputData.setAnalogBrake(brake);
PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDrive4W*)drive); rawInputData.setAnalogSteer(wheelVehicle->_steering);
break; rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDrive4W*)drive);
break;
}
case WheeledVehicle::DriveTypes::DriveNW:
{
PxVehicleDriveNWRawInputData rawInputData;
rawInputData.setAnalogAccel(throttle);
rawInputData.setAnalogBrake(brake);
rawInputData.setAnalogSteer(wheelVehicle->_steering);
rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDriveNW*)drive);
break;
}
}
} }
case WheeledVehicle::DriveTypes::DriveNW: else
{ {
PxVehicleDriveNWRawInputData rawInputData; const float deadZone = 0.1f;
rawInputData.setAnalogAccel(throttle); switch (wheelVehicle->_driveTypeCurrent)
rawInputData.setAnalogBrake(brake); {
rawInputData.setAnalogSteer(wheelVehicle->_steering); case WheeledVehicle::DriveTypes::Drive4W:
rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake); {
PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDriveNW*)drive); PxVehicleDrive4WRawInputData rawInputData;
break; rawInputData.setDigitalAccel(throttle > deadZone);
} rawInputData.setDigitalBrake(brake > deadZone);
rawInputData.setDigitalSteerLeft(wheelVehicle->_steering < -deadZone);
rawInputData.setDigitalSteerRight(wheelVehicle->_steering > deadZone);
rawInputData.setDigitalHandbrake(wheelVehicle->_handBrake > deadZone);
PxVehicleDrive4WSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDrive4W*)drive);
break;
}
case WheeledVehicle::DriveTypes::DriveNW:
{
PxVehicleDriveNWRawInputData rawInputData;
rawInputData.setDigitalAccel(throttle > deadZone);
rawInputData.setDigitalBrake(brake > deadZone);
rawInputData.setDigitalSteerLeft(wheelVehicle->_steering < -deadZone);
rawInputData.setDigitalSteerRight(wheelVehicle->_steering > deadZone);
rawInputData.setDigitalHandbrake(wheelVehicle->_handBrake > deadZone);
PxVehicleDriveNWSmoothDigitalRawInputsAndSetAnalogInputs(keySmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDriveNW*)drive);
break;
}
}
} }
} }
@@ -787,8 +844,8 @@ void Physics::CollectResults()
{ {
if (WheelRaycastBatchQuery) if (WheelRaycastBatchQuery)
WheelRaycastBatchQuery->release(); WheelRaycastBatchQuery->release();
WheelQueryResults.Resize(wheelsCount); WheelQueryResults.Resize(wheelsCount, false);
WheelQueryResults.Resize(WheelQueryResults.Capacity()); WheelHitResults.Resize(wheelsCount, false);
PxBatchQueryDesc desc(wheelsCount, 0, 0); PxBatchQueryDesc desc(wheelsCount, 0, 0);
desc.queryMemory.userRaycastResultBuffer = WheelQueryResults.Get(); desc.queryMemory.userRaycastResultBuffer = WheelQueryResults.Get();
desc.queryMemory.userRaycastTouchBuffer = WheelHitResults.Get(); desc.queryMemory.userRaycastTouchBuffer = WheelHitResults.Get();
@@ -810,13 +867,17 @@ void Physics::CollectResults()
} }
// Setup cache for wheel states // Setup cache for wheel states
WheelVehiclesResultsPerVehicle.Resize(WheelVehicles.Count(), false); WheelVehiclesResultsPerVehicle.Resize(WheelVehiclesCache.Count(), false);
WheelVehiclesResultsPerWheel.Resize(wheelsCount, false); WheelVehiclesResultsPerWheel.Resize(wheelsCount, false);
wheelsCount = 0; wheelsCount = 0;
for (int32 i = 0; i < WheelVehicles.Count(); i++) for (int32 i = 0, ii = 0; i < WheelVehicles.Count(); i++)
{ {
auto drive = (PxVehicleWheels*)WheelVehicles[i]->_drive; auto wheelVehicle = WheelVehicles[i];
auto& perVehicle = WheelVehiclesResultsPerVehicle[i]; if (!wheelVehicle->IsActiveInHierarchy())
continue;
auto drive = (PxVehicleWheels*)WheelVehicles[ii]->_drive;
auto& perVehicle = WheelVehiclesResultsPerVehicle[ii];
ii++;
perVehicle.nbWheelQueryResults = drive->mWheelsSimData.getNbWheels(); perVehicle.nbWheelQueryResults = drive->mWheelsSimData.getNbWheels();
perVehicle.wheelQueryResults = WheelVehiclesResultsPerWheel.Get() + wheelsCount; perVehicle.wheelQueryResults = WheelVehiclesResultsPerWheel.Get() + wheelsCount;
wheelsCount += perVehicle.nbWheelQueryResults; wheelsCount += perVehicle.nbWheelQueryResults;
@@ -827,17 +888,26 @@ void Physics::CollectResults()
PxVehicleUpdates(LastDeltaTime, PhysicsScene->getGravity(), *WheelTireFrictions, WheelVehiclesCache.Count(), WheelVehiclesCache.Get(), WheelVehiclesResultsPerVehicle.Get()); PxVehicleUpdates(LastDeltaTime, PhysicsScene->getGravity(), *WheelTireFrictions, WheelVehiclesCache.Count(), WheelVehiclesCache.Get(), WheelVehiclesResultsPerVehicle.Get());
// Synchronize state // Synchronize state
for (int32 i = 0; i < WheelVehicles.Count(); i++) for (int32 i = 0, ii = 0; i < WheelVehicles.Count(); i++)
{ {
auto wheelVehicle = WheelVehicles[i]; auto wheelVehicle = WheelVehicles[i];
auto drive = WheelVehiclesCache[i]; if (!wheelVehicle->IsActiveInHierarchy())
auto& perVehicle = WheelVehiclesResultsPerVehicle[i]; continue;
auto drive = WheelVehiclesCache[ii];
auto& perVehicle = WheelVehiclesResultsPerVehicle[ii];
ii++;
#if PHYSX_VEHICLE_DEBUG_TELEMETRY
LOG(Info, "Vehicle[{}] Gear={}, RPM={}", ii, wheelVehicle->GetCurrentGear(), (int32)wheelVehicle->GetEngineRotationSpeed());
#endif
// Update wheels // Update wheels
for (int32 j = 0; j < wheelVehicle->_wheelsData.Count(); j++) for (int32 j = 0; j < wheelVehicle->_wheelsData.Count(); j++)
{ {
auto& wheelData = wheelVehicle->_wheelsData[j]; auto& wheelData = wheelVehicle->_wheelsData[j];
auto& perWheel = perVehicle.wheelQueryResults[j]; auto& perWheel = perVehicle.wheelQueryResults[j];
#if PHYSX_VEHICLE_DEBUG_TELEMETRY
LOG(Info, "Vehicle[{}] Wheel[{}] longitudinalSlip={}, lateralSlip={}, suspSpringForce={}", ii, j, Utilities::RoundTo2DecimalPlaces(perWheel.longitudinalSlip), Utilities::RoundTo2DecimalPlaces(perWheel.lateralSlip), (int32)perWheel.suspSpringForce);
#endif
auto& state = wheelData.State; auto& state = wheelData.State;
state.IsInAir = perWheel.isInAir; state.IsInAir = perWheel.isInAir;
@@ -845,10 +915,24 @@ void Physics::CollectResults()
state.TireContactPoint = P2C(perWheel.tireContactPoint); state.TireContactPoint = P2C(perWheel.tireContactPoint);
state.TireContactNormal = P2C(perWheel.tireContactNormal); state.TireContactNormal = P2C(perWheel.tireContactNormal);
state.TireFriction = perWheel.tireFriction; state.TireFriction = perWheel.tireFriction;
state.SteerAngle = RadiansToDegrees * perWheel.steerAngle;
state.RotationAngle = -RadiansToDegrees * drive->mWheelsDynData.getWheelRotationAngle(j);
state.SuspensionOffset = perWheel.suspJounce;
#if USE_EDITOR
state.SuspensionTraceStart = P2C(perWheel.suspLineStart);
state.SuspensionTraceEnd = P2C(perWheel.suspLineStart + perWheel.suspLineDir * perWheel.suspLineLength);
#endif
const float wheelRotationAngle = -RadiansToDegrees * drive->mWheelsDynData.getWheelRotationAngle(j); if (!wheelData.Collider)
const float wheelSteerAngle = RadiansToDegrees * perWheel.steerAngle; continue;
wheelData.Collider->SetLocalOrientation(Quaternion::Euler(0, wheelSteerAngle, wheelRotationAngle) * wheelData.LocalOrientation); auto shape = wheelData.Collider->GetPxShape();
// Update wheel collider transformation
auto localPose = shape->getLocalPose();
Transform t = wheelData.Collider->GetLocalTransform();
t.Orientation = Quaternion::Euler(0, state.SteerAngle, state.RotationAngle) * wheelData.LocalOrientation;
t.Translation = P2C(localPose.p) / wheelVehicle->GetScale() - t.Orientation * wheelData.Collider->GetCenter();
wheelData.Collider->SetLocalTransform(t);
} }
} }
} }
@@ -61,7 +61,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(CreateWindowSettings);
/// <summary> /// <summary>
/// The maximum size. /// The maximum size.
/// </summary> /// </summary>
API_FIELD() Vector2 MaximumSize = Vector2(4096, 4096); API_FIELD() Vector2 MaximumSize = Vector2(8192, 4096);
/// <summary> /// <summary>
/// The start position mode. /// The start position mode.
+12 -14
View File
@@ -386,33 +386,31 @@ struct TIsPODType<ProfilerCPU::Event>
enum { Value = true }; enum { Value = true };
}; };
#include "ProfilerSrcLoc.h"
// Shortcut macros for profiling a single code block execution on CPU // Shortcut macros for profiling a single code block execution on CPU
// Use ZoneTransient for Tracy for code that can be hot-reloaded (eg. in Editor) or if name can be a variable // Use ZoneTransient for Tracy for code that can be hot-reloaded (eg. in Editor) or if name can be a variable
#define PROFILE_CPU_USE_TRANSIENT_DATA 0
#define PROFILE_CPU_NAMED(name) ZoneTransientN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(name) #if PROFILE_CPU_USE_TRANSIENT_DATA
#if defined(_MSC_VER)
#if USE_EDITOR
#define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__))
#else
#define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(TEXT(__FUNCTION__))
#endif
#else
#if USE_EDITOR
#define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(__FUNCTION__) #define PROFILE_CPU() ZoneTransient(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(__FUNCTION__)
#define PROFILE_CPU_NAMED(name) ZoneTransientN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(name)
#else #else
#define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(__FUNCTION__) #define PROFILE_CPU() ZoneNamed(___tracy_scoped_zone, true); ScopeProfileBlockCPU ProfileBlockCPU(__FUNCTION__)
#define PROFILE_CPU_NAMED(name) ZoneNamedN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(name)
#endif #endif
#ifdef TRACY_ENABLE
#define PROFILE_CPU_SRC_LOC(srcLoc) tracy::ScopedZone ___tracy_scoped_zone( (tracy::SourceLocationData*)&(srcLoc) ); ScopeProfileBlockCPU ProfileBlockCPU((srcLoc).name)
#else
#define PROFILE_CPU_SRC_LOC(srcLoc) ScopeProfileBlockCPU ProfileBlockCPU((srcLoc).name)
#endif #endif
#else #else
// Empty macros for disabled profiler // Empty macros for disabled profiler
#define PROFILE_CPU_NAMED(name)
#define PROFILE_CPU() #define PROFILE_CPU()
#define PROFILE_CPU_NAMED(name)
#define PROFILE_CPU_SRC_LOC(srcLoc)
#endif #endif
+18
View File
@@ -0,0 +1,18 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if COMPILE_WITH_PROFILER
#include "Engine/Core/Types/BaseTypes.h"
struct FLAXENGINE_API SourceLocationData
{
const char* name;
const char* function;
const char* file;
uint32 line;
uint32 color;
};
#endif
+1
View File
@@ -492,6 +492,7 @@ ScriptingObject* ManagedBinaryModule::ManagedObjectSpawn(const ScriptingObjectSp
ScriptingObject* object = nativeTypePtr->Script.Spawn(params); ScriptingObject* object = nativeTypePtr->Script.Spawn(params);
if (!object) if (!object)
{ {
LOG(Error, "Failed to spawn object of type {0} with native base type {1}.", managedTypePtr->ToString(), nativeTypePtr->ToString());
return nullptr; return nullptr;
} }
@@ -60,18 +60,23 @@ MMethod::MMethod(MonoMethod* monoMethod, const char* name, MClass* parentClass)
ProfilerName.Get()[className.Length()] = ':'; ProfilerName.Get()[className.Length()] = ':';
ProfilerName.Get()[className.Length() + 1] = ':'; ProfilerName.Get()[className.Length() + 1] = ':';
Platform::MemoryCopy(ProfilerName.Get() + className.Length() + 2, _name.Get(), _name.Length()); Platform::MemoryCopy(ProfilerName.Get() + className.Length() + 2, _name.Get(), _name.Length());
ProfilerData.name = ProfilerName.Get();
ProfilerData.function = _name.Get();
ProfilerData.file = nullptr;
ProfilerData.line = 0;
ProfilerData.color = 0;
#endif #endif
} }
MonoObject* MMethod::Invoke(void* instance, void** params, MonoObject** exception) const MonoObject* MMethod::Invoke(void* instance, void** params, MonoObject** exception) const
{ {
PROFILE_CPU_NAMED(*ProfilerName); PROFILE_CPU_SRC_LOC(ProfilerData);
return mono_runtime_invoke(_monoMethod, instance, params, exception); return mono_runtime_invoke(_monoMethod, instance, params, exception);
} }
MonoObject* MMethod::InvokeVirtual(MonoObject* instance, void** params, MonoObject** exception) const MonoObject* MMethod::InvokeVirtual(MonoObject* instance, void** params, MonoObject** exception) const
{ {
PROFILE_CPU_NAMED(*ProfilerName); PROFILE_CPU_SRC_LOC(ProfilerData);
MonoMethod* virtualMethod = mono_object_get_virtual_method(instance, _monoMethod); MonoMethod* virtualMethod = mono_object_get_virtual_method(instance, _monoMethod);
return mono_runtime_invoke(virtualMethod, instance, params, exception); return mono_runtime_invoke(virtualMethod, instance, params, exception);
} }
@@ -3,6 +3,9 @@
#pragma once #pragma once
#include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Array.h"
#if COMPILE_WITH_PROFILER
#include "Engine/Profiler/ProfilerSrcLoc.h"
#endif
#include "MTypes.h" #include "MTypes.h"
/// <summary> /// <summary>
@@ -42,6 +45,7 @@ public:
#if COMPILE_WITH_PROFILER #if COMPILE_WITH_PROFILER
MString ProfilerName; MString ProfilerName;
SourceLocationData ProfilerData;
#endif #endif
#if USE_MONO #if USE_MONO
+44 -1
View File
@@ -9,13 +9,29 @@
#include "Engine/Scripting/ManagedCLR/MUtils.h" #include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Core/ObjectsRemovalService.h" #include "Engine/Core/ObjectsRemovalService.h"
#include "Engine/Profiler/Profiler.h" #include "Engine/Profiler/Profiler.h"
#if TRACY_ENABLE && !PROFILE_CPU_USE_TRANSIENT_DATA
#include "Engine/Core/Collections/ChunkedArray.h"
#endif
#include "Engine/Core/Types/Pair.h"
#include "Engine/Threading/Threading.h" #include "Engine/Threading/Threading.h"
#include <ThirdParty/mono-2.0/mono/metadata/mono-gc.h> #include <ThirdParty/mono-2.0/mono/metadata/mono-gc.h>
namespace ProfilerInternal namespace ProfilerInternal
{ {
#if COMPILE_WITH_PROFILER #if COMPILE_WITH_PROFILER
Array<int32> ManagedEventsGPU; Array<int32, InlinedAllocation<32>> ManagedEventsGPU;
#if TRACY_ENABLE && !PROFILE_CPU_USE_TRANSIENT_DATA
CriticalSection ManagedSourceLocationsLocker;
struct Location
{
String Name;
StringAnsi NameAnsi;
tracy::SourceLocationData SrcLocation;
};
ChunkedArray<Location, 256> ManagedSourceLocations;
#endif
#endif #endif
void BeginEvent(MonoString* nameObj) void BeginEvent(MonoString* nameObj)
@@ -24,7 +40,34 @@ namespace ProfilerInternal
const StringView name((const Char*)mono_string_chars(nameObj), mono_string_length(nameObj)); const StringView name((const Char*)mono_string_chars(nameObj), mono_string_length(nameObj));
ProfilerCPU::BeginEvent(*name); ProfilerCPU::BeginEvent(*name);
#if TRACY_ENABLE #if TRACY_ENABLE
#if PROFILE_CPU_USE_TRANSIENT_DATA
tracy::ScopedZone::Begin(__LINE__, __FILE__, strlen( __FILE__ ), __FUNCTION__, strlen( __FUNCTION__ ), name.Get(), name.Length() ); tracy::ScopedZone::Begin(__LINE__, __FILE__, strlen( __FILE__ ), __FUNCTION__, strlen( __FUNCTION__ ), name.Get(), name.Length() );
#else
ScopeLock lock(ManagedSourceLocationsLocker);
tracy::SourceLocationData* srcLoc = nullptr;
for (auto e = ManagedSourceLocations.Begin(); e.IsNotEnd(); ++e)
{
if (name == e->Name)
{
srcLoc = &e->SrcLocation;
break;
}
}
if (!srcLoc)
{
auto& e = ManagedSourceLocations.AddOne();
e.Name = name;
e.NameAnsi = name.Get();
srcLoc = &e.SrcLocation;
srcLoc->name = e.NameAnsi.Get();
srcLoc->function = nullptr;
srcLoc->file = nullptr;
srcLoc->line = 0;
srcLoc->color = 0;
}
//static constexpr tracy::SourceLocationData tracySrcLoc{ nullptr, __FUNCTION__, __FILE__, (uint32_t)__LINE__, 0 };
tracy::ScopedZone::Begin(srcLoc);
#endif
#endif #endif
#endif #endif
} }
+14 -3
View File
@@ -212,6 +212,7 @@ namespace FlaxEngine.Json
break; break;
} }
case JsonToken.Comment: break; case JsonToken.Comment: break;
case JsonToken.String: break;
default: return value; default: return value;
} }
} }
@@ -244,16 +245,21 @@ namespace FlaxEngine.Json
/// <inheritdoc /> /// <inheritdoc />
public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
{ {
#if FLAX_EDITOR
bool writeTypename = (serializer.TypeNameHandling & TypeNameHandling.Objects) == TypeNameHandling.Objects;
#else
bool writeTypename = false;
#endif
var str = (LocalizedString)value; var str = (LocalizedString)value;
if (string.IsNullOrEmpty(str.Id)) if (string.IsNullOrEmpty(str.Id) && !writeTypename)
{ {
writer.WriteValue(str.Value); writer.WriteValue(str.Value ?? string.Empty);
} }
else else
{ {
writer.WriteStartObject(); writer.WriteStartObject();
#if FLAX_EDITOR #if FLAX_EDITOR
if ((serializer.TypeNameHandling & TypeNameHandling.Objects) == TypeNameHandling.Objects) if (writeTypename)
{ {
writer.WritePropertyName("$type"); writer.WritePropertyName("$type");
writer.WriteValue("FlaxEngine.LocalizedString, FlaxEngine.CSharp"); writer.WriteValue("FlaxEngine.LocalizedString, FlaxEngine.CSharp");
@@ -297,10 +303,15 @@ namespace FlaxEngine.Json
break; break;
} }
case JsonToken.Comment: break; case JsonToken.Comment: break;
case JsonToken.String: break;
default: return str; default: return str;
} }
} }
} }
else
return null;
if (existingValue == null && str.Id == null && str.Value == null)
return null;
return str; return str;
} }
+7 -3
View File
@@ -134,9 +134,13 @@ void JobSystemService::Dispose()
for (int32 i = 0; i < ThreadsCount; i++) for (int32 i = 0; i < ThreadsCount; i++)
{ {
if (Threads[i] && Threads[i]->IsRunning()) if (Threads[i])
Threads[i]->Kill(true); {
Threads[i] = nullptr; if (Threads[i]->IsRunning())
Threads[i]->Kill(true);
Delete(Threads[i]);
Threads[i] = nullptr;
}
} }
} }
+1
View File
@@ -140,6 +140,7 @@ namespace FlaxEngine.GUI
{ {
_targetValue = value; _targetValue = value;
_value = value; _value = value;
SetUpdate(ref _update, null);
OnValueChanged(); OnValueChanged();
} }
} }
+2 -2
View File
@@ -13,5 +13,5 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
[assembly: Guid("b8442186-4a70-7c85-704a-857c262d00f6")] [assembly: Guid("b8442186-4a70-7c85-704a-857c262d00f6")]
[assembly: AssemblyVersion("1.2.6222")] [assembly: AssemblyVersion("1.2.6224")]
[assembly: AssemblyFileVersion("1.2.6222")] [assembly: AssemblyFileVersion("1.2.6224")]
+3 -3
View File
@@ -3,11 +3,11 @@
#pragma once #pragma once
#define FLAXENGINE_NAME "FlaxEngine" #define FLAXENGINE_NAME "FlaxEngine"
#define FLAXENGINE_VERSION Version(1, 2, 6222) #define FLAXENGINE_VERSION Version(1, 2, 6224)
#define FLAXENGINE_VERSION_TEXT "1.2.6222" #define FLAXENGINE_VERSION_TEXT "1.2.6224"
#define FLAXENGINE_VERSION_MAJOR 1 #define FLAXENGINE_VERSION_MAJOR 1
#define FLAXENGINE_VERSION_MINOR 2 #define FLAXENGINE_VERSION_MINOR 2
#define FLAXENGINE_VERSION_BUILD 6222 #define FLAXENGINE_VERSION_BUILD 6224
#define FLAXENGINE_COMPANY "Flax" #define FLAXENGINE_COMPANY "Flax"
#define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved." #define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved."
+2
View File
@@ -228,6 +228,7 @@ static int64_t SetupHwTimer()
uint32_t regs[4]; uint32_t regs[4];
CpuId( regs, 1 ); CpuId( regs, 1 );
if( !( regs[3] & ( 1 << 4 ) ) ) InitFailure( "CPU doesn't support RDTSC instruction." ); if( !( regs[3] & ( 1 << 4 ) ) ) InitFailure( "CPU doesn't support RDTSC instruction." );
#if !defined TRACY_NO_INVARIANT_CHECK
CpuId( regs, 0x80000007 ); CpuId( regs, 0x80000007 );
if( !( regs[3] & ( 1 << 8 ) ) ) if( !( regs[3] & ( 1 << 8 ) ) )
{ {
@@ -241,6 +242,7 @@ static int64_t SetupHwTimer()
#endif #endif
} }
} }
#endif
#endif #endif
return Profiler::GetTime(); return Profiler::GetTime();
+17
View File
@@ -12,8 +12,22 @@
namespace tracy namespace tracy
{ {
void ScopedZone::Begin(const SourceLocationData* srcloc)
{
#ifdef TRACY_ON_DEMAND
if (!GetProfiler().IsConnected()) return;
#endif
TracyLfqPrepare( QueueType::ZoneBegin );
MemWrite( &item->zoneBegin.time, Profiler::GetTime() );
MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc );
TracyLfqCommit;
}
void ScopedZone::Begin(uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const Char* name, size_t nameSz) void ScopedZone::Begin(uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const Char* name, size_t nameSz)
{ {
#ifdef TRACY_ON_DEMAND
if (!GetProfiler().IsConnected()) return;
#endif
TracyLfqPrepare( QueueType::ZoneBeginAllocSrcLoc ); TracyLfqPrepare( QueueType::ZoneBeginAllocSrcLoc );
const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz ); const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz );
MemWrite( &item->zoneBegin.time, Profiler::GetTime() ); MemWrite( &item->zoneBegin.time, Profiler::GetTime() );
@@ -23,6 +37,9 @@ void ScopedZone::Begin(uint32_t line, const char* source, size_t sourceSz, const
void ScopedZone::End() void ScopedZone::End()
{ {
#ifdef TRACY_ON_DEMAND
if (!GetProfiler().IsConnected()) return;
#endif
TracyLfqPrepare( QueueType::ZoneEnd ); TracyLfqPrepare( QueueType::ZoneEnd );
MemWrite( &item->zoneEnd.time, Profiler::GetTime() ); MemWrite( &item->zoneEnd.time, Profiler::GetTime() );
TracyLfqCommit; TracyLfqCommit;
+1
View File
@@ -46,6 +46,7 @@ struct TRACY_API SourceLocationData
class TRACY_API ScopedZone class TRACY_API ScopedZone
{ {
public: public:
static void Begin( const SourceLocationData* srcloc );
static void Begin( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const Char* name, size_t nameSz ); static void Begin( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const Char* name, size_t nameSz );
static void End(); static void End();
+3
View File
@@ -34,7 +34,10 @@ public class tracy : ThirdPartyModule
options.PublicDefinitions.Add("TRACY_ENABLE"); options.PublicDefinitions.Add("TRACY_ENABLE");
if (options.Platform.Target == TargetPlatform.Windows) if (options.Platform.Target == TargetPlatform.Windows)
{
options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=DbgHelp"); options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=DbgHelp");
options.PrivateDefinitions.Add("TRACY_NO_INVARIANT_CHECK");
}
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -665,7 +665,11 @@ namespace Flax.Build.Bindings
indent += " "; indent += " ";
var eventInstance = eventInfo.IsStatic ? string.Empty : "__unmanagedPtr, "; var eventInstance = eventInfo.IsStatic ? string.Empty : "__unmanagedPtr, ";
contents.Append(indent).Append($"add {{ Internal_{eventInfo.Name} += value; if (Internal_{eventInfo.Name}_Count++ == 0) Internal_{eventInfo.Name}_Bind({eventInstance}true); }}").AppendLine(); contents.Append(indent).Append($"add {{ Internal_{eventInfo.Name} += value; if (Internal_{eventInfo.Name}_Count++ == 0) Internal_{eventInfo.Name}_Bind({eventInstance}true); }}").AppendLine();
contents.Append(indent).Append($"remove {{ Internal_{eventInfo.Name} -= value; if (--Internal_{eventInfo.Name}_Count == 0) Internal_{eventInfo.Name}_Bind({eventInstance}false); }}").AppendLine(); contents.Append(indent).Append("remove { ").AppendLine();
contents.Append("#if FLAX_EDITOR || BUILD_DEBUG").AppendLine();
contents.Append(indent).Append($"if (Internal_{eventInfo.Name} != null) {{ bool invalid = true; foreach (Delegate e in Internal_{eventInfo.Name}.GetInvocationList()) {{ if (e == (Delegate)value) {{ invalid = false; break; }} }} if (invalid) throw new Exception(\"Cannot unregister from event if not registered before.\"); }}").AppendLine();
contents.Append("#endif").AppendLine();
contents.Append(indent).Append($"Internal_{eventInfo.Name} -= value; if (--Internal_{eventInfo.Name}_Count == 0) Internal_{eventInfo.Name}_Bind({eventInstance}false); }}").AppendLine();
indent = indent.Substring(0, indent.Length - 4); indent = indent.Substring(0, indent.Length - 4);
contents.Append(indent).Append('}').AppendLine(); contents.Append(indent).Append('}').AppendLine();
@@ -1028,7 +1028,7 @@ namespace Flax.Build.Bindings
contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;"); contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;");
contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableIndex}]);"); contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableIndex}]);");
contents.AppendLine($" auto method = scriptVTable[{scriptVTableIndex}];"); contents.AppendLine($" auto method = scriptVTable[{scriptVTableIndex}];");
contents.AppendLine(" PROFILE_CPU_NAMED(*method->ProfilerName);"); contents.AppendLine($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::{functionInfo.Name}\");");
contents.AppendLine(" MonoObject* exception = nullptr;"); contents.AppendLine(" MonoObject* exception = nullptr;");
contents.AppendLine(" auto prevWrapperCallInstance = WrapperCallInstance;"); contents.AppendLine(" auto prevWrapperCallInstance = WrapperCallInstance;");
@@ -1290,7 +1290,7 @@ namespace Flax.Build.Bindings
contents.Append(" if (!mmethod)").AppendLine(); contents.Append(" if (!mmethod)").AppendLine();
contents.AppendFormat(" mmethod = {1}::TypeInitializer.GetType().ManagedClass->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine(); contents.AppendFormat(" mmethod = {1}::TypeInitializer.GetType().ManagedClass->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine();
contents.Append(" CHECK(mmethod);").AppendLine(); contents.Append(" CHECK(mmethod);").AppendLine();
contents.Append(" PROFILE_CPU_NAMED(*mmethod->ProfilerName);").AppendLine(); contents.Append($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::On{eventInfo.Name}\");").AppendLine();
contents.Append(" MonoObject* exception = nullptr;").AppendLine(); contents.Append(" MonoObject* exception = nullptr;").AppendLine();
if (paramsCount == 0) if (paramsCount == 0)
contents.AppendLine(" void** params = nullptr;"); contents.AppendLine(" void** params = nullptr;");

Some files were not shown because too many files have changed in this diff Show More