Compare commits

...

44 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
77 changed files with 1126 additions and 301 deletions
@@ -194,16 +194,16 @@ void PS_Decal(
Out2 = float4(material.Emissive, material.Opacity);
#if USE_NORMAL
// GBuffer1
Out3 = float4(material.WorldNormal * 0.5f + 0.5f, 1);
Out3 = float4(material.WorldNormal * 0.5f + 0.5f, material.Opacity);
#endif
#elif USE_NORMAL
// GBuffer1
Out2 = float4(material.WorldNormal * 0.5f + 0.5f, 1);
Out2 = float4(material.WorldNormal * 0.5f + 0.5f, material.Opacity);
#endif
#elif DECAL_BLEND_MODE == DECAL_BLEND_MODE_STAIN
Out0 = float4(material.Color, material.Opacity);
#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
Out0 = float4(material.Emissive * material.Opacity, material.Opacity);
#else
+1 -1
View File
@@ -3,7 +3,7 @@
"Version": {
"Major": 1,
"Minor": 2,
"Build": 6223
"Build": 6224
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.",
+7 -9
View File
@@ -101,11 +101,6 @@ namespace FlaxEditor.CustomEditors
/// </summary>
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>
/// 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>
@@ -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)
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 = obj._parent;
@@ -301,12 +296,15 @@ namespace FlaxEditor.CustomEditors
_isSetBlocked = false;
// Update children
if (_skipChildrenRefresh)
{
_skipChildrenRefresh = false;
return;
}
try
{
var childrenCount = _skipChildrenRefresh ? 0 : _children.Count;
for (int i = 0; i < childrenCount; i++)
for (int i = 0; i < _children.Count; i++)
_children[i].RefreshInternal();
_skipChildrenRefresh = false;
}
catch (TargetException ex)
{
@@ -154,6 +154,10 @@ namespace FlaxEditor.CustomEditors.Editors
var panel = layout.VerticalPanel();
panel.Panel.BackgroundColor = _background;
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)
{
for (int i = 0; i < size; i++)
@@ -178,7 +182,9 @@ namespace FlaxEditor.CustomEditors.Editors
}
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
@@ -194,7 +200,9 @@ namespace FlaxEditor.CustomEditors.Editors
}
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;
var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType<object>();
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++)
{
if (i != 0 && spacing > 0f)
@@ -239,7 +244,9 @@ namespace FlaxEditor.CustomEditors.Editors
var key = keys.ElementAt(i);
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;
@@ -96,14 +96,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Update tooltip
if (_value is SceneObject sceneObject)
{
var str = sceneObject is Actor actor ? actor.Name : type.Name;
var o = sceneObject.Parent;
while (o)
{
str = o.Name + " -> " + str;
o = o.Parent;
}
TooltipText = str;
TooltipText = Utilities.Utils.GetTooltip(sceneObject);
}
else
{
@@ -228,6 +228,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
private VisibleIfCache[] _visibleIfCaches;
private bool _isNull;
/// <summary>
/// Gets the items for the type
@@ -264,7 +265,7 @@ namespace FlaxEditor.CustomEditors.Editors
// Skip properties without getter or setter
if (!p.HasGet || (!p.HasSet && !showInEditor))
continue;
// Skip hidden fields, handle special attributes
if ((!p.IsPublic && !showInEditor) || attributes.Any(x => x is HideInEditorAttribute))
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 />
public override void Initialize(LayoutElementsContainer layout)
{
@@ -446,12 +455,7 @@ namespace FlaxEditor.CustomEditors.Editors
Parent = layout.ContainerControl,
Location = new Vector2(layout.ContainerControl.Width - ButtonSize - 4, (layout.ContainerControl.Height - ButtonSize) * 0.5f),
};
button.Clicked += () =>
{
var newType = Values.Type;
SetValue(newType.CreateInstance());
RebuildLayoutOnRefresh();
};
button.Clicked += () => SetValue(Values.Type.CreateInstance());
}
layout.Label("<null>");
@@ -558,6 +562,13 @@ namespace FlaxEditor.CustomEditors.Editors
/// <inheritdoc />
public override void Refresh()
{
// Automatic refresh when value nullability changed
if (_isNull != Values.IsNull)
{
RebuildLayout();
return;
}
if (_visibleIfCaches != null)
{
try
@@ -76,14 +76,7 @@ namespace FlaxEditor.CustomEditors.Editors
if (_value)
{
_valueName = _value.Name;
TooltipText = _value.TypeName;
var attributes = _value.GetAttributes(false);
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
if (tooltipAttribute != null)
{
TooltipText += "\n" + tooltipAttribute.Text;
}
TooltipText = Surface.SurfaceUtils.GetVisualScriptTypeDescription(_value);
}
else
{
@@ -30,6 +30,11 @@ namespace FlaxEditor.CustomEditors
/// </summary>
public readonly List<LayoutElement> Children = new List<LayoutElement>();
/// <summary>
/// The child custom editors.
/// </summary>
public readonly List<CustomEditor> Editors = new List<CustomEditor>();
/// <summary>
/// Gets the control represented by this element.
/// </summary>
@@ -722,6 +727,7 @@ namespace FlaxEditor.CustomEditors
var customEditor = CustomEditor.CurrentCustomEditor;
Assert.IsNotNull(customEditor);
customEditor.OnChildCreated(editor);
Editors.Add(editor);
}
/// <summary>
@@ -730,6 +736,7 @@ namespace FlaxEditor.CustomEditors
public virtual void ClearLayout()
{
Children.Clear();
Editors.Clear();
}
/// <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>
/// 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>
+99
View File
@@ -388,6 +388,105 @@ int32 Editor::LoadProduct()
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
if (projectPath.IsEmpty())
{
+30 -1
View File
@@ -272,8 +272,10 @@ namespace FlaxEditor
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>();
_isHeadlessMode = isHeadless;
_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()
{
for (int i = 0; i < _modules.Count; i++)
+15 -3
View File
@@ -456,18 +456,30 @@ namespace FlaxEditor.GUI.ContextMenu
if (base.OnKeyDown(key))
return true;
// Se;ect the first item
if (key == KeyboardKeys.ArrowDown)
switch (key)
{
case KeyboardKeys.ArrowDown:
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();
_panel.ScrollViewTo(item);
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;
@@ -183,7 +183,7 @@ namespace FlaxEditor.GUI.ContextMenu
case KeyboardKeys.ArrowUp:
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();
ParentContextMenu.ItemsContainer.ScrollViewTo(item);
@@ -194,7 +194,7 @@ namespace FlaxEditor.GUI.ContextMenu
case KeyboardKeys.ArrowDown:
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();
ParentContextMenu.ItemsContainer.ScrollViewTo(item);
+16 -24
View File
@@ -74,23 +74,21 @@ namespace FlaxEditor.GUI.Timeline.GUI
}
// Setup time axis ticks
int minDistanceBetweenTicks = 4000;
int maxDistanceBetweenTicks = 6000;
var minDistanceBetweenTicks = 50.0f;
var maxDistanceBetweenTicks = 100.0f;
var zoom = Timeline.UnitsPerSecond * _timeline.Zoom;
var left = Vector2.Min(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 rightFrame = Mathf.Ceil((right - Timeline.StartOffset) / zoom) * _timeline.FramesPerSecond;
var min = leftFrame;
var max = rightFrame;
var range = max - min;
int smallestTick = 0;
int biggestTick = _tickSteps.Length - 1;
for (int i = _tickSteps.Length - 1; i >= 0; i--)
{
// 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
_tickStrengths[i] = Mathf.Saturate((tickSpacing - minDistanceBetweenTicks) / (maxDistanceBetweenTicks - minDistanceBetweenTicks));
@@ -117,14 +115,16 @@ namespace FlaxEditor.GUI.Timeline.GUI
// Draw all ticks
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
int startTick = Mathf.FloorToInt(min / _tickSteps[l]);
int endTick = Mathf.CeilToInt(max / _tickSteps[l]);
var lStep = _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);
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;
var tick = i * _tickSteps[l];
var tick = i * lStep;
var time = tick / _timeline.FramesPerSecond;
var x = time * zoom + Timeline.StartOffset;
@@ -163,15 +163,17 @@ namespace FlaxEditor.GUI.Timeline.GUI
// Draw all ticks
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
int startTick = Mathf.FloorToInt(min / _tickSteps[l]);
int endTick = Mathf.CeilToInt(max / _tickSteps[l]);
var lStep = _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 labelColor = style.ForegroundDisabled.AlphaMultiplied(strength);
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;
var tick = i * _tickSteps[l];
var tick = i * lStep;
var time = tick / _timeline.FramesPerSecond;
var x = time * zoom + Timeline.StartOffset;
@@ -195,17 +197,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
default: throw new ArgumentOutOfRangeException();
}
var labelRect = new Rectangle(x + 2, -verticalLinesHeaderExtend + timeAxisHeaderOffset, 50, verticalLinesHeaderExtend);
Render2D.DrawText(
style.FontSmall,
labelText,
labelRect,
labelColor,
TextAlignment.Near,
TextAlignment.Center,
TextWrapping.NoWrap,
1.0f,
0.8f
);
Render2D.DrawText(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 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);
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.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();
}
@@ -37,7 +37,8 @@ namespace FlaxEditor.GUI.Timeline.GUI
public override void Draw()
{
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 thickness = 2.0f;
+53 -43
View File
@@ -288,7 +288,7 @@ namespace FlaxEditor.GUI.Timeline
private TimeIntervalsHeader _timeIntervalsHeader;
private ContainerControl _backgroundScroll;
private Background _background;
private Panel _backgroundArea;
private BackgroundArea _backgroundArea;
private TimelineEdge _leftEdge;
private TimelineEdge _rightEdge;
private Button _addTrackButton;
@@ -305,6 +305,7 @@ namespace FlaxEditor.GUI.Timeline
private PositionHandle _positionHandle;
private bool _isRightMouseButtonDown;
private Vector2 _rightMouseButtonDownPos;
private Vector2 _rightMouseButtonMovePos;
private float _zoom = 1.0f;
private bool _isMovingPositionHandle;
private bool _canPlayPauseStop = true;
@@ -579,7 +580,7 @@ namespace FlaxEditor.GUI.Timeline
get => _zoom;
set
{
value = Mathf.Clamp(value, 0.0001f, 1000.0f);
value = Mathf.Clamp(value, 0.00001f, 1000.0f);
if (Mathf.NearEqual(_zoom, value))
return;
@@ -912,7 +913,7 @@ namespace FlaxEditor.GUI.Timeline
Offsets = new Margin(0, 0, 0, HeaderTopAreaHeight),
Parent = _splitter.Panel2
};
_backgroundArea = new Panel(ScrollBars.Both)
_backgroundArea = new BackgroundArea(this)
{
AutoFocus = false,
ClipChildren = false,
@@ -958,12 +959,12 @@ namespace FlaxEditor.GUI.Timeline
private void UpdatePositionHandle()
{
var handleWidth = 12.0f;
_positionHandle.Bounds = new Rectangle(
StartOffset * 2.0f - handleWidth * 0.5f + _currentFrame / _framesPerSecond * UnitsPerSecond * Zoom,
HeaderTopAreaHeight * -0.5f,
handleWidth,
HeaderTopAreaHeight * 0.5f
);
var bounds = new Rectangle();
bounds.Location.X = StartOffset * 2.0f - handleWidth * 0.5f + _currentFrame / _framesPerSecond * UnitsPerSecond * Zoom;
bounds.Location.Y = 0;
bounds.Size.X = handleWidth;
bounds.Size.Y = HeaderTopAreaHeight * 0.5f;
_positionHandle.Bounds = bounds;
}
private void OnFpsPopupShowing(ComboBox comboBox)
@@ -1039,6 +1040,13 @@ namespace FlaxEditor.GUI.Timeline
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);
menu.Show(_viewButton.Parent, _viewButton.BottomLeft);
@@ -1935,8 +1943,8 @@ namespace FlaxEditor.GUI.Timeline
_background.Bounds = new Rectangle(StartOffset, 0, Duration * UnitsPerSecond * Zoom, height);
var edgeWidth = 6.0f;
_leftEdge.Bounds = new Rectangle(_background.Left - edgeWidth * 0.5f + StartOffset, HeaderTopAreaHeight * -0.5f, edgeWidth, height + HeaderTopAreaHeight * 0.5f);
_rightEdge.Bounds = new Rectangle(_background.Right - 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, 0, edgeWidth, 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 />
protected override void PerformLayoutBeforeChildren()
{
@@ -1986,8 +2026,8 @@ namespace FlaxEditor.GUI.Timeline
{
_isRightMouseButtonDown = true;
_rightMouseButtonDownPos = location;
_rightMouseButtonMovePos = location;
Focus();
return true;
}
@@ -2006,38 +2046,8 @@ namespace FlaxEditor.GUI.Timeline
if (button == MouseButton.Right && _isRightMouseButtonDown)
{
_isRightMouseButtonDown = false;
if (Vector2.Distance(ref location, ref _rightMouseButtonDownPos) < 4.0f)
{
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);
}
ShowContextMenu(location);
}
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))
{
menu.AddButton("Select " + actorNode.Actor, OnClickedSelectActor);
menu.AddButton("Select " + actorNode.Actor, OnClickedSelectActor).TooltipText = Utilities.Utils.GetTooltip(actorNode.Actor);
}
else
{
@@ -7,6 +7,7 @@ using System.Reflection;
using System.Text;
using FlaxEditor.CustomEditors;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Scripting;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -197,8 +198,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks
AddMemberTag tag;
tag.Member = m;
tag.Archetype = EventTrack.GetArchetype();
menu.AddButton(sb.ToString(), OnAddMemberTrack).Tag = tag;
var tooltip = Surface.SurfaceUtils.GetVisualScriptMemberInfoDescription(new ScriptMemberInfo(m));
menu.AddButton(sb.ToString(), OnAddMemberTrack).LinkTooltip(tooltip).Tag = tag;
count++;
}
@@ -295,7 +296,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks
AddMemberTag tag;
tag.Member = m;
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++;
}
+7
View File
@@ -80,6 +80,13 @@ namespace FlaxEditor.Gizmo
{
}
/// <summary>
/// Performs scene objects snapping to the ground.
/// </summary>
public virtual void SnapToGround()
{
}
/// <summary>
/// Draws the gizmo.
/// </summary>
+5
View File
@@ -89,5 +89,10 @@ namespace FlaxEditor.Gizmo
/// Gets a <see cref="FlaxEditor.Undo"/> object used by the gizmo owner.
/// </summary>
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 />
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.Collections.Generic;
using FlaxEditor.SceneGraph;
using FlaxEngine;
namespace FlaxEditor.Gizmo
@@ -398,15 +399,7 @@ namespace FlaxEditor.Gizmo
// Snap to ground
if (_activeAxis == Axis.None && SelectionCount != 0 && Owner.SnapToGround)
{
if (Physics.RayCast(Position, Vector3.Down, out var hit, float.MaxValue, uint.MaxValue, false))
{
StartTransforming();
var translationDelta = hit.Point - Position;
var rotationDelta = Quaternion.Identity;
var scaleDelta = Vector3.Zero;
OnApplyTransformation(ref translationDelta, ref rotationDelta, ref scaleDelta);
EndTransforming();
}
SnapToGround();
}
// Only when is active
else if (_isActive)
@@ -517,6 +510,39 @@ namespace FlaxEditor.Gizmo
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>
/// Gets a value indicating whether this tool can transform objects.
/// </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>
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>
/// Called when user starts transforming selected objects.
/// </summary>
+4 -2
View File
@@ -175,7 +175,7 @@ ManagedEditor::~ManagedEditor()
void ManagedEditor::Init()
{
// 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();
if (mclass == nullptr)
{
@@ -194,14 +194,16 @@ void ManagedEditor::Init()
MonoObject* exception = nullptr;
bool isHeadless = CommandLine::Options.Headless.IsTrue();
bool skipCompile = CommandLine::Options.SkipCompile.IsTrue();
bool newProject = CommandLine::Options.NewProject.IsTrue();
args[0] = &isHeadless;
args[1] = &skipCompile;
args[2] = &newProject;
Guid sceneId;
if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId)))
{
sceneId = Guid::Empty;
}
args[2] = &sceneId;
args[3] = &sceneId;
initMethod->Invoke(instance, args, &exception);
if (exception)
{
@@ -383,6 +383,7 @@ namespace FlaxEditor.Modules
var objects = Selection.Where(x => x.CanDelete).ToList().BuildAllNodes().Where(x => x.CanDelete).ToList();
if (objects.Count == 0)
return;
var isSceneTreeFocus = Editor.Windows.SceneWin.ContainsFocus;
SelectionDeleteBegin?.Invoke();
@@ -404,6 +405,9 @@ namespace FlaxEditor.Modules
SelectionDeleteEnd?.Invoke();
OnDirty(objects);
if (isSceneTreeFocus)
Editor.Windows.SceneWin.Focus();
}
/// <summary>
+23
View File
@@ -251,6 +251,15 @@ namespace FlaxEditor.Modules
if (!Editor.StateMachine.CurrentState.CanChangeScene)
return;
// In play-mode Editor mocks the level streaming script
if (Editor.IsPlayMode)
{
if (!additive)
Level.UnloadAllScenesAsync();
Level.LoadSceneAsync(sceneId);
return;
}
if (!additive)
{
// Ensure to save all pending changes
@@ -272,6 +281,13 @@ namespace FlaxEditor.Modules
if (!Editor.StateMachine.CurrentState.CanChangeScene)
return;
// In play-mode Editor mocks the level streaming script
if (Editor.IsPlayMode)
{
Level.UnloadSceneAsync(scene);
return;
}
// Ensure to save all pending changes
if (CheckSaveBeforeClose())
return;
@@ -289,6 +305,13 @@ namespace FlaxEditor.Modules
if (!Editor.StateMachine.CurrentState.CanChangeScene)
return;
// In play-mode Editor mocks the level streaming script
if (Editor.IsPlayMode)
{
Level.UnloadAllScenesAsync();
return;
}
// Ensure to save all pending changes
if (CheckSaveBeforeClose())
return;
+34 -7
View File
@@ -1,5 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.IO;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.SceneGraph.GUI;
using FlaxEngine;
@@ -17,9 +19,6 @@ namespace FlaxEditor.SceneGraph.Actors
/// <summary>
/// Gets or sets a value indicating whether this scene is edited.
/// </summary>
/// <value>
/// <c>true</c> if this scene is edited; otherwise, <c>false</c>.
/// </value>
public bool IsEdited
{
get => _isEdited;
@@ -28,7 +27,6 @@ namespace FlaxEditor.SceneGraph.Actors
if (_isEdited != value)
{
_isEdited = value;
_treeNode.UpdateText();
}
}
@@ -37,9 +35,6 @@ namespace FlaxEditor.SceneGraph.Actors
/// <summary>
/// Gets the scene.
/// </summary>
/// <value>
/// The scene.
/// </value>
public Scene Scene => _actor as Scene;
/// <summary>
@@ -68,5 +63,37 @@ namespace FlaxEditor.SceneGraph.Actors
/// <inheritdoc />
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);
}
}
}
+14 -6
View File
@@ -28,6 +28,9 @@ namespace FlaxEditor.States
/// <inheritdoc />
public override bool CanEditScene => true;
/// <inheritdoc />
public override bool CanChangeScene => true;
/// <inheritdoc />
public override bool CanUseUndoRedo => false;
@@ -89,9 +92,13 @@ namespace FlaxEditor.States
{
Profiler.BeginEvent("PlayingState.CacheSelection");
_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();
}
@@ -99,15 +106,16 @@ namespace FlaxEditor.States
private void RestoreSelection()
{
Profiler.BeginEvent("PlayingState.RestoreSelection");
var count = Editor.SceneEditing.Selection.Count;
Editor.SceneEditing.Selection.Clear();
var selection = Editor.SceneEditing.Selection;
var count = selection.Count;
selection.Clear();
for (int i = 0; i < _selectedObjects.Count; i++)
{
var node = SceneGraphFactory.FindNode(_selectedObjects[i]);
if (node != null)
Editor.SceneEditing.Selection.Add(node);
selection.Add(node);
}
if (Editor.SceneEditing.Selection.Count != count)
if (selection.Count != count)
Editor.SceneEditing.OnSelectionChanged();
Profiler.EndEvent();
}
+1 -4
View File
@@ -122,10 +122,7 @@ namespace FlaxEditor.Surface.Archetypes
{
GetBox(0).CurrentType = type;
Title = (_isUnpacking ? "Unpack " : "Pack ") + type.Name;
var attributes = type.GetAttributes(false);
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
if (tooltipAttribute != null)
TooltipText += "\n" + tooltipAttribute.Text;
TooltipText = SurfaceUtils.GetVisualScriptTypeDescription(type);
}
else
{
+17
View File
@@ -92,6 +92,7 @@ namespace FlaxEditor.Surface
case MaterialParameterType.GPUTexture: return typeof(GPUTexture);
case MaterialParameterType.Matrix: return typeof(Matrix);
case MaterialParameterType.ChannelMask: return typeof(ChannelMask);
case MaterialParameterType.TextureGroupSampler: return typeof(int);
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
@@ -427,6 +428,22 @@ namespace FlaxEditor.Surface
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)
{
var name = member.Name;
+13 -6
View File
@@ -604,7 +604,7 @@ namespace FlaxEditor.Surface
Parent = this
};
Presenter = new CustomEditorPresenter(undo);
Presenter = new CustomEditorPresenter(undo, "Loading...");
Presenter.Panel.Parent = scrollPanel;
}
@@ -744,7 +744,7 @@ namespace FlaxEditor.Surface
}
else
{
_propertiesEditor = new CustomEditorPresenter(_undo);
_propertiesEditor = new CustomEditorPresenter(_undo, "Loading...");
_propertiesEditor.Panel.Parent = _split2.Panel2;
}
_propertiesEditor.Modified += OnPropertyEdited;
@@ -896,6 +896,16 @@ namespace FlaxEditor.Surface
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>
/// Loads the surface from the asset. Called during <see cref="Update"/> when asset is loaded and surface is missing.
/// </summary>
@@ -939,10 +949,7 @@ namespace FlaxEditor.Surface
return;
}
// Setup
_undo.Clear();
_surface.Enabled = true;
_propertiesEditor.BuildLayout();
OnSurfaceEditingStart();
ClearEditedFlag();
if (_showWholeGraphOnLoad)
{
@@ -2,6 +2,7 @@
using System;
using FlaxEditor.Gizmo;
using FlaxEditor.SceneGraph;
using FlaxEditor.Tools.Foliage.Undo;
using FlaxEngine;
@@ -87,6 +88,12 @@ namespace FlaxEditor.Tools.Foliage
navigationDirty = false;
}
/// <inheritdoc />
protected override bool IsSelected(SceneGraphNode obj)
{
return false;
}
/// <inheritdoc />
protected override void OnStartTransforming()
{
@@ -231,6 +238,21 @@ namespace FlaxEditor.Tools.Foliage
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 />
public override void OnActivated()
{
@@ -141,15 +141,20 @@ namespace FlaxEditor.Actions
for (int i = 0; i < nodeParents.Count; i++)
{
var node = nodeParents[i];
var parent = node.Actor?.Parent;
var actor = node.Actor;
var parent = actor?.Parent;
if (parent != null)
{
// Fix name collisions
string name = node.Name;
Actor[] children = parent.Children;
if (children.Any(x => x.Name == name))
var name = actor.Name;
for (int j = 0; j < parent.ChildrenCount; j++)
{
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]);
}
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>
/// The colors for the keyframes used by the curve editor.
/// </summary>
@@ -21,10 +21,12 @@ namespace FlaxEditor.Viewport
/// </summary>
/// <param name="task">The task.</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)
{
Undo = undo;
SceneGraphRoot = sceneGraphRoot;
SetUpdate(ref _update, OnUpdate);
}
@@ -73,6 +75,9 @@ namespace FlaxEditor.Viewport
/// <inheritdoc />
public Undo Undo { get; }
/// <inheritdoc />
public SceneGraph.RootNode SceneGraphRoot { get; }
/// <inheritdoc />
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
@@ -184,7 +184,7 @@ namespace FlaxEditor.Viewport
/// </summary>
/// <param name="editor">Editor instance.</param>
public MainEditorGizmoViewport(Editor editor)
: base(Object.New<SceneRenderTask>(), editor.Undo)
: base(Object.New<SceneRenderTask>(), editor.Undo, editor.Scene.Root)
{
_editor = editor;
_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem);
@@ -336,6 +336,9 @@ namespace FlaxEditor.Viewport
/// <inheritdoc />
public Undo Undo { get; }
/// <inheritdoc />
public RootNode SceneGraphRoot => _window.Graph.Root;
/// <inheritdoc />
public EditorViewport Viewport => this;
@@ -186,7 +186,6 @@ namespace FlaxEditor.Windows.Assets
// Asset properties proxy
_properties = new PropertiesProxy();
_propertiesEditor.Select(_properties);
// Preview properties editor
_previewTab = new Tab("Preview");
@@ -322,22 +321,17 @@ namespace FlaxEditor.Windows.Assets
{
if (value == null)
{
// Error
Editor.LogError("Failed to save animation graph surface");
return;
}
// Save data to the temporary asset
if (_asset.SaveSurface(value))
{
// Error
_surface.MarkAsEdited();
Editor.LogError("Failed to save animation graph surface data");
return;
}
_asset.Reload();
// Reset any root motion
_asset.WaitForLoaded();
_preview.PreviewActor.ResetLocalTransform();
_previewTab.Presenter.BuildLayoutOnUpdate();
}
@@ -376,6 +370,14 @@ namespace FlaxEditor.Windows.Assets
return false;
}
/// <inheritdoc />
protected override void OnSurfaceEditingStart()
{
_propertiesEditor.Select(_properties);
base.OnSurfaceEditingStart();
}
/// <inheritdoc />
protected override void PerformLayoutBeforeChildren()
{
@@ -229,7 +229,7 @@ namespace FlaxEditor.Windows.Assets
layout.Label("No parameters");
return;
}
if (!materialInstance.IsLoaded || materialInstance.BaseMaterial && !materialInstance.BaseMaterial.IsLoaded)
if (!materialInstance.IsLoaded || (materialInstance.BaseMaterial && !materialInstance.BaseMaterial.IsLoaded))
{
layout.Label("Loading...");
return;
@@ -352,10 +352,9 @@ namespace FlaxEditor.Windows.Assets
};
// Material properties editor
_editor = new CustomEditorPresenter(_undo);
_editor = new CustomEditorPresenter(_undo, "Loading...");
_editor.Panel.Parent = _split.Panel2;
_properties = new PropertiesProxy();
_editor.Select(_properties);
_editor.Modified += OnMaterialPropertyEdited;
// Setup input actions
@@ -479,16 +478,11 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
public override void Update(float deltaTime)
{
// Check if need to load
if (_isWaitingForLoad && _asset.IsLoaded && (_asset.BaseMaterial == null || _asset.BaseMaterial.IsLoaded))
{
// Clear flag
_isWaitingForLoad = false;
// Init material properties and parameters proxy
_properties.OnLoad(this);
// Setup
_editor.Select(_properties);
ClearEditedFlag();
_undo.Clear();
_editor.BuildLayout();
@@ -222,7 +222,6 @@ namespace FlaxEditor.Windows.Assets
// Asset properties proxy
_properties = new PropertiesProxy();
_propertiesEditor.Select(_properties);
// Surface
_surface = new MaterialSurface(this, Save, _undo)
@@ -324,17 +323,14 @@ namespace FlaxEditor.Windows.Assets
get => _asset.LoadSurface(true);
set
{
// Create material info
FillMaterialInfo(out var info);
// Save data to the temporary material
if (_asset.SaveSurface(value, info))
{
// Error
_surface.MarkAsEdited();
Editor.LogError("Failed to save material surface data");
}
_asset.Reload();
_asset.WaitForLoaded();
}
}
@@ -347,7 +343,6 @@ namespace FlaxEditor.Windows.Assets
// Load surface graph
if (_surface.Load())
{
// Error
Editor.LogError("Failed to load material surface.");
return true;
}
@@ -362,6 +357,14 @@ namespace FlaxEditor.Windows.Assets
return false;
}
/// <inheritdoc />
protected override void OnSurfaceEditingStart()
{
_propertiesEditor.Select(_properties);
base.OnSurfaceEditingStart();
}
/// <inheritdoc />
protected override bool CanEditSurfaceOnAssetLoadError => true;
@@ -120,7 +120,6 @@ namespace FlaxEditor.Windows.Assets
// Asset properties proxy
_properties = new PropertiesProxy();
_propertiesEditor.Select(_properties);
// Preview properties editor
_previewTab = new Tab("Preview");
@@ -217,14 +216,13 @@ namespace FlaxEditor.Windows.Assets
get => _asset.LoadSurface(true);
set
{
// Save data to the temporary asset
if (_asset.SaveSurface(value))
{
// Error
_surface.MarkAsEdited();
Editor.LogError("Failed to save Particle Emitter surface data");
}
_asset.Reload();
_asset.WaitForLoaded();
_preview.PreviewActor.ResetSimulation();
_previewTab.Presenter.BuildLayoutOnUpdate();
}
@@ -255,6 +253,14 @@ namespace FlaxEditor.Windows.Assets
return false;
}
/// <inheritdoc />
protected override void OnSurfaceEditingStart()
{
_propertiesEditor.Select(_properties);
base.OnSurfaceEditingStart();
}
/// <inheritdoc />
protected override bool SaveToOriginal()
{
@@ -29,6 +29,7 @@ namespace FlaxEditor.Windows
: base(editor, true, ScrollBars.Vertical)
{
Title = "Properties";
AutoFocus = true;
Presenter = new CustomEditorPresenter(editor.Undo);
Presenter.Panel.Parent = this;
@@ -104,12 +104,11 @@ namespace FlaxEditor.Windows
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
bool hasPrefabLink = canEditScene && isSingleActorSelected && (Editor.SceneEditing.Selection[0] as ActorNode).HasPrefabLink;
b = contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab);
b.Enabled = hasPrefabLink;
b = contextMenu.AddButton("Break Prefab Link", Editor.Prefabs.BreakLinks);
b.Enabled = hasPrefabLink;
if (hasPrefabLink)
{
contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab);
contextMenu.AddButton("Break Prefab Link", Editor.Prefabs.BreakLinks);
}
// Spawning actors options
+1 -8
View File
@@ -204,14 +204,7 @@ namespace FlaxEditor.Windows
continue;
var item = _groupSearch.AddChild(CreateActorItem(CustomEditors.CustomEditorsUtil.GetPropertyNameUI(text), actorType));
item.TooltipText = actorType.TypeName;
var attributes = actorType.GetAttributes(false);
var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute);
if (tooltipAttribute != null)
{
item.TooltipText += '\n';
item.TooltipText += tooltipAttribute.Text;
}
item.TooltipText = Surface.SurfaceUtils.GetVisualScriptTypeDescription(actorType);
var highlights = new List<Rectangle>(ranges.Length);
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>();
_objectsMapping[trackData->ID] = to;
break;
return;
}
case SceneAnimation::Track::Types::Script:
{
const auto trackData = track.GetData<SceneAnimation::ScriptTrack::Data>();
_objectsMapping[trackData->ID] = to;
break;
return;
}
case SceneAnimation::Track::Types::CameraCut:
{
const auto trackData = track.GetData<SceneAnimation::CameraCutTrack::Data>();
_objectsMapping[trackData->ID] = to;
break;
return;
}
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)
@@ -744,7 +745,7 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
const auto trackData = track.GetData<SceneAnimation::ActorTrack::Data>();
Guid id = trackData->ID;
_objectsMapping.TryGet(id, id);
state.Object = Scripting::FindObject<Actor>(trackData->ID);
state.Object = Scripting::FindObject<Actor>(id);
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"));
@@ -1222,6 +1222,11 @@ Asset::LoadResult VisualScript::load()
method.ProfilerName.Get()[assetName.Length()] = ':';
method.ProfilerName.Get()[assetName.Length() + 1] = ':';
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
@@ -2139,7 +2144,7 @@ VisualScriptingBinaryModule* VisualScripting::GetBinaryModule()
Variant VisualScripting::Invoke(VisualScript::Method* method, ScriptingObject* instance, Span<Variant> parameters)
{
CHECK_RETURN(method && method->Script->IsLoaded(), Variant::Zero);
PROFILE_CPU_NAMED(*method->ProfilerName);
PROFILE_CPU_SRC_LOC(method->ProfilerData);
// Add to the calling stack
ScopeContext scope;
@@ -5,6 +5,9 @@
#include "../BinaryAsset.h"
#include "Engine/Scripting/BinaryModule.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_DEBUGGING USE_EDITOR
@@ -118,6 +121,7 @@ public:
Array<StringAnsi, InlinedAllocation<16>> ParamNames;
#if COMPILE_WITH_PROFILER
StringAnsi ProfilerName;
SourceLocationData ProfilerData;
#endif
};
+1 -1
View File
@@ -296,7 +296,7 @@ namespace FlaxEditor.Content.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))
return true;
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("-clearcooker ", ClearCookerCache);
PARSE_ARG_SWITCH("-project ", Project);
PARSE_BOOL_SWITCH("-new ", NewProject);
PARSE_BOOL_SWITCH("-genprojectfiles ", GenProjectFiles);
PARSE_ARG_SWITCH("-build ", Build);
PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile);
+5
View File
@@ -134,6 +134,11 @@ public:
/// </summary>
String Project;
/// <summary>
/// -new (generates the project files inside the specified project folder or uses current workspace folder)
/// </summary>
Nullable<bool> NewProject;
/// <summary>
/// -genprojectfiles (generates the scripts project files)
/// </summary>
+1 -2
View File
@@ -156,8 +156,7 @@ public:
/// <summary>
/// Checks whenever the game viewport is focused by the user (eg. can receive input).
/// </summary>
/// <returns>True if game viewport is focused, otherwise false.</returns>
static bool HasGameViewportFocus();
API_PROPERTY() static bool HasGameViewportFocus();
private:
+22
View File
@@ -73,6 +73,7 @@ public:
}
void Update() override;
void Dispose() override;
};
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
auto obj = SceneObjectsFactory::Spawn(document, modifier);
sceneObjects->At(i) = obj;
if (obj == nullptr)
{
LOG(Warning, "Cannot create object.");
return true;
continue;
}
obj->RegisterObject();
// Add to results
sceneObjects->At(i) = obj;
Actor* actor = dynamic_cast<Actor*>(obj);
if (actor)
{
output.Add(actor);
}
}
// TODO: optimize this to call json parsing only once per-object instead of twice (spawn + load)
stream.SetPosition(startPos);
for (int32 i = 0; i < objectsCount; i++)
{
@@ -1639,7 +1640,7 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
int32 orderInParent;
stream.ReadInt32(&orderInParent);
// Load JSON
// Load JSON
rapidjson_flax::Document document;
{
PROFILE_CPU_NAMED("Json.Parse");
@@ -1653,7 +1654,10 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
// Deserialize object
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);
@@ -25,13 +25,13 @@ public:
/// <summary>
/// The fog density factor.
/// </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;
/// <summary>
/// The fog height density factor that controls how the density increases as height decreases. The smaller values produce more visible transition larger.
/// </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;
/// <summary>
+22 -1
View File
@@ -47,6 +47,11 @@ int32 BoxTrianglesIndicesCache[] =
};
#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
{
@@ -108,6 +113,9 @@ struct NavigationSceneRasterization
auto v0 = vb[ib[i0++]];
auto v1 = 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);
n.Normalize();
@@ -124,6 +132,9 @@ struct NavigationSceneRasterization
auto v0 = Vector3::Transform(vb[ib[i0++]], worldToNavMesh);
auto v1 = 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);
n.Normalize();
@@ -223,6 +234,8 @@ struct NavigationSceneRasterization
// Extract data from the actor
if (const auto* boxCollider = dynamic_cast<BoxCollider*>(actor))
{
if (boxCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("BoxCollider");
const OrientedBoundingBox box = boxCollider->GetOrientedBox();
@@ -232,6 +245,8 @@ struct NavigationSceneRasterization
}
else if (const auto* sphereCollider = dynamic_cast<SphereCollider*>(actor))
{
if (sphereCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("SphereCollider");
const BoundingSphere sphere = sphereCollider->GetSphere();
@@ -241,6 +256,8 @@ struct NavigationSceneRasterization
}
else if (const auto* capsuleCollider = dynamic_cast<CapsuleCollider*>(actor))
{
if (capsuleCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("CapsuleCollider");
const BoundingBox box = capsuleCollider->GetBox();
@@ -250,6 +267,8 @@ struct NavigationSceneRasterization
}
else if (const auto* meshCollider = dynamic_cast<MeshCollider*>(actor))
{
if (meshCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("MeshCollider");
auto collisionData = meshCollider->CollisionData.Get();
@@ -260,13 +279,15 @@ struct NavigationSceneRasterization
Matrix meshColliderToWorld;
meshCollider->GetLocalToWorldMatrix(meshColliderToWorld);
for(auto& v : vb)
for (auto& v : vb)
Vector3::Transform(v, meshColliderToWorld, v);
e.RasterizeTriangles();
}
else if (const auto* splineCollider = dynamic_cast<SplineCollider*>(actor))
{
if (splineCollider->GetIsTrigger())
return true;
PROFILE_CPU_NAMED("SplineCollider");
auto collisionData = splineCollider->CollisionData.Get();
@@ -153,7 +153,7 @@ void ENetDriver::Disconnect(const NetworkConnection& connection)
void* peer = nullptr;
if(_peerMap.TryGet(connectionId, peer))
{
enet_peer_disconnect_now((ENetPeer*)_peer, 0);
enet_peer_disconnect_now((ENetPeer*)peer, 0);
_peerMap.Remove(connectionId);
}
else
+1 -2
View File
@@ -276,8 +276,7 @@ void RigidBody::UpdateMass()
float raiseMassToPower = 0.75f;
// TODO: link physical material or expose density parameter
PxVec3 centerOfMassOffset = C2P(_centerOfMassOffset);
PxRigidBodyExt::updateMassAndInertia(*_actor, densityKGPerCubicUU, &centerOfMassOffset);
PxRigidBodyExt::updateMassAndInertia(*_actor, densityKGPerCubicUU);
// Grab old mass so we can apply new mass while maintaining inertia tensor
const float oldMass = _actor->getMass();
+63 -12
View File
@@ -291,7 +291,8 @@ void WheeledVehicle::Setup()
offsets[i] = C2P(wheel.Collider->GetLocalPosition());
}
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());
for (int32 i = 0; i < wheels.Count(); i++)
{
@@ -303,15 +304,17 @@ void WheeledVehicle::Setup()
PxVehicleSuspensionData suspensionData;
const float suspensionFrequency = 7.0f;
const float suspensionDampingRatio = 1.0f;
suspensionData.mMaxCompression = 10.0f;
suspensionData.mMaxDroop = 10.0f;
suspensionData.mMaxCompression = wheel.SuspensionMaxRaise;
suspensionData.mMaxDroop = wheel.SuspensionMaxDrop;
suspensionData.mSprungMass = sprungMasses[i];
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;
tire.mType = 0;
tire.mLatStiffX = wheel.TireLateralMax;
tire.mLatStiffY = wheel.TireLateralStiffness;
tire.mLongitudinalStiffnessPerUnitGravity = wheel.TireLongitudinalStiffness;
PxVehicleWheelData wheelData;
wheelData.mMass = wheel.Mass;
@@ -324,16 +327,17 @@ void WheeledVehicle::Setup()
wheelData.mMaxHandBrakeTorque = M2ToCm2(wheel.MaxHandBrakeTorque);
PxVec3 centreOffset = centerOfMassOffset.transformInv(offsets[i]);
float suspensionForceOffset = 0.0f;
PxVec3 forceAppPointOffset(centreOffset.x, centreOffset.y, centreOffset.z + suspensionForceOffset);
PxVec3 forceAppPointOffset(centreOffset.x, wheel.SuspensionForceOffset, centreOffset.z);
wheelsSimData->setTireData(i, tire);
wheelsSimData->setWheelData(i, wheelData);
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->setSuspForceAppPointOffset(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();
if (wheel.Collider->IsActiveInHierarchy())
@@ -346,6 +350,9 @@ void WheeledVehicle::Setup()
wheelShape->setQueryFilterData(filter);
wheelShape->setSimulationFilterData(filter);
wheelsSimData->setSceneQueryFilterData(i, filter);
// Remove wheels from the simulation (suspension force hold the vehicle)
wheelShape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, false);
}
else
{
@@ -508,11 +515,29 @@ void WheeledVehicle::Setup()
void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
{
// 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())
{
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()
{
// 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())
{
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(Wheels, _wheels);
SERIALIZE(UseReverseAsBrake);
SERIALIZE(UseAnalogSteering);
SERIALIZE_MEMBER(Engine, _engine);
SERIALIZE_MEMBER(Differential, _differential);
SERIALIZE_MEMBER(Gearbox, _gearbox);
@@ -557,6 +607,7 @@ void WheeledVehicle::Deserialize(DeserializeStream& stream, ISerializeModifier*
DESERIALIZE_MEMBER(DriveType, _driveType);
DESERIALIZE_MEMBER(Wheels, _wheels);
DESERIALIZE(UseReverseAsBrake);
DESERIALIZE(UseAnalogSteering);
DESERIALIZE_MEMBER(Engine, _engine);
DESERIALIZE_MEMBER(Differential, _differential);
DESERIALIZE_MEMBER(Gearbox, _gearbox);
+77 -9
View File
@@ -172,47 +172,82 @@ public:
/// <summary>
/// Wheel placement type.
/// </summary>
API_FIELD() WheelTypes Type = WheelTypes::FrontLeft;
API_FIELD(Attributes="EditorOrder(0)") WheelTypes Type = WheelTypes::FrontLeft;
/// <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.
/// </summary>
API_FIELD() float Mass = 20.0f;
API_FIELD(Attributes="EditorOrder(1)") float Mass = 20.0f;
/// <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.
/// </summary>
API_FIELD() float Radius = 50.0f;
API_FIELD(Attributes="EditorOrder(2)") float Radius = 50.0f;
/// <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.
/// </summary>
API_FIELD() float Width = 20.0f;
API_FIELD(Attributes="EditorOrder(3)") float Width = 20.0f;
/// <summary>
/// Max steer angle that can be achieved by the wheel (in degrees).
/// </summary>
API_FIELD(Attributes="Limit(0)") float MaxSteerAngle = 0.0f;
API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(10)") float MaxSteerAngle = 0.0f;
/// <summary>
/// Damping rate applied to wheel. Specified in kilograms metres-squared per second (kg m^2 s^-1).
/// </summary>
API_FIELD(Attributes="Limit(0)") float DampingRate = 0.25f;
API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(11)") float DampingRate = 0.25f;
/// <summary>
/// Max brake torque that can be applied to wheel. Specified in kilograms metres-squared per second-squared (kg m^2 s^-2)
/// </summary>
API_FIELD(Attributes="Limit(0)") float MaxBrakeTorque = 1500.0f;
API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(12)") float MaxBrakeTorque = 1500.0f;
/// <summary>
/// Max handbrake torque that can be applied to wheel. Specified in kilograms metres-squared per second-squared (kg m^2 s^-2)
/// </summary>
API_FIELD(Attributes="Limit(0)") float MaxHandBrakeTorque = 2000.0f;
API_FIELD(Attributes="Limit(0), EditorDisplay(\"Steering\"), EditorOrder(13)") float MaxHandBrakeTorque = 2000.0f;
/// <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).
/// </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>
@@ -246,6 +281,33 @@ public:
/// The friction experienced by the tire for the combination of tire type and surface type after accounting.
/// </summary>
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:
@@ -274,6 +336,12 @@ public:
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Vehicle\")")
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>
/// Gets the vehicle driving model type.
/// </summary>
+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.
#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
{
public:
@@ -661,6 +667,8 @@ void Physics::CollectResults()
int32 wheelsCount = 0;
for (auto wheelVehicle : WheelVehicles)
{
if (!wheelVehicle->IsActiveInHierarchy())
continue;
auto drive = (PxVehicleWheels*)wheelVehicle->_drive;
ASSERT(drive);
WheelVehiclesCache.Add(drive);
@@ -742,6 +750,23 @@ void Physics::CollectResults()
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
// TODO: expose steer vs forward curve into per-vehicle (up to 8 points, values clamped into 0/1 range)
static constexpr PxF32 steerVsForwardSpeedData[] =
@@ -757,28 +782,60 @@ void Physics::CollectResults()
};
const PxFixedSizeLookupTable<8> steerVsForwardSpeed(steerVsForwardSpeedData, 4);
// @formatter:on
switch (wheelVehicle->_driveTypeCurrent)
if (wheelVehicle->UseAnalogSteering)
{
case WheeledVehicle::DriveTypes::Drive4W:
{
PxVehicleDrive4WRawInputData rawInputData;
rawInputData.setAnalogAccel(throttle);
rawInputData.setAnalogBrake(brake);
rawInputData.setAnalogSteer(wheelVehicle->_steering);
rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
PxVehicleDrive4WSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDrive4W*)drive);
break;
switch (wheelVehicle->_driveTypeCurrent)
{
case WheeledVehicle::DriveTypes::Drive4W:
{
PxVehicleDrive4WRawInputData rawInputData;
rawInputData.setAnalogAccel(throttle);
rawInputData.setAnalogBrake(brake);
rawInputData.setAnalogSteer(wheelVehicle->_steering);
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;
rawInputData.setAnalogAccel(throttle);
rawInputData.setAnalogBrake(brake);
rawInputData.setAnalogSteer(wheelVehicle->_steering);
rawInputData.setAnalogHandbrake(wheelVehicle->_handBrake);
PxVehicleDriveNWSmoothAnalogRawInputsAndSetAnalogInputs(padSmoothing, steerVsForwardSpeed, rawInputData, LastDeltaTime, false, *(PxVehicleDriveNW*)drive);
break;
}
const float deadZone = 0.1f;
switch (wheelVehicle->_driveTypeCurrent)
{
case WheeledVehicle::DriveTypes::Drive4W:
{
PxVehicleDrive4WRawInputData rawInputData;
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)
WheelRaycastBatchQuery->release();
WheelQueryResults.Resize(wheelsCount);
WheelQueryResults.Resize(WheelQueryResults.Capacity());
WheelQueryResults.Resize(wheelsCount, false);
WheelHitResults.Resize(wheelsCount, false);
PxBatchQueryDesc desc(wheelsCount, 0, 0);
desc.queryMemory.userRaycastResultBuffer = WheelQueryResults.Get();
desc.queryMemory.userRaycastTouchBuffer = WheelHitResults.Get();
@@ -810,13 +867,17 @@ void Physics::CollectResults()
}
// Setup cache for wheel states
WheelVehiclesResultsPerVehicle.Resize(WheelVehicles.Count(), false);
WheelVehiclesResultsPerVehicle.Resize(WheelVehiclesCache.Count(), false);
WheelVehiclesResultsPerWheel.Resize(wheelsCount, false);
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& perVehicle = WheelVehiclesResultsPerVehicle[i];
auto wheelVehicle = WheelVehicles[i];
if (!wheelVehicle->IsActiveInHierarchy())
continue;
auto drive = (PxVehicleWheels*)WheelVehicles[ii]->_drive;
auto& perVehicle = WheelVehiclesResultsPerVehicle[ii];
ii++;
perVehicle.nbWheelQueryResults = drive->mWheelsSimData.getNbWheels();
perVehicle.wheelQueryResults = WheelVehiclesResultsPerWheel.Get() + wheelsCount;
wheelsCount += perVehicle.nbWheelQueryResults;
@@ -827,17 +888,26 @@ void Physics::CollectResults()
PxVehicleUpdates(LastDeltaTime, PhysicsScene->getGravity(), *WheelTireFrictions, WheelVehiclesCache.Count(), WheelVehiclesCache.Get(), WheelVehiclesResultsPerVehicle.Get());
// 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 drive = WheelVehiclesCache[i];
auto& perVehicle = WheelVehiclesResultsPerVehicle[i];
if (!wheelVehicle->IsActiveInHierarchy())
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
for (int32 j = 0; j < wheelVehicle->_wheelsData.Count(); j++)
{
auto& wheelData = wheelVehicle->_wheelsData[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;
state.IsInAir = perWheel.isInAir;
@@ -845,10 +915,24 @@ void Physics::CollectResults()
state.TireContactPoint = P2C(perWheel.tireContactPoint);
state.TireContactNormal = P2C(perWheel.tireContactNormal);
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);
const float wheelSteerAngle = RadiansToDegrees * perWheel.steerAngle;
wheelData.Collider->SetLocalOrientation(Quaternion::Euler(0, wheelSteerAngle, wheelRotationAngle) * wheelData.LocalOrientation);
if (!wheelData.Collider)
continue;
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>
/// The maximum size.
/// </summary>
API_FIELD() Vector2 MaximumSize = Vector2(4096, 4096);
API_FIELD() Vector2 MaximumSize = Vector2(8192, 4096);
/// <summary>
/// The start position mode.
+12 -14
View File
@@ -386,33 +386,31 @@ struct TIsPODType<ProfilerCPU::Event>
enum { Value = true };
};
#include "ProfilerSrcLoc.h"
// 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
#define PROFILE_CPU_USE_TRANSIENT_DATA 0
#define PROFILE_CPU_NAMED(name) ZoneTransientN(___tracy_scoped_zone, name, true); ScopeProfileBlockCPU ProfileBlockCPU(name)
#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
#if PROFILE_CPU_USE_TRANSIENT_DATA
#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
#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
#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
#else
// Empty macros for disabled profiler
#define PROFILE_CPU_NAMED(name)
#define PROFILE_CPU()
#define PROFILE_CPU_NAMED(name)
#define PROFILE_CPU_SRC_LOC(srcLoc)
#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
@@ -60,18 +60,23 @@ MMethod::MMethod(MonoMethod* monoMethod, const char* name, MClass* parentClass)
ProfilerName.Get()[className.Length()] = ':';
ProfilerName.Get()[className.Length() + 1] = ':';
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
}
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);
}
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);
return mono_runtime_invoke(virtualMethod, instance, params, exception);
}
@@ -3,6 +3,9 @@
#pragma once
#include "Engine/Core/Collections/Array.h"
#if COMPILE_WITH_PROFILER
#include "Engine/Profiler/ProfilerSrcLoc.h"
#endif
#include "MTypes.h"
/// <summary>
@@ -42,6 +45,7 @@ public:
#if COMPILE_WITH_PROFILER
MString ProfilerName;
SourceLocationData ProfilerData;
#endif
#if USE_MONO
+44 -1
View File
@@ -9,13 +9,29 @@
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include "Engine/Core/ObjectsRemovalService.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 <ThirdParty/mono-2.0/mono/metadata/mono-gc.h>
namespace ProfilerInternal
{
#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
void BeginEvent(MonoString* nameObj)
@@ -24,7 +40,34 @@ namespace ProfilerInternal
const StringView name((const Char*)mono_string_chars(nameObj), mono_string_length(nameObj));
ProfilerCPU::BeginEvent(*name);
#if TRACY_ENABLE
#if PROFILE_CPU_USE_TRANSIENT_DATA
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
}
+14 -3
View File
@@ -212,6 +212,7 @@ namespace FlaxEngine.Json
break;
}
case JsonToken.Comment: break;
case JsonToken.String: break;
default: return value;
}
}
@@ -244,16 +245,21 @@ namespace FlaxEngine.Json
/// <inheritdoc />
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;
if (string.IsNullOrEmpty(str.Id))
if (string.IsNullOrEmpty(str.Id) && !writeTypename)
{
writer.WriteValue(str.Value);
writer.WriteValue(str.Value ?? string.Empty);
}
else
{
writer.WriteStartObject();
#if FLAX_EDITOR
if ((serializer.TypeNameHandling & TypeNameHandling.Objects) == TypeNameHandling.Objects)
if (writeTypename)
{
writer.WritePropertyName("$type");
writer.WriteValue("FlaxEngine.LocalizedString, FlaxEngine.CSharp");
@@ -297,10 +303,15 @@ namespace FlaxEngine.Json
break;
}
case JsonToken.Comment: break;
case JsonToken.String: break;
default: return str;
}
}
}
else
return null;
if (existingValue == null && str.Id == null && str.Value == null)
return null;
return str;
}
+7 -3
View File
@@ -134,9 +134,13 @@ void JobSystemService::Dispose()
for (int32 i = 0; i < ThreadsCount; i++)
{
if (Threads[i] && Threads[i]->IsRunning())
Threads[i]->Kill(true);
Threads[i] = nullptr;
if (Threads[i])
{
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;
_value = value;
SetUpdate(ref _update, null);
OnValueChanged();
}
}
+2 -2
View File
@@ -13,5 +13,5 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("b8442186-4a70-7c85-704a-857c262d00f6")]
[assembly: AssemblyVersion("1.2.6223")]
[assembly: AssemblyFileVersion("1.2.6223")]
[assembly: AssemblyVersion("1.2.6224")]
[assembly: AssemblyFileVersion("1.2.6224")]
+3 -3
View File
@@ -3,11 +3,11 @@
#pragma once
#define FLAXENGINE_NAME "FlaxEngine"
#define FLAXENGINE_VERSION Version(1, 2, 6223)
#define FLAXENGINE_VERSION_TEXT "1.2.6223"
#define FLAXENGINE_VERSION Version(1, 2, 6224)
#define FLAXENGINE_VERSION_TEXT "1.2.6224"
#define FLAXENGINE_VERSION_MAJOR 1
#define FLAXENGINE_VERSION_MINOR 2
#define FLAXENGINE_VERSION_BUILD 6223
#define FLAXENGINE_VERSION_BUILD 6224
#define FLAXENGINE_COMPANY "Flax"
#define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved."
+17
View File
@@ -12,8 +12,22 @@
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)
{
#ifdef TRACY_ON_DEMAND
if (!GetProfiler().IsConnected()) return;
#endif
TracyLfqPrepare( QueueType::ZoneBeginAllocSrcLoc );
const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz );
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()
{
#ifdef TRACY_ON_DEMAND
if (!GetProfiler().IsConnected()) return;
#endif
TracyLfqPrepare( QueueType::ZoneEnd );
MemWrite( &item->zoneEnd.time, Profiler::GetTime() );
TracyLfqCommit;
+1
View File
@@ -46,6 +46,7 @@ struct TRACY_API SourceLocationData
class TRACY_API ScopedZone
{
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 End();
@@ -665,7 +665,11 @@ namespace Flax.Build.Bindings
indent += " ";
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($"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);
contents.Append(indent).Append('}').AppendLine();
@@ -1028,7 +1028,7 @@ namespace Flax.Build.Bindings
contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;");
contents.AppendLine($" ASSERT(scriptVTable && 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(" auto prevWrapperCallInstance = WrapperCallInstance;");
@@ -1290,7 +1290,7 @@ namespace Flax.Build.Bindings
contents.Append(" if (!mmethod)").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(" PROFILE_CPU_NAMED(*mmethod->ProfilerName);").AppendLine();
contents.Append($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::On{eventInfo.Name}\");").AppendLine();
contents.Append(" MonoObject* exception = nullptr;").AppendLine();
if (paramsCount == 0)
contents.AppendLine(" void** params = nullptr;");