Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c328d2b559 | |||
| 65747c9ddf | |||
| d4a4ae996e | |||
| e312485a34 | |||
| 7dfe709196 | |||
| 759845c37b | |||
| 5e3fe56aa7 | |||
| 4b506560e9 | |||
| 330c46b949 | |||
| b29d1f9902 | |||
| 15f60b33b1 | |||
| 1a54ea82b5 | |||
| 949bc67354 | |||
| d816af41b4 | |||
| a7880d73f3 | |||
| ac21c4d1e7 | |||
| ed8a10f276 | |||
| 8b8a83eb20 | |||
| 890ea38470 | |||
| 07d06c74b5 | |||
| ec18b01ef7 | |||
| 7a1817b099 | |||
| a2782515a2 | |||
| 9deb021a5e | |||
| 0bbb72e24d | |||
| 7656cf541d | |||
| d77ad33583 | |||
| 6f5605b7ca | |||
| 718ae78036 | |||
| f6eef82864 | |||
| 26d37103ba | |||
| d85c65bfc5 | |||
| 1943c64a33 | |||
| 9c1fa7c0ce | |||
| 14068307d1 | |||
| 670e48ec1c | |||
| e288104472 | |||
| 99b0cf71a8 | |||
| 2b7e6944e8 | |||
| 9e7ce69e9c | |||
| 8c97a645e9 | |||
| d194a06e59 | |||
| 783372c787 | |||
| 51016949b8 |
@@ -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
@@ -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.",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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++)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, ¢erOfMassOffset);
|
||||
PxRigidBodyExt::updateMassAndInertia(*_actor, densityKGPerCubicUU);
|
||||
|
||||
// Grab old mass so we can apply new mass while maintaining inertia tensor
|
||||
const float oldMass = _actor->getMass();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ namespace FlaxEngine.GUI
|
||||
{
|
||||
_targetValue = value;
|
||||
_value = value;
|
||||
SetUpdate(ref _update, null);
|
||||
OnValueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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
@@ -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;
|
||||
|
||||
@@ -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;");
|
||||
|
||||
Reference in New Issue
Block a user