Merge remote-tracking branch 'origin/master' into 1.8
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,6 +12,7 @@ Source/*.csproj
|
||||
/Package_*/
|
||||
!Source/Engine/Debug
|
||||
/Source/Platforms/Editor/Linux/Mono/etc/mono/registry
|
||||
PackageEditor_Cert.command
|
||||
PackageEditor_Cert.bat
|
||||
PackagePlatforms_Cert.bat
|
||||
|
||||
|
||||
BIN
Content/Editor/Particles/Smoke.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Smoke.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Particles/Sparks.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Sparks.flax
(Stored with Git LFS)
Binary file not shown.
@@ -33,4 +33,3 @@ public class %class% : Script
|
||||
// Here you can add code that needs to be called every frame
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"Version": {
|
||||
"Major": 1,
|
||||
"Minor": 8,
|
||||
"Revision": 0,
|
||||
"Build": 6500
|
||||
},
|
||||
"Company": "Flax",
|
||||
|
||||
@@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed
|
||||
|
||||
:: Build bindings for all editor configurations
|
||||
echo Building C# bindings...
|
||||
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame
|
||||
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor
|
||||
|
||||
popd
|
||||
echo Done!
|
||||
|
||||
@@ -14,4 +14,4 @@ bash ./Development/Scripts/Mac/CallBuildTool.sh --genproject "$@"
|
||||
# Build bindings for all editor configurations
|
||||
echo Building C# bindings...
|
||||
# TODO: Detect the correct architecture here
|
||||
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor,FlaxGame
|
||||
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor
|
||||
|
||||
@@ -14,4 +14,4 @@ bash ./Development/Scripts/Linux/CallBuildTool.sh --genproject "$@"
|
||||
# Build bindings for all editor configurations
|
||||
echo Building C# bindings...
|
||||
# TODO: Detect the correct architecture here
|
||||
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor,FlaxGame
|
||||
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<a href="https://flaxengine.com/discord"><img src="https://discordapp.com/api/guilds/437989205315158016/widget.png"/></a>
|
||||
|
||||
Flax Engine is a high quality modern 3D game engine written in C++ and C#.
|
||||
From stunning graphics to powerful scripts - Flax can give everything for your games. Designed for fast workflow with many ready to use features waiting for you right now. To learn more see the website ([www.flaxengine.com](https://flaxengine.com)).
|
||||
From stunning graphics to powerful scripts, it's designed for fast workflow with many ready-to-use features waiting for you right now. To learn more see the website ([www.flaxengine.com](https://flaxengine.com)).
|
||||
|
||||
This repository contains full source code of the Flax Engine (excluding NDA-protected platforms support). Anyone is welcome to contribute or use the modified source in Flax-based games.
|
||||
|
||||
|
||||
@@ -104,4 +104,19 @@ bool LinuxPlatformTools::OnDeployBinaries(CookingData& data)
|
||||
return false;
|
||||
}
|
||||
|
||||
void LinuxPlatformTools::OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir)
|
||||
{
|
||||
// Pick the first executable file
|
||||
Array<String> files;
|
||||
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
|
||||
for (auto& file : files)
|
||||
{
|
||||
if (FileSystem::GetExtension(file).IsEmpty())
|
||||
{
|
||||
executableFile = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
ArchitectureType GetArchitecture() const override;
|
||||
bool UseSystemDotnet() const override;
|
||||
bool OnDeployBinaries(CookingData& data) override;
|
||||
void OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -249,4 +249,19 @@ bool MacPlatformTools::OnPostProcess(CookingData& data)
|
||||
return false;
|
||||
}
|
||||
|
||||
void MacPlatformTools::OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir)
|
||||
{
|
||||
// Pick the first executable file
|
||||
Array<String> files;
|
||||
FileSystem::DirectoryGetFiles(files, data.NativeCodeOutputPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
|
||||
for (auto& file : files)
|
||||
{
|
||||
if (FileSystem::GetExtension(file).IsEmpty())
|
||||
{
|
||||
executableFile = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
bool IsNativeCodeFile(CookingData& data, const String& file) override;
|
||||
void OnBuildStarted(CookingData& data) override;
|
||||
bool OnPostProcess(CookingData& data) override;
|
||||
void OnRun(CookingData& data, String& executableFile, String& commandLineFormat, String& workingDir) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -225,8 +225,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
_actor = actor;
|
||||
|
||||
var showActorPicker = actor == null || ParentEditor.Values.All(x => x is not Cloth);
|
||||
if (showActorPicker)
|
||||
if (ParentEditor.Values.Any(x => x is Cloth))
|
||||
{
|
||||
// Cloth always picks the parent model mesh
|
||||
if (actor == null)
|
||||
{
|
||||
layout.Label("Cloth needs to be added as a child to model actor.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Actor reference picker
|
||||
_actorPicker = layout.Custom<FlaxObjectRefPickerControl>();
|
||||
@@ -242,7 +249,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
var model = staticModel.Model;
|
||||
if (model == null || model.WaitForLoaded())
|
||||
{
|
||||
layout.Label("No model.");
|
||||
return;
|
||||
}
|
||||
var materials = model.MaterialSlots;
|
||||
var lods = model.LODs;
|
||||
meshNames = new string[lods.Length][];
|
||||
@@ -267,7 +277,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
var skinnedModel = animatedModel.SkinnedModel;
|
||||
if (skinnedModel == null || skinnedModel.WaitForLoaded())
|
||||
{
|
||||
layout.Label("No model.");
|
||||
return;
|
||||
}
|
||||
var materials = skinnedModel.MaterialSlots;
|
||||
var lods = skinnedModel.LODs;
|
||||
meshNames = new string[lods.Length][];
|
||||
|
||||
@@ -44,11 +44,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
var mousePos = window.MousePosition;
|
||||
var previousSize = window.Size;
|
||||
window.Restore();
|
||||
window.Position = FlaxEngine.Input.MouseScreenPosition - mousePos * window.Size / previousSize;
|
||||
window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
|
||||
}
|
||||
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
var mouseScreenPosition = FlaxEngine.Input.MouseScreenPosition;
|
||||
var mouseScreenPosition = Platform.MousePosition;
|
||||
|
||||
// If the _toMove window was not focused when initializing this window, the result vector only contains zeros
|
||||
// and to prevent a failure, we need to perform an update for the drag offset at later time which will be done in the OnMouseMove event handler.
|
||||
@@ -83,6 +83,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
// Enable hit window presentation
|
||||
Proxy.Window.RenderingEnabled = true;
|
||||
Proxy.Window.Show();
|
||||
Proxy.Window.Focus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,7 +114,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
var window = _toMove.Window?.Window;
|
||||
if (window == null)
|
||||
return;
|
||||
var mouse = FlaxEngine.Input.MouseScreenPosition;
|
||||
var mouse = Platform.MousePosition;
|
||||
|
||||
// Move base window
|
||||
window.Position = mouse - _dragOffset;
|
||||
@@ -193,7 +194,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
|
||||
// Move window to the mouse position (with some offset for caption bar)
|
||||
var window = (WindowRootControl)toMove.Root;
|
||||
var mouse = FlaxEngine.Input.MouseScreenPosition;
|
||||
var mouse = Platform.MousePosition;
|
||||
window.Window.Position = mouse - new Float2(8, 8);
|
||||
|
||||
// Get floating panel
|
||||
@@ -244,7 +245,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
private void UpdateRects()
|
||||
{
|
||||
// Cache mouse position
|
||||
_mouse = FlaxEngine.Input.MouseScreenPosition;
|
||||
_mouse = Platform.MousePosition;
|
||||
|
||||
// Check intersection with any dock panel
|
||||
var uiMouse = _mouse;
|
||||
@@ -270,15 +271,16 @@ namespace FlaxEditor.GUI.Docking
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size;
|
||||
var offset = _rectDock.Location;
|
||||
float BorderMargin = 4.0f;
|
||||
float ProxyHintWindowsSize2 = Proxy.HintWindowsSize * 0.5f;
|
||||
float centerX = size.X * 0.5f;
|
||||
float centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - ProxyHintWindowsSize2, BorderMargin, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - ProxyHintWindowsSize2, size.Y - Proxy.HintWindowsSize - BorderMargin, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(BorderMargin, centerY - ProxyHintWindowsSize2, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - Proxy.HintWindowsSize - BorderMargin, centerY - ProxyHintWindowsSize2, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - ProxyHintWindowsSize2, centerY - ProxyHintWindowsSize2, Proxy.HintWindowsSize, Proxy.HintWindowsSize) + offset;
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var centerX = size.X * 0.5f;
|
||||
var centerY = size.Y * 0.5f;
|
||||
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
|
||||
|
||||
// Hit test
|
||||
DockState toSet = DockState.Float;
|
||||
@@ -428,7 +430,6 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
if (Window == null)
|
||||
{
|
||||
// Create proxy window
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = "DockHint.Window";
|
||||
settings.Size = initSize;
|
||||
@@ -440,12 +441,10 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.IsRegularWindow = false;
|
||||
settings.SupportsTransparency = true;
|
||||
settings.ShowInTaskbar = false;
|
||||
settings.ShowAfterFirstPaint = true;
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
settings.IsTopmost = true;
|
||||
|
||||
Window = Platform.CreateWindow(ref settings);
|
||||
|
||||
// Set opacity and background color
|
||||
Window.Opacity = 0.6f;
|
||||
Window.GUI.BackgroundColor = Style.Current.DragWindow;
|
||||
}
|
||||
@@ -465,7 +464,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
|
||||
var settings = CreateWindowSettings.Default;
|
||||
settings.Title = name;
|
||||
settings.Size = new Float2(HintWindowsSize);
|
||||
settings.Size = new Float2(HintWindowsSize * Platform.DpiScale);
|
||||
settings.AllowInput = false;
|
||||
settings.AllowMaximize = false;
|
||||
settings.AllowMinimize = false;
|
||||
@@ -479,7 +478,6 @@ namespace FlaxEditor.GUI.Docking
|
||||
settings.ShowAfterFirstPaint = false;
|
||||
|
||||
win = Platform.CreateWindow(ref settings);
|
||||
|
||||
win.Opacity = 0.6f;
|
||||
win.GUI.BackgroundColor = Style.Current.DragWindow;
|
||||
}
|
||||
|
||||
@@ -776,12 +776,21 @@ namespace FlaxEditor.GUI.Tree
|
||||
// Check if mouse hits arrow
|
||||
if (_mouseOverArrow && HasAnyVisibleChild)
|
||||
{
|
||||
// Toggle open state
|
||||
if (ParentTree.Root.GetKey(KeyboardKeys.Alt))
|
||||
{
|
||||
if (_opened)
|
||||
CollapseAll();
|
||||
else
|
||||
ExpandAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_opened)
|
||||
Collapse();
|
||||
else
|
||||
Expand();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if mouse hits bar
|
||||
if (button == MouseButton.Right && TestHeaderHit(ref location))
|
||||
|
||||
@@ -513,7 +513,9 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
}
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
for (auto& win : WindowsManager::Windows)
|
||||
Array<Window*, InlinedAllocation<32>> windows;
|
||||
windows.Add(WindowsManager::Windows);
|
||||
for (Window* win : windows)
|
||||
{
|
||||
if (win->IsVisible())
|
||||
win->OnUpdate(deltaTime);
|
||||
|
||||
@@ -237,7 +237,11 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public void LoadDefaultLayout()
|
||||
{
|
||||
LoadLayout(StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/LayoutDefault.xml"));
|
||||
var path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/LayoutDefault.xml");
|
||||
if (File.Exists(path))
|
||||
{
|
||||
LoadLayout(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -259,10 +259,7 @@ namespace FlaxEditor.Options
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
@@ -270,9 +267,7 @@ namespace FlaxEditor.Options
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
@@ -284,7 +279,6 @@ namespace FlaxEditor.Options
|
||||
InputBinding.TryParse(str, out var result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
@@ -295,7 +289,6 @@ namespace FlaxEditor.Options
|
||||
{
|
||||
return ((InputBinding)value).ToString();
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,45 +26,108 @@ namespace FlaxEditor.Options
|
||||
public float MouseWheelSensitivity { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default movement speed for the viewport camera (must match the dropdown menu values in the viewport).
|
||||
/// Gets or sets the total amount of steps the camera needs to go from minimum to maximum speed.
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f), Limit(0.01f, 100.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(110), Tooltip("The default movement speed for the viewport camera (must match the dropdown menu values in the viewport).")]
|
||||
public float DefaultMovementSpeed { get; set; } = 1.0f;
|
||||
[DefaultValue(64), Limit(1, 128)]
|
||||
[EditorDisplay("Camera"), EditorOrder(110), Tooltip("The total amount of steps the camera needs to go from minimum to maximum speed.")]
|
||||
public int TotalCameraSpeedSteps { get; set; } = 64;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the degree to which the camera will be eased when using camera flight in the editor window.
|
||||
/// </summary>
|
||||
[DefaultValue(3.0f), Limit(1.0f, 8.0f)]
|
||||
[EditorDisplay("Camera"), EditorOrder(111), Tooltip("The degree to which the camera will be eased when using camera flight in the editor window (ignored if camera easing degree is enabled).")]
|
||||
public float CameraEasingDegree { get; set; } = 3.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default movement speed for the viewport camera (must be in range between minimum and maximum movement speed values).
|
||||
/// </summary>
|
||||
[DefaultValue(1.0f), Limit(0.05f, 32.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(120), Tooltip("The default movement speed for the viewport camera (must be in range between minimum and maximum movement speed values).")]
|
||||
public float MovementSpeed { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default minimum camera movement speed.
|
||||
/// </summary>
|
||||
[DefaultValue(0.05f), Limit(0.05f, 32.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(121), Tooltip("The default minimum movement speed for the viewport camera.")]
|
||||
public float MinMovementSpeed { get; set; } = 0.05f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default maximum camera movement speed.
|
||||
/// </summary>
|
||||
[DefaultValue(32.0f), Limit(16.0f, 1000.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(122), Tooltip("The default maximum movement speed for the viewport camera.")]
|
||||
public float MaxMovementSpeed { get; set; } = 32f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default camera easing mode.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(130), Tooltip("The default camera easing mode.")]
|
||||
public bool UseCameraEasing { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default near clipping plane distance for the viewport camera.
|
||||
/// </summary>
|
||||
[DefaultValue(10.0f), Limit(0.001f, 1000.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(120), Tooltip("The default near clipping plane distance for the viewport camera.")]
|
||||
public float DefaultNearPlane { get; set; } = 10.0f;
|
||||
[EditorDisplay("Defaults"), EditorOrder(140), Tooltip("The default near clipping plane distance for the viewport camera.")]
|
||||
public float NearPlane { get; set; } = 10.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default far clipping plane distance for the viewport camera.
|
||||
/// </summary>
|
||||
[DefaultValue(40000.0f), Limit(10.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(130), Tooltip("The default far clipping plane distance for the viewport camera.")]
|
||||
public float DefaultFarPlane { get; set; } = 40000.0f;
|
||||
[EditorDisplay("Defaults"), EditorOrder(150), Tooltip("The default far clipping plane distance for the viewport camera.")]
|
||||
public float FarPlane { get; set; } = 40000.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default field of view angle (in degrees) for the viewport camera.
|
||||
/// </summary>
|
||||
[DefaultValue(60.0f), Limit(35.0f, 160.0f, 0.1f)]
|
||||
[EditorDisplay("Defaults", "Default Field Of View"), EditorOrder(140), Tooltip("The default field of view angle (in degrees) for the viewport camera.")]
|
||||
public float DefaultFieldOfView { get; set; } = 60.0f;
|
||||
[EditorDisplay("Defaults"), EditorOrder(160), Tooltip("The default field of view angle (in degrees) for the viewport camera.")]
|
||||
public float FieldOfView { get; set; } = 60.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the panning direction is inverted for the viewport camera.
|
||||
/// Gets or sets the default camera orthographic mode.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(150), Tooltip("Invert the panning direction for the viewport camera.")]
|
||||
public bool DefaultInvertPanning { get; set; } = false;
|
||||
[EditorDisplay("Defaults"), EditorOrder(170), Tooltip("The default camera orthographic mode.")]
|
||||
public bool UseOrthographicProjection { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Scales editor viewport grid.
|
||||
/// Gets or sets the default camera orthographic scale (if camera uses orthographic mode).
|
||||
/// </summary>
|
||||
[DefaultValue(5.0f), Limit(0.001f, 100000.0f, 0.1f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(180), Tooltip("The default camera orthographic scale (if camera uses orthographic mode).")]
|
||||
public float OrthographicScale { get; set; } = 5.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default panning direction for the viewport camera.
|
||||
/// </summary>
|
||||
[DefaultValue(false)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(190), Tooltip("The default panning direction for the viewport camera.")]
|
||||
public bool InvertPanning { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default relative panning mode.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(200), Tooltip("The default relative panning mode. Uses distance between camera and target to determine panning speed.")]
|
||||
public bool UseRelativePanning { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default panning speed (ignored if relative panning is speed enabled).
|
||||
/// </summary>
|
||||
[DefaultValue(0.8f), Limit(0.01f, 128.0f, 0.1f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(210), Tooltip("The default camera panning speed (ignored if relative panning is enabled).")]
|
||||
public float PanningSpeed { get; set; } = 0.8f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default editor viewport grid scale.
|
||||
/// </summary>
|
||||
[DefaultValue(50.0f), Limit(25.0f, 500.0f, 5.0f)]
|
||||
[EditorDisplay("Defaults"), EditorOrder(160), Tooltip("Scales editor viewport grid.")]
|
||||
[EditorDisplay("Defaults"), EditorOrder(220), Tooltip("The default editor viewport grid scale.")]
|
||||
public float ViewportGridScale { get; set; } = 50.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,8 @@ bool ProjectInfo::LoadProject(const String& projectPath)
|
||||
Version = ::Version(
|
||||
JsonTools::GetInt(version, "Major", 0),
|
||||
JsonTools::GetInt(version, "Minor", 0),
|
||||
JsonTools::GetInt(version, "Build", 0));
|
||||
JsonTools::GetInt(version, "Build", -1),
|
||||
JsonTools::GetInt(version, "Revision", -1));
|
||||
}
|
||||
}
|
||||
if (Version.Revision() == 0)
|
||||
|
||||
@@ -23,18 +23,12 @@ namespace FlaxEditor
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else if (value is Version)
|
||||
{
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new JsonSerializationException("Expected Version object value");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the JSON representation of the object.
|
||||
@@ -47,17 +41,14 @@ namespace FlaxEditor
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (reader.TokenType == JsonToken.StartObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
reader.Read();
|
||||
Dictionary<string, int> values = new Dictionary<string, int>();
|
||||
var values = new Dictionary<string, int>();
|
||||
while (reader.TokenType == JsonToken.PropertyName)
|
||||
{
|
||||
var key = reader.Value as string;
|
||||
@@ -67,45 +58,43 @@ namespace FlaxEditor
|
||||
values.Add(key, (int)val);
|
||||
}
|
||||
|
||||
int major = 0, minor = 0, build = 0;
|
||||
values.TryGetValue("Major", out major);
|
||||
values.TryGetValue("Minor", out minor);
|
||||
values.TryGetValue("Build", out build);
|
||||
values.TryGetValue("Major", out var major);
|
||||
values.TryGetValue("Minor", out var minor);
|
||||
if (!values.TryGetValue("Build", out var build))
|
||||
build = -1;
|
||||
if (!values.TryGetValue("Revision", out var revision))
|
||||
revision = -1;
|
||||
|
||||
Version v = new Version(major, minor, build);
|
||||
return v;
|
||||
if (build <= 0)
|
||||
return new Version(major, minor);
|
||||
if (revision <= 0)
|
||||
return new Version(major, minor, build);
|
||||
return new Version(major, minor, build, revision);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex);
|
||||
}
|
||||
}
|
||||
else if (reader.TokenType == JsonToken.String)
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
{
|
||||
try
|
||||
{
|
||||
Version v = new Version((string)reader.Value!);
|
||||
return v;
|
||||
return new Version((string)reader.Value!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(String.Format("Unexpected token or value when parsing version. Token: {0}, Value: {1}", reader.TokenType, reader.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance can convert the specified object type.
|
||||
/// </summary>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <returns><c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.</returns>
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(Version);
|
||||
|
||||
@@ -66,7 +66,8 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
_orderInParent = actor.OrderInParent;
|
||||
Visible = (actor.HideFlags & HideFlags.HideInHierarchy) == 0;
|
||||
|
||||
var id = actor.ID;
|
||||
// Pick the correct id when inside a prefab window.
|
||||
var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
|
||||
if (Editor.Instance.ProjectCache.IsExpandedActor(ref id))
|
||||
{
|
||||
Expand(true);
|
||||
@@ -171,7 +172,8 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
// Restore cached state on query filter clear
|
||||
if (noFilter && actor != null)
|
||||
{
|
||||
var id = actor.ID;
|
||||
// Pick the correct id when inside a prefab window.
|
||||
var id = actor.HasPrefabLink && actor.Scene.Scene == null ? actor.PrefabObjectID : actor.ID;
|
||||
isExpanded = Editor.Instance.ProjectCache.IsExpandedActor(ref id);
|
||||
}
|
||||
|
||||
@@ -301,10 +303,12 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
protected override void OnExpandedChanged()
|
||||
{
|
||||
base.OnExpandedChanged();
|
||||
var actor = Actor;
|
||||
|
||||
if (!IsLayoutLocked && Actor)
|
||||
if (!IsLayoutLocked && actor)
|
||||
{
|
||||
var id = Actor.ID;
|
||||
// Pick the correct id when inside a prefab window.
|
||||
var id = actor.HasPrefabLink && actor.Scene == null ? actor.PrefabObjectID : actor.ID;
|
||||
Editor.Instance.ProjectCache.SetExpandedActor(ref id, IsExpanded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +225,7 @@ namespace FlaxEngine.Tools
|
||||
var cloth = _cloth;
|
||||
if (cloth == null)
|
||||
return;
|
||||
var hasPaintInput = Owner.IsLeftMouseButtonDown && !Owner.IsAltKeyDown;
|
||||
|
||||
// Perform detailed tracing to find cursor location for the brush
|
||||
var ray = Owner.MouseRay;
|
||||
@@ -240,7 +241,7 @@ namespace FlaxEngine.Tools
|
||||
// Cursor hit other object or nothing
|
||||
PaintEnd();
|
||||
|
||||
if (Owner.IsLeftMouseButtonDown)
|
||||
if (hasPaintInput)
|
||||
{
|
||||
// Select something else
|
||||
var view = new Ray(Owner.ViewPosition, Owner.ViewDirection);
|
||||
@@ -253,7 +254,7 @@ namespace FlaxEngine.Tools
|
||||
}
|
||||
|
||||
// Handle painting
|
||||
if (Owner.IsLeftMouseButtonDown)
|
||||
if (hasPaintInput)
|
||||
PaintStart();
|
||||
else
|
||||
PaintEnd();
|
||||
|
||||
@@ -259,7 +259,10 @@ namespace FlaxEditor.Viewport.Cameras
|
||||
// Pan
|
||||
if (input.IsPanning)
|
||||
{
|
||||
var panningSpeed = 0.8f;
|
||||
var panningSpeed = (Viewport.RelativePanning)
|
||||
? Mathf.Abs((position - TargetPoint).Length) * 0.005f
|
||||
: Viewport.PanningSpeed;
|
||||
|
||||
if (Viewport.InvertPanning)
|
||||
{
|
||||
position += up * (mouseDelta.Y * panningSpeed);
|
||||
|
||||
@@ -128,12 +128,26 @@ namespace FlaxEditor.Viewport
|
||||
public const int FpsCameraFilteringFrames = 3;
|
||||
|
||||
/// <summary>
|
||||
/// The speed widget button.
|
||||
/// The camera settings widget.
|
||||
/// </summary>
|
||||
protected ViewportWidgetButton _speedWidget;
|
||||
protected ViewportWidgetsContainer _cameraWidget;
|
||||
|
||||
/// <summary>
|
||||
/// The camera settings widget button.
|
||||
/// </summary>
|
||||
protected ViewportWidgetButton _cameraButton;
|
||||
|
||||
/// <summary>
|
||||
/// The orthographic mode widget button.
|
||||
/// </summary>
|
||||
protected ViewportWidgetButton _orthographicModeButton;
|
||||
|
||||
private readonly Editor _editor;
|
||||
|
||||
private float _mouseSensitivity;
|
||||
private float _movementSpeed;
|
||||
private float _minMovementSpeed;
|
||||
private float _maxMovementSpeed;
|
||||
private float _mouseAccelerationScale;
|
||||
private bool _useMouseFiltering;
|
||||
private bool _useMouseAcceleration;
|
||||
@@ -174,11 +188,17 @@ namespace FlaxEditor.Viewport
|
||||
private float _fieldOfView;
|
||||
private float _nearPlane;
|
||||
private float _farPlane;
|
||||
private float _orthoSize = 1.0f;
|
||||
private bool _isOrtho = false;
|
||||
private float _wheelMovementChangeDeltaSum = 0;
|
||||
private float _orthoSize;
|
||||
private bool _isOrtho;
|
||||
private bool _useCameraEasing;
|
||||
private float _cameraEasingDegree;
|
||||
private float _panningSpeed;
|
||||
private bool _relativePanning;
|
||||
private bool _invertPanning;
|
||||
|
||||
private int _speedStep;
|
||||
private int _maxSpeedSteps;
|
||||
|
||||
/// <summary>
|
||||
/// Speed of the mouse.
|
||||
/// </summary>
|
||||
@@ -189,6 +209,25 @@ namespace FlaxEditor.Viewport
|
||||
/// </summary>
|
||||
public float MouseWheelZoomSpeedFactor = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Format of the text for the camera move speed.
|
||||
/// </summary>
|
||||
private string MovementSpeedTextFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Mathf.Abs(_movementSpeed - _maxMovementSpeed) < Mathf.Epsilon || Mathf.Abs(_movementSpeed - _minMovementSpeed) < Mathf.Epsilon)
|
||||
return "{0:0.##}";
|
||||
|
||||
if (_movementSpeed < 10.0f)
|
||||
return "{0:0.00}";
|
||||
else if (_movementSpeed < 100.0f)
|
||||
return "{0:0.0}";
|
||||
else
|
||||
return "{0:#}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the camera movement speed.
|
||||
/// </summary>
|
||||
@@ -197,17 +236,38 @@ namespace FlaxEditor.Viewport
|
||||
get => _movementSpeed;
|
||||
set
|
||||
{
|
||||
for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++)
|
||||
_movementSpeed = value;
|
||||
|
||||
if (_cameraButton != null)
|
||||
_cameraButton.Text = string.Format(MovementSpeedTextFormat, _movementSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum camera movement speed.
|
||||
/// </summary>
|
||||
public float MinMovementSpeed
|
||||
{
|
||||
if (Math.Abs(value - EditorViewportCameraSpeedValues[i]) < 0.001f)
|
||||
get => _minMovementSpeed;
|
||||
set => _minMovementSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum camera movement speed.
|
||||
/// </summary>
|
||||
public float MaxMovementSpeed
|
||||
{
|
||||
_movementSpeed = EditorViewportCameraSpeedValues[i];
|
||||
if (_speedWidget != null)
|
||||
_speedWidget.Text = _movementSpeed.ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
get => _maxMovementSpeed;
|
||||
set => _maxMovementSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the camera easing mode.
|
||||
/// </summary>
|
||||
public bool UseCameraEasing
|
||||
{
|
||||
get => _useCameraEasing;
|
||||
set => _useCameraEasing = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -396,6 +456,15 @@ namespace FlaxEditor.Viewport
|
||||
set => _isOrtho = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the panning speed should be relative to the camera target.
|
||||
/// </summary>
|
||||
public bool RelativePanning
|
||||
{
|
||||
get => _relativePanning;
|
||||
set => _relativePanning = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the panning direction is inverted.
|
||||
/// </summary>
|
||||
@@ -405,6 +474,15 @@ namespace FlaxEditor.Viewport
|
||||
set => _invertPanning = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the camera panning speed.
|
||||
/// </summary>
|
||||
public float PanningSpeed
|
||||
{
|
||||
get => _panningSpeed;
|
||||
set => _panningSpeed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input actions collection to processed during user input.
|
||||
/// </summary>
|
||||
@@ -419,6 +497,8 @@ namespace FlaxEditor.Viewport
|
||||
public EditorViewport(SceneRenderTask task, ViewportCamera camera, bool useWidgets)
|
||||
: base(task)
|
||||
{
|
||||
_editor = Editor.Instance;
|
||||
|
||||
_mouseAccelerationScale = 0.1f;
|
||||
_useMouseFiltering = false;
|
||||
_useMouseAcceleration = false;
|
||||
@@ -431,43 +511,299 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
// Setup options
|
||||
{
|
||||
var options = Editor.Instance.Options.Options;
|
||||
_movementSpeed = options.Viewport.DefaultMovementSpeed;
|
||||
_nearPlane = options.Viewport.DefaultNearPlane;
|
||||
_farPlane = options.Viewport.DefaultFarPlane;
|
||||
_fieldOfView = options.Viewport.DefaultFieldOfView;
|
||||
_invertPanning = options.Viewport.DefaultInvertPanning;
|
||||
|
||||
Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged;
|
||||
OnEditorOptionsChanged(options);
|
||||
SetupViewportOptions();
|
||||
}
|
||||
|
||||
// Initialize camera values from cache
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraMovementSpeedValue", out var cachedState))
|
||||
MovementSpeed = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraMinMovementSpeedValue", out cachedState))
|
||||
_minMovementSpeed = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraMaxMovementSpeedValue", out cachedState))
|
||||
_maxMovementSpeed = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("UseCameraEasingState", out cachedState))
|
||||
_useCameraEasing = bool.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraPanningSpeedValue", out cachedState))
|
||||
_panningSpeed = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraInvertPanningState", out cachedState))
|
||||
_invertPanning = bool.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraRelativePanningState", out cachedState))
|
||||
_relativePanning = bool.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraOrthographicState", out cachedState))
|
||||
_isOrtho = bool.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraOrthographicSizeValue", out cachedState))
|
||||
_orthoSize = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraFieldOfViewValue", out cachedState))
|
||||
_fieldOfView = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraNearPlaneValue", out cachedState))
|
||||
_nearPlane = float.Parse(cachedState);
|
||||
if (_editor.ProjectCache.TryGetCustomData("CameraFarPlaneValue", out cachedState))
|
||||
_farPlane = float.Parse(cachedState);
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
var largestText = "Invert Panning";
|
||||
#region Camera settings widget
|
||||
|
||||
var largestText = "Relative Panning";
|
||||
var textSize = Style.Current.FontMedium.MeasureText(largestText);
|
||||
var xLocationForExtras = textSize.X + 5;
|
||||
// Camera speed widget
|
||||
var camSpeed = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
var camSpeedCM = new ContextMenu();
|
||||
var camSpeedButton = new ViewportWidgetButton(_movementSpeed.ToString(), Editor.Instance.Icons.CamSpeed32, camSpeedCM)
|
||||
var cameraSpeedTextWidth = Style.Current.FontMedium.MeasureText("0.00").X;
|
||||
|
||||
// Camera Settings Widget
|
||||
_cameraWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
|
||||
// Camera Settings Menu
|
||||
var cameraCM = new ContextMenu();
|
||||
_cameraButton = new ViewportWidgetButton(string.Format(MovementSpeedTextFormat, _movementSpeed), Editor.Instance.Icons.Camera64, cameraCM, false, cameraSpeedTextWidth)
|
||||
{
|
||||
Tag = this,
|
||||
TooltipText = "Camera speed scale"
|
||||
TooltipText = "Camera Settings",
|
||||
Parent = _cameraWidget
|
||||
};
|
||||
_speedWidget = camSpeedButton;
|
||||
for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++)
|
||||
{
|
||||
var v = EditorViewportCameraSpeedValues[i];
|
||||
var button = camSpeedCM.AddButton(v.ToString());
|
||||
button.Tag = v;
|
||||
}
|
||||
camSpeedCM.ButtonClicked += button => MovementSpeed = (float)button.Tag;
|
||||
camSpeedCM.VisibleChanged += WidgetCamSpeedShowHide;
|
||||
camSpeedButton.Parent = camSpeed;
|
||||
camSpeed.Parent = this;
|
||||
_cameraWidget.Parent = this;
|
||||
|
||||
// Orthographic/Perspective Mode Widget
|
||||
_orthographicModeButton = new ViewportWidgetButton(string.Empty, Editor.Instance.Icons.CamSpeed32, null, true)
|
||||
{
|
||||
Checked = !_isOrtho,
|
||||
TooltipText = "Toggle Orthographic/Perspective Mode",
|
||||
Parent = _cameraWidget
|
||||
};
|
||||
_orthographicModeButton.Toggled += OnOrthographicModeToggled;
|
||||
|
||||
// Camera Speed
|
||||
var camSpeedButton = cameraCM.AddButton("Camera Speed");
|
||||
camSpeedButton.CloseMenuOnClick = false;
|
||||
var camSpeedValue = new FloatValueBox(_movementSpeed, xLocationForExtras, 2, 70.0f, _minMovementSpeed, _maxMovementSpeed, 0.5f)
|
||||
{
|
||||
Parent = camSpeedButton
|
||||
};
|
||||
|
||||
camSpeedValue.ValueChanged += () => OnMovementSpeedChanged(camSpeedValue);
|
||||
cameraCM.VisibleChanged += control => camSpeedValue.Value = _movementSpeed;
|
||||
|
||||
// Minimum & Maximum Camera Speed
|
||||
var minCamSpeedButton = cameraCM.AddButton("Min Cam Speed");
|
||||
minCamSpeedButton.CloseMenuOnClick = false;
|
||||
var minCamSpeedValue = new FloatValueBox(_minMovementSpeed, xLocationForExtras, 2, 70.0f, 0.05f, _maxMovementSpeed, 0.5f)
|
||||
{
|
||||
Parent = minCamSpeedButton
|
||||
};
|
||||
var maxCamSpeedButton = cameraCM.AddButton("Max Cam Speed");
|
||||
maxCamSpeedButton.CloseMenuOnClick = false;
|
||||
var maxCamSpeedValue = new FloatValueBox(_maxMovementSpeed, xLocationForExtras, 2, 70.0f, _minMovementSpeed, 1000.0f, 0.5f)
|
||||
{
|
||||
Parent = maxCamSpeedButton
|
||||
};
|
||||
|
||||
minCamSpeedValue.ValueChanged += () =>
|
||||
{
|
||||
OnMinMovementSpeedChanged(minCamSpeedValue);
|
||||
|
||||
maxCamSpeedValue.MinValue = minCamSpeedValue.Value;
|
||||
|
||||
if (Math.Abs(camSpeedValue.MinValue - minCamSpeedValue.Value) > Mathf.Epsilon)
|
||||
camSpeedValue.MinValue = minCamSpeedValue.Value;
|
||||
};
|
||||
cameraCM.VisibleChanged += control => minCamSpeedValue.Value = _minMovementSpeed;
|
||||
maxCamSpeedValue.ValueChanged += () =>
|
||||
{
|
||||
OnMaxMovementSpeedChanged(maxCamSpeedValue);
|
||||
|
||||
minCamSpeedValue.MaxValue = maxCamSpeedValue.Value;
|
||||
|
||||
if (Math.Abs(camSpeedValue.MaxValue - maxCamSpeedValue.Value) > Mathf.Epsilon)
|
||||
camSpeedValue.MaxValue = maxCamSpeedValue.Value;
|
||||
};
|
||||
cameraCM.VisibleChanged += control => maxCamSpeedValue.Value = _maxMovementSpeed;
|
||||
|
||||
// Camera Easing
|
||||
{
|
||||
var useCameraEasing = cameraCM.AddButton("Camera Easing");
|
||||
useCameraEasing.CloseMenuOnClick = false;
|
||||
var useCameraEasingValue = new CheckBox(xLocationForExtras, 2, _useCameraEasing)
|
||||
{
|
||||
Parent = useCameraEasing
|
||||
};
|
||||
|
||||
useCameraEasingValue.StateChanged += OnCameraEasingToggled;
|
||||
cameraCM.VisibleChanged += control => useCameraEasingValue.Checked = _useCameraEasing;
|
||||
}
|
||||
|
||||
// Panning Speed
|
||||
{
|
||||
var panningSpeed = cameraCM.AddButton("Panning Speed");
|
||||
panningSpeed.CloseMenuOnClick = false;
|
||||
var panningSpeedValue = new FloatValueBox(_panningSpeed, xLocationForExtras, 2, 70.0f, 0.01f, 128.0f, 0.1f)
|
||||
{
|
||||
Parent = panningSpeed
|
||||
};
|
||||
|
||||
panningSpeedValue.ValueChanged += () => OnPanningSpeedChanged(panningSpeedValue);
|
||||
cameraCM.VisibleChanged += control =>
|
||||
{
|
||||
panningSpeed.Visible = !_relativePanning;
|
||||
panningSpeedValue.Value = _panningSpeed;
|
||||
};
|
||||
}
|
||||
|
||||
// Relative Panning
|
||||
{
|
||||
var relativePanning = cameraCM.AddButton("Relative Panning");
|
||||
relativePanning.CloseMenuOnClick = false;
|
||||
var relativePanningValue = new CheckBox(xLocationForExtras, 2, _relativePanning)
|
||||
{
|
||||
Parent = relativePanning
|
||||
};
|
||||
|
||||
relativePanningValue.StateChanged += checkBox =>
|
||||
{
|
||||
if (checkBox.Checked != _relativePanning)
|
||||
{
|
||||
OnRelativePanningToggled(checkBox);
|
||||
cameraCM.Hide();
|
||||
}
|
||||
};
|
||||
cameraCM.VisibleChanged += control => relativePanningValue.Checked = _relativePanning;
|
||||
}
|
||||
|
||||
// Invert Panning
|
||||
{
|
||||
var invertPanning = cameraCM.AddButton("Invert Panning");
|
||||
invertPanning.CloseMenuOnClick = false;
|
||||
var invertPanningValue = new CheckBox(xLocationForExtras, 2, _invertPanning)
|
||||
{
|
||||
Parent = invertPanning
|
||||
};
|
||||
|
||||
invertPanningValue.StateChanged += OnInvertPanningToggled;
|
||||
cameraCM.VisibleChanged += control => invertPanningValue.Checked = _invertPanning;
|
||||
}
|
||||
|
||||
cameraCM.AddSeparator();
|
||||
|
||||
// Camera Viewpoints
|
||||
{
|
||||
var cameraView = cameraCM.AddChildMenu("Viewpoints").ContextMenu;
|
||||
for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++)
|
||||
{
|
||||
var co = EditorViewportCameraViewpointValues[i];
|
||||
var button = cameraView.AddButton(co.Name);
|
||||
button.Tag = co.Orientation;
|
||||
}
|
||||
|
||||
cameraView.ButtonClicked += OnViewpointChanged;
|
||||
}
|
||||
|
||||
// Orthographic Mode
|
||||
{
|
||||
var ortho = cameraCM.AddButton("Orthographic");
|
||||
ortho.CloseMenuOnClick = false;
|
||||
var orthoValue = new CheckBox(xLocationForExtras, 2, _isOrtho)
|
||||
{
|
||||
Parent = ortho
|
||||
};
|
||||
|
||||
orthoValue.StateChanged += checkBox =>
|
||||
{
|
||||
if (checkBox.Checked != _isOrtho)
|
||||
{
|
||||
OnOrthographicModeToggled(checkBox);
|
||||
cameraCM.Hide();
|
||||
}
|
||||
};
|
||||
cameraCM.VisibleChanged += control => orthoValue.Checked = _isOrtho;
|
||||
}
|
||||
|
||||
// Field of View
|
||||
{
|
||||
var fov = cameraCM.AddButton("Field Of View");
|
||||
fov.CloseMenuOnClick = false;
|
||||
var fovValue = new FloatValueBox(_fieldOfView, xLocationForExtras, 2, 70.0f, 35.0f, 160.0f, 0.1f)
|
||||
{
|
||||
Parent = fov
|
||||
};
|
||||
|
||||
fovValue.ValueChanged += () => OnFieldOfViewChanged(fovValue);
|
||||
cameraCM.VisibleChanged += control =>
|
||||
{
|
||||
fov.Visible = !_isOrtho;
|
||||
fovValue.Value = _fieldOfView;
|
||||
};
|
||||
}
|
||||
|
||||
// Orthographic Scale
|
||||
{
|
||||
var orthoSize = cameraCM.AddButton("Ortho Scale");
|
||||
orthoSize.CloseMenuOnClick = false;
|
||||
var orthoSizeValue = new FloatValueBox(_orthoSize, xLocationForExtras, 2, 70.0f, 0.001f, 100000.0f, 0.01f)
|
||||
{
|
||||
Parent = orthoSize
|
||||
};
|
||||
|
||||
orthoSizeValue.ValueChanged += () => OnOrthographicSizeChanged(orthoSizeValue);
|
||||
cameraCM.VisibleChanged += control =>
|
||||
{
|
||||
orthoSize.Visible = _isOrtho;
|
||||
orthoSizeValue.Value = _orthoSize;
|
||||
};
|
||||
}
|
||||
|
||||
// Near Plane
|
||||
{
|
||||
var nearPlane = cameraCM.AddButton("Near Plane");
|
||||
nearPlane.CloseMenuOnClick = false;
|
||||
var nearPlaneValue = new FloatValueBox(_nearPlane, xLocationForExtras, 2, 70.0f, 0.001f, 1000.0f)
|
||||
{
|
||||
Parent = nearPlane
|
||||
};
|
||||
|
||||
nearPlaneValue.ValueChanged += () => OnNearPlaneChanged(nearPlaneValue);
|
||||
cameraCM.VisibleChanged += control => nearPlaneValue.Value = _nearPlane;
|
||||
}
|
||||
|
||||
// Far Plane
|
||||
{
|
||||
var farPlane = cameraCM.AddButton("Far Plane");
|
||||
farPlane.CloseMenuOnClick = false;
|
||||
var farPlaneValue = new FloatValueBox(_farPlane, xLocationForExtras, 2, 70.0f, 10.0f)
|
||||
{
|
||||
Parent = farPlane
|
||||
};
|
||||
|
||||
farPlaneValue.ValueChanged += () => OnFarPlaneChanged(farPlaneValue);
|
||||
cameraCM.VisibleChanged += control => farPlaneValue.Value = _farPlane;
|
||||
}
|
||||
|
||||
cameraCM.AddSeparator();
|
||||
|
||||
// Reset Button
|
||||
{
|
||||
var reset = cameraCM.AddButton("Reset to default");
|
||||
reset.ButtonClicked += button =>
|
||||
{
|
||||
SetupViewportOptions();
|
||||
|
||||
// if the context menu is opened without triggering the value changes beforehand,
|
||||
// the movement speed will not be correctly reset to its default value in certain cases
|
||||
// therefore, a UI update needs to be triggered here
|
||||
minCamSpeedValue.Value = _minMovementSpeed;
|
||||
camSpeedValue.Value = _movementSpeed;
|
||||
maxCamSpeedValue.Value = _maxMovementSpeed;
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Camera settings widget
|
||||
|
||||
#region View mode widget
|
||||
|
||||
largestText = "Brightness";
|
||||
textSize = Style.Current.FontMedium.MeasureText(largestText);
|
||||
xLocationForExtras = textSize.X + 5;
|
||||
|
||||
// View mode widget
|
||||
var viewMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperLeft);
|
||||
ViewWidgetButtonMenu = new ContextMenu();
|
||||
var viewModeButton = new ViewportWidgetButton("View", SpriteHandle.Invalid, ViewWidgetButtonMenu)
|
||||
@@ -484,8 +820,8 @@ namespace FlaxEditor.Viewport
|
||||
// Show FPS
|
||||
{
|
||||
InitFpsCounter();
|
||||
_showFpsButon = ViewWidgetShowMenu.AddButton("FPS Counter", () => ShowFpsCounter = !ShowFpsCounter);
|
||||
_showFpsButon.CloseMenuOnClick = false;
|
||||
_showFpsButton = ViewWidgetShowMenu.AddButton("FPS Counter", () => ShowFpsCounter = !ShowFpsCounter);
|
||||
_showFpsButton.CloseMenuOnClick = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,104 +946,6 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
ViewWidgetButtonMenu.AddSeparator();
|
||||
|
||||
// Orthographic
|
||||
{
|
||||
var ortho = ViewWidgetButtonMenu.AddButton("Orthographic");
|
||||
ortho.CloseMenuOnClick = false;
|
||||
var orthoValue = new CheckBox(xLocationForExtras, 2, _isOrtho)
|
||||
{
|
||||
Parent = ortho
|
||||
};
|
||||
orthoValue.StateChanged += checkBox =>
|
||||
{
|
||||
if (checkBox.Checked != _isOrtho)
|
||||
{
|
||||
_isOrtho = checkBox.Checked;
|
||||
ViewWidgetButtonMenu.Hide();
|
||||
if (_isOrtho)
|
||||
{
|
||||
var orient = ViewOrientation;
|
||||
OrientViewport(ref orient);
|
||||
}
|
||||
}
|
||||
};
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => orthoValue.Checked = _isOrtho;
|
||||
}
|
||||
|
||||
// Camera Viewpoints
|
||||
{
|
||||
var cameraView = ViewWidgetButtonMenu.AddChildMenu("Viewpoints").ContextMenu;
|
||||
for (int i = 0; i < EditorViewportCameraViewpointValues.Length; i++)
|
||||
{
|
||||
var co = EditorViewportCameraViewpointValues[i];
|
||||
var button = cameraView.AddButton(co.Name);
|
||||
button.Tag = co.Orientation;
|
||||
}
|
||||
cameraView.ButtonClicked += button =>
|
||||
{
|
||||
var orient = Quaternion.Euler((Float3)button.Tag);
|
||||
OrientViewport(ref orient);
|
||||
};
|
||||
}
|
||||
|
||||
// Field of View
|
||||
{
|
||||
var fov = ViewWidgetButtonMenu.AddButton("Field Of View");
|
||||
fov.CloseMenuOnClick = false;
|
||||
var fovValue = new FloatValueBox(1, xLocationForExtras, 2, 70.0f, 35.0f, 160.0f, 0.1f)
|
||||
{
|
||||
Parent = fov
|
||||
};
|
||||
|
||||
fovValue.ValueChanged += () => _fieldOfView = fovValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control =>
|
||||
{
|
||||
fov.Visible = !_isOrtho;
|
||||
fovValue.Value = _fieldOfView;
|
||||
};
|
||||
}
|
||||
|
||||
// Ortho Scale
|
||||
{
|
||||
var orthoSize = ViewWidgetButtonMenu.AddButton("Ortho Scale");
|
||||
orthoSize.CloseMenuOnClick = false;
|
||||
var orthoSizeValue = new FloatValueBox(_orthoSize, xLocationForExtras, 2, 70.0f, 0.001f, 100000.0f, 0.01f)
|
||||
{
|
||||
Parent = orthoSize
|
||||
};
|
||||
|
||||
orthoSizeValue.ValueChanged += () => _orthoSize = orthoSizeValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control =>
|
||||
{
|
||||
orthoSize.Visible = _isOrtho;
|
||||
orthoSizeValue.Value = _orthoSize;
|
||||
};
|
||||
}
|
||||
|
||||
// Near Plane
|
||||
{
|
||||
var nearPlane = ViewWidgetButtonMenu.AddButton("Near Plane");
|
||||
nearPlane.CloseMenuOnClick = false;
|
||||
var nearPlaneValue = new FloatValueBox(2.0f, xLocationForExtras, 2, 70.0f, 0.001f, 1000.0f)
|
||||
{
|
||||
Parent = nearPlane
|
||||
};
|
||||
nearPlaneValue.ValueChanged += () => _nearPlane = nearPlaneValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => nearPlaneValue.Value = _nearPlane;
|
||||
}
|
||||
|
||||
// Far Plane
|
||||
{
|
||||
var farPlane = ViewWidgetButtonMenu.AddButton("Far Plane");
|
||||
farPlane.CloseMenuOnClick = false;
|
||||
var farPlaneValue = new FloatValueBox(1000, xLocationForExtras, 2, 70.0f, 10.0f)
|
||||
{
|
||||
Parent = farPlane
|
||||
};
|
||||
farPlaneValue.ValueChanged += () => _farPlane = farPlaneValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => farPlaneValue.Value = _farPlane;
|
||||
}
|
||||
|
||||
// Brightness
|
||||
{
|
||||
var brightness = ViewWidgetButtonMenu.AddButton("Brightness");
|
||||
@@ -732,24 +970,7 @@ namespace FlaxEditor.Viewport
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale;
|
||||
}
|
||||
|
||||
// Invert Panning
|
||||
{
|
||||
var invert = ViewWidgetButtonMenu.AddButton("Invert Panning");
|
||||
invert.CloseMenuOnClick = false;
|
||||
var invertValue = new CheckBox(xLocationForExtras, 2, _invertPanning)
|
||||
{
|
||||
Parent = invert
|
||||
};
|
||||
|
||||
invertValue.StateChanged += checkBox =>
|
||||
{
|
||||
if (checkBox.Checked != _invertPanning)
|
||||
{
|
||||
_invertPanning = checkBox.Checked;
|
||||
}
|
||||
};
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => invertValue.Checked = _invertPanning;
|
||||
}
|
||||
#endregion View mode widget
|
||||
}
|
||||
|
||||
InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Top").Orientation)));
|
||||
@@ -766,6 +987,135 @@ namespace FlaxEditor.Viewport
|
||||
task.Begin += OnRenderBegin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the viewport options to the default values.
|
||||
/// </summary>
|
||||
private void SetupViewportOptions()
|
||||
{
|
||||
var options = Editor.Instance.Options.Options;
|
||||
_minMovementSpeed = options.Viewport.MinMovementSpeed;
|
||||
MovementSpeed = options.Viewport.MovementSpeed;
|
||||
_maxMovementSpeed = options.Viewport.MaxMovementSpeed;
|
||||
_useCameraEasing = options.Viewport.UseCameraEasing;
|
||||
_panningSpeed = options.Viewport.PanningSpeed;
|
||||
_invertPanning = options.Viewport.InvertPanning;
|
||||
_relativePanning = options.Viewport.UseRelativePanning;
|
||||
|
||||
_isOrtho = options.Viewport.UseOrthographicProjection;
|
||||
_orthoSize = options.Viewport.OrthographicScale;
|
||||
_fieldOfView = options.Viewport.FieldOfView;
|
||||
_nearPlane = options.Viewport.NearPlane;
|
||||
_farPlane = options.Viewport.FarPlane;
|
||||
|
||||
OnEditorOptionsChanged(options);
|
||||
}
|
||||
|
||||
private void OnMovementSpeedChanged(FloatValueBox control)
|
||||
{
|
||||
var value = Mathf.Clamp(control.Value, _minMovementSpeed, _maxMovementSpeed);
|
||||
MovementSpeed = value;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
_editor.ProjectCache.SetCustomData("CameraMovementSpeedValue", _movementSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnMinMovementSpeedChanged(FloatValueBox control)
|
||||
{
|
||||
var value = Mathf.Clamp(control.Value, 0.05f, _maxMovementSpeed);
|
||||
_minMovementSpeed = value;
|
||||
|
||||
if (_movementSpeed < value)
|
||||
MovementSpeed = value;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
_editor.ProjectCache.SetCustomData("CameraMinMovementSpeedValue", _minMovementSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnMaxMovementSpeedChanged(FloatValueBox control)
|
||||
{
|
||||
var value = Mathf.Clamp(control.Value, _minMovementSpeed, 1000.0f);
|
||||
_maxMovementSpeed = value;
|
||||
|
||||
if (_movementSpeed > value)
|
||||
MovementSpeed = value;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
_editor.ProjectCache.SetCustomData("CameraMaxMovementSpeedValue", _maxMovementSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnCameraEasingToggled(Control control)
|
||||
{
|
||||
_useCameraEasing = !_useCameraEasing;
|
||||
|
||||
OnCameraMovementProgressChanged();
|
||||
_editor.ProjectCache.SetCustomData("UseCameraEasingState", _useCameraEasing.ToString());
|
||||
}
|
||||
|
||||
private void OnPanningSpeedChanged(FloatValueBox control)
|
||||
{
|
||||
_panningSpeed = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraPanningSpeedValue", _panningSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnRelativePanningToggled(Control control)
|
||||
{
|
||||
_relativePanning = !_relativePanning;
|
||||
_editor.ProjectCache.SetCustomData("CameraRelativePanningState", _relativePanning.ToString());
|
||||
}
|
||||
|
||||
private void OnInvertPanningToggled(Control control)
|
||||
{
|
||||
_invertPanning = !_invertPanning;
|
||||
_editor.ProjectCache.SetCustomData("CameraInvertPanningState", _invertPanning.ToString());
|
||||
}
|
||||
|
||||
|
||||
private void OnViewpointChanged(ContextMenuButton button)
|
||||
{
|
||||
var orient = Quaternion.Euler((Float3)button.Tag);
|
||||
OrientViewport(ref orient);
|
||||
}
|
||||
|
||||
private void OnFieldOfViewChanged(FloatValueBox control)
|
||||
{
|
||||
_fieldOfView = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraFieldOfViewValue", _fieldOfView.ToString());
|
||||
}
|
||||
|
||||
private void OnOrthographicModeToggled(Control control)
|
||||
{
|
||||
_isOrtho = !_isOrtho;
|
||||
|
||||
if (_orthographicModeButton != null)
|
||||
_orthographicModeButton.Checked = !_isOrtho;
|
||||
|
||||
if (_isOrtho)
|
||||
{
|
||||
var orient = ViewOrientation;
|
||||
OrientViewport(ref orient);
|
||||
}
|
||||
|
||||
_editor.ProjectCache.SetCustomData("CameraOrthographicState", _isOrtho.ToString());
|
||||
}
|
||||
|
||||
private void OnOrthographicSizeChanged(FloatValueBox control)
|
||||
{
|
||||
_orthoSize = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraOrthographicSizeValue", _orthoSize.ToString());
|
||||
}
|
||||
|
||||
private void OnNearPlaneChanged(FloatValueBox control)
|
||||
{
|
||||
_nearPlane = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraNearPlaneValue", _nearPlane.ToString());
|
||||
}
|
||||
|
||||
private void OnFarPlaneChanged(FloatValueBox control)
|
||||
{
|
||||
_farPlane = control.Value;
|
||||
_editor.ProjectCache.SetCustomData("CameraNearPlaneValue", _farPlane.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this viewport is using mouse currently (eg. user moving objects).
|
||||
/// </summary>
|
||||
@@ -798,33 +1148,59 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCameraMovementProgressChanged()
|
||||
{
|
||||
// prevent NaN
|
||||
if (Math.Abs(_minMovementSpeed - _maxMovementSpeed) < Mathf.Epsilon)
|
||||
{
|
||||
_speedStep = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(_movementSpeed - _maxMovementSpeed) < Mathf.Epsilon)
|
||||
{
|
||||
_speedStep = _maxSpeedSteps;
|
||||
return;
|
||||
}
|
||||
else if (Math.Abs(_movementSpeed - _minMovementSpeed) < Mathf.Epsilon)
|
||||
{
|
||||
_speedStep = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate current linear/eased progress
|
||||
var progress = Mathf.Remap(_movementSpeed, _minMovementSpeed, _maxMovementSpeed, 0.0f, 1.0f);
|
||||
|
||||
if (_useCameraEasing)
|
||||
progress = Mathf.Pow(progress, 1.0f / _cameraEasingDegree);
|
||||
|
||||
_speedStep = Mathf.RoundToInt(progress * _maxSpeedSteps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases or decreases the camera movement speed.
|
||||
/// </summary>
|
||||
/// <param name="step">The stepping direction for speed adjustment.</param>
|
||||
protected void AdjustCameraMoveSpeed(int step)
|
||||
{
|
||||
int camValueIndex = -1;
|
||||
for (int i = 0; i < EditorViewportCameraSpeedValues.Length; i++)
|
||||
{
|
||||
if (Mathf.NearEqual(EditorViewportCameraSpeedValues[i], _movementSpeed))
|
||||
{
|
||||
camValueIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (camValueIndex == -1)
|
||||
return;
|
||||
_speedStep = Mathf.Clamp(_speedStep + step, 0, _maxSpeedSteps);
|
||||
|
||||
if (step > 0)
|
||||
MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Min(camValueIndex + 1, EditorViewportCameraSpeedValues.Length - 1)];
|
||||
else if (step < 0)
|
||||
MovementSpeed = EditorViewportCameraSpeedValues[Mathf.Max(camValueIndex - 1, 0)];
|
||||
// calculate new linear/eased progress
|
||||
var progress = _useCameraEasing
|
||||
? Mathf.Pow((float)_speedStep / _maxSpeedSteps, _cameraEasingDegree)
|
||||
: (float)_speedStep / _maxSpeedSteps;
|
||||
|
||||
var speed = Mathf.Lerp(_minMovementSpeed, _maxMovementSpeed, progress);
|
||||
MovementSpeed = (float)Math.Round(speed, 3);
|
||||
_editor.ProjectCache.SetCustomData("CameraMovementSpeedValue", _movementSpeed.ToString());
|
||||
}
|
||||
|
||||
private void OnEditorOptionsChanged(EditorOptions options)
|
||||
{
|
||||
_mouseSensitivity = options.Viewport.MouseSensitivity;
|
||||
_maxSpeedSteps = options.Viewport.TotalCameraSpeedSteps;
|
||||
_cameraEasingDegree = options.Viewport.CameraEasingDegree;
|
||||
OnCameraMovementProgressChanged();
|
||||
}
|
||||
|
||||
private void OnRenderBegin(RenderTask task, GPUContext context)
|
||||
@@ -863,7 +1239,7 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
|
||||
private FpsCounter _fpsCounter;
|
||||
private ContextMenuButton _showFpsButon;
|
||||
private ContextMenuButton _showFpsButton;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show or hide FPS counter.
|
||||
@@ -875,7 +1251,7 @@ namespace FlaxEditor.Viewport
|
||||
{
|
||||
_fpsCounter.Visible = value;
|
||||
_fpsCounter.Enabled = value;
|
||||
_showFpsButon.Icon = value ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
_showFpsButton.Icon = value ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1016,8 +1392,6 @@ namespace FlaxEditor.Viewport
|
||||
/// <param name="win">The parent window.</param>
|
||||
protected virtual void OnControlMouseBegin(Window win)
|
||||
{
|
||||
_wheelMovementChangeDeltaSum = 0;
|
||||
|
||||
// Hide cursor and start tracking mouse movement
|
||||
win.StartTrackingMouse(false);
|
||||
win.Cursor = CursorType.Hidden;
|
||||
@@ -1113,8 +1487,8 @@ namespace FlaxEditor.Viewport
|
||||
_camera.Update(deltaTime);
|
||||
useMovementSpeed = _camera.UseMovementSpeed;
|
||||
|
||||
if (_speedWidget != null)
|
||||
_speedWidget.Parent.Visible = useMovementSpeed;
|
||||
if (_cameraButton != null)
|
||||
_cameraButton.Parent.Visible = useMovementSpeed;
|
||||
}
|
||||
|
||||
// Get parent window
|
||||
@@ -1217,18 +1591,8 @@ namespace FlaxEditor.Viewport
|
||||
rmbWheel = useMovementSpeed && (_input.IsMouseRightDown || _isVirtualMouseRightDown) && wheelInUse;
|
||||
if (rmbWheel)
|
||||
{
|
||||
const float step = 4.0f;
|
||||
_wheelMovementChangeDeltaSum += _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity;
|
||||
if (_wheelMovementChangeDeltaSum >= step)
|
||||
{
|
||||
_wheelMovementChangeDeltaSum -= step;
|
||||
AdjustCameraMoveSpeed(1);
|
||||
}
|
||||
else if (_wheelMovementChangeDeltaSum <= -step)
|
||||
{
|
||||
_wheelMovementChangeDeltaSum += step;
|
||||
AdjustCameraMoveSpeed(-1);
|
||||
}
|
||||
var step = _input.MouseWheelDelta * options.Viewport.MouseWheelSensitivity;
|
||||
AdjustCameraMoveSpeed(step > 0.0f ? 1 : -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1497,22 +1861,6 @@ namespace FlaxEditor.Viewport
|
||||
new CameraViewpoint("Bottom", new Float3(-90, 0, 0))
|
||||
};
|
||||
|
||||
private readonly float[] EditorViewportCameraSpeedValues =
|
||||
{
|
||||
0.05f,
|
||||
0.1f,
|
||||
0.25f,
|
||||
0.5f,
|
||||
1.0f,
|
||||
2.0f,
|
||||
4.0f,
|
||||
6.0f,
|
||||
8.0f,
|
||||
16.0f,
|
||||
32.0f,
|
||||
64.0f,
|
||||
};
|
||||
|
||||
private struct ViewModeOptions
|
||||
{
|
||||
public readonly string Name;
|
||||
@@ -1568,24 +1916,6 @@ namespace FlaxEditor.Viewport
|
||||
new ViewModeOptions(ViewMode.GlobalIllumination, "Global Illumination"),
|
||||
};
|
||||
|
||||
private void WidgetCamSpeedShowHide(Control cm)
|
||||
{
|
||||
if (cm.Visible == false)
|
||||
return;
|
||||
|
||||
var ccm = (ContextMenu)cm;
|
||||
foreach (var e in ccm.Items)
|
||||
{
|
||||
if (e is ContextMenuButton b)
|
||||
{
|
||||
var v = (float)b.Tag;
|
||||
b.Icon = Mathf.Abs(MovementSpeed - v) < 0.001f
|
||||
? Style.Current.CheckBoxTick
|
||||
: SpriteHandle.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WidgetViewModeShowHideClicked(ContextMenuButton button)
|
||||
{
|
||||
if (button.Tag is ViewMode v)
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace FlaxEditor.Viewport.Widgets
|
||||
private bool _checked;
|
||||
private bool _autoCheck;
|
||||
private bool _isMosueDown;
|
||||
private float _forcedTextWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when user toggles checked state.
|
||||
@@ -63,14 +64,16 @@ namespace FlaxEditor.Viewport.Widgets
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="icon">The icon.</param>
|
||||
/// <param name="contextMenu">The context menu.</param>
|
||||
/// <param name="autoCheck">if set to <c>true</c> will be automatic checked on mouse click.</param>
|
||||
public ViewportWidgetButton(string text, SpriteHandle icon, ContextMenu contextMenu = null, bool autoCheck = false)
|
||||
: base(0, 0, CalculateButtonWidth(0, icon.IsValid), ViewportWidgetsContainer.WidgetsHeight)
|
||||
/// <param name="autoCheck">If set to <c>true</c> will be automatic checked on mouse click.</param>
|
||||
/// <param name="textWidth">Forces the text to be drawn with the specified width.</param>
|
||||
public ViewportWidgetButton(string text, SpriteHandle icon, ContextMenu contextMenu = null, bool autoCheck = false, float textWidth = 0.0f)
|
||||
: base(0, 0, CalculateButtonWidth(textWidth, icon.IsValid), ViewportWidgetsContainer.WidgetsHeight)
|
||||
{
|
||||
_text = text;
|
||||
Icon = icon;
|
||||
_cm = contextMenu;
|
||||
_autoCheck = autoCheck;
|
||||
_forcedTextWidth = textWidth;
|
||||
|
||||
if (_cm != null)
|
||||
_cm.VisibleChanged += CmOnVisibleChanged;
|
||||
@@ -160,7 +163,7 @@ namespace FlaxEditor.Viewport.Widgets
|
||||
var style = Style.Current;
|
||||
|
||||
if (style != null && style.FontMedium)
|
||||
Width = CalculateButtonWidth(style.FontMedium.MeasureText(_text).X, Icon.IsValid);
|
||||
Width = CalculateButtonWidth(_forcedTextWidth > 0.0f ? _forcedTextWidth : style.FontMedium.MeasureText(_text).X, Icon.IsValid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
// Prefab structure tree
|
||||
Graph = new LocalSceneGraph(new CustomRootNode(this));
|
||||
Graph.Root.TreeNode.Expand(true);
|
||||
_tree = new PrefabTree
|
||||
{
|
||||
Margin = new Margin(0.0f, 0.0f, -16.0f, _treePanel.ScrollBarsSize), // Hide root node
|
||||
@@ -317,7 +318,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
Graph.MainActor = _viewport.Instance;
|
||||
Selection.Clear();
|
||||
Select(Graph.Main);
|
||||
Graph.Root.TreeNode.ExpandAll(true);
|
||||
Graph.Root.TreeNode.Expand(true);
|
||||
_undo.Clear();
|
||||
ClearEditedFlag();
|
||||
}
|
||||
@@ -413,7 +414,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
_focusCamera = true;
|
||||
Selection.Clear();
|
||||
Select(Graph.Main);
|
||||
Graph.Root.TreeNode.ExpandAll(true);
|
||||
Graph.Root.TreeNode.Expand(true);
|
||||
|
||||
_undo.Clear();
|
||||
ClearEditedFlag();
|
||||
|
||||
@@ -369,7 +369,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
|
||||
var pluginPath = Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text);
|
||||
if (Directory.Exists(pluginPath))
|
||||
if (!IsValidModuleName(nameTextBox.Text) || Directory.Exists(pluginPath))
|
||||
{
|
||||
nameTextBox.BorderColor = Color.Red;
|
||||
nameTextBox.BorderSelectedColor = Color.Red;
|
||||
@@ -429,6 +429,12 @@ namespace FlaxEditor.Windows
|
||||
submitButton.Clicked += () =>
|
||||
{
|
||||
// TODO: Check all modules in project including plugins
|
||||
if (!IsValidModuleName(nameTextBox.Text))
|
||||
{
|
||||
Editor.LogWarning("Invalid module name. Module names cannot contain spaces, start with a number or contain non-alphanumeric characters.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text)))
|
||||
{
|
||||
Editor.LogWarning("Cannot create module due to name conflict.");
|
||||
@@ -460,5 +466,16 @@ namespace FlaxEditor.Windows
|
||||
button.ParentContextMenu.Hide();
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsValidModuleName(string text)
|
||||
{
|
||||
if (text.Contains(' '))
|
||||
return false;
|
||||
if (char.IsDigit(text[0]))
|
||||
return false;
|
||||
if (text.Any(c => !char.IsLetterOrDigit(c) && c != '_'))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,30 +155,43 @@ namespace FlaxEditor.Windows
|
||||
|
||||
public virtual void OnNotAvailableLayout(LayoutElementsContainer layout)
|
||||
{
|
||||
layout.Label("Missing platform data tools for the target platform.", TextAlignment.Center);
|
||||
string text = "Missing platform data tools for the target platform.";
|
||||
if (FlaxEditor.Editor.IsOfficialBuild())
|
||||
{
|
||||
switch (BuildPlatform)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
case BuildPlatform.Windows32:
|
||||
case BuildPlatform.Windows64:
|
||||
case BuildPlatform.UWPx86:
|
||||
case BuildPlatform.UWPx64:
|
||||
case BuildPlatform.LinuxX64:
|
||||
case BuildPlatform.AndroidARM64:
|
||||
layout.Label("Use Flax Launcher and download the required package.", TextAlignment.Center);
|
||||
text += "\nUse Flax Launcher and download the required package.";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
layout.Label("Engine source is required to target this platform.", TextAlignment.Center);
|
||||
text += "\nEngine source is required to target this platform.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var label = layout.Label("To target this platform separate engine source package is required.\nTo get access please contact via https://flaxengine.com/contact", TextAlignment.Center);
|
||||
label.Label.AutoHeight = true;
|
||||
text += "\nTo target this platform separate engine source package is required.";
|
||||
switch (BuildPlatform)
|
||||
{
|
||||
case BuildPlatform.XboxOne:
|
||||
case BuildPlatform.XboxScarlett:
|
||||
case BuildPlatform.PS4:
|
||||
case BuildPlatform.PS5:
|
||||
case BuildPlatform.Switch:
|
||||
text += "\nTo get access please contact via https://flaxengine.com/contact";
|
||||
break;
|
||||
}
|
||||
}
|
||||
var label = layout.Label(text, TextAlignment.Center);
|
||||
label.Label.AutoHeight = true;
|
||||
}
|
||||
|
||||
public virtual void Build()
|
||||
{
|
||||
|
||||
@@ -82,7 +82,6 @@ public:
|
||||
|
||||
private:
|
||||
int32 _elementsCount = 0;
|
||||
int32 _deletedCount = 0;
|
||||
int32 _size = 0;
|
||||
AllocationData _allocation;
|
||||
|
||||
@@ -109,14 +108,11 @@ public:
|
||||
/// <param name="other">The other collection to move.</param>
|
||||
HashSet(HashSet&& other) noexcept
|
||||
: _elementsCount(other._elementsCount)
|
||||
, _deletedCount(other._deletedCount)
|
||||
, _size(other._size)
|
||||
{
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
_size = other._size;
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
@@ -154,10 +150,8 @@ public:
|
||||
Clear();
|
||||
_allocation.Free();
|
||||
_elementsCount = other._elementsCount;
|
||||
_deletedCount = other._deletedCount;
|
||||
_size = other._size;
|
||||
other._elementsCount = 0;
|
||||
other._deletedCount = 0;
|
||||
other._size = 0;
|
||||
_allocation.Swap(other._allocation);
|
||||
}
|
||||
@@ -337,12 +331,12 @@ public:
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
if (_elementsCount + _deletedCount != 0)
|
||||
if (_elementsCount != 0)
|
||||
{
|
||||
Bucket* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; i++)
|
||||
data[i].Free();
|
||||
_elementsCount = _deletedCount = 0;
|
||||
_elementsCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +371,7 @@ public:
|
||||
oldAllocation.Swap(_allocation);
|
||||
const int32 oldSize = _size;
|
||||
const int32 oldElementsCount = _elementsCount;
|
||||
_deletedCount = _elementsCount = 0;
|
||||
_elementsCount = 0;
|
||||
if (capacity != 0 && (capacity & (capacity - 1)) != 0)
|
||||
{
|
||||
// Align capacity value to the next power of two (http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2)
|
||||
@@ -439,7 +433,7 @@ public:
|
||||
bool Add(const ItemType& item)
|
||||
{
|
||||
// Ensure to have enough memory for the next item (in case of new element insertion)
|
||||
EnsureCapacity(_elementsCount + _deletedCount + 1);
|
||||
EnsureCapacity(_elementsCount + 1);
|
||||
|
||||
// Find location of the item or place to insert it
|
||||
FindPositionResult pos;
|
||||
@@ -485,7 +479,6 @@ public:
|
||||
{
|
||||
_allocation.Get()[pos.ObjectIndex].Delete();
|
||||
_elementsCount--;
|
||||
_deletedCount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -504,7 +497,6 @@ public:
|
||||
ASSERT(_allocation.Get()[i._index].IsOccupied());
|
||||
_allocation.Get()[i._index].Delete();
|
||||
_elementsCount--;
|
||||
_deletedCount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -511,7 +511,7 @@ public:
|
||||
_locker = New<CriticalSection>();
|
||||
ScopeLock lock(*_locker);
|
||||
if (_functions == nullptr)
|
||||
_functions = New<HashSet<FunctionType>>(32);
|
||||
_functions = New<HashSet<FunctionType>>();
|
||||
_functions->Add(f);
|
||||
#endif
|
||||
}
|
||||
@@ -583,18 +583,9 @@ public:
|
||||
template<void(*Method)(Params...)>
|
||||
void Unbind()
|
||||
{
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
FunctionType f;
|
||||
f.template Bind<Method>();
|
||||
Unbind(f);
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return;
|
||||
FunctionType f;
|
||||
f.template Bind<Method>();
|
||||
ScopeLock lock(*_locker);
|
||||
_functions->Remove(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -604,18 +595,9 @@ public:
|
||||
template<class T, void(T::*Method)(Params...)>
|
||||
void Unbind(T* callee)
|
||||
{
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
FunctionType f;
|
||||
f.template Bind<T, Method>(callee);
|
||||
Unbind(f);
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return;
|
||||
FunctionType f;
|
||||
f.template Bind<T, Method>(callee);
|
||||
ScopeLock lock(*_locker);
|
||||
_functions->Remove(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -624,16 +606,8 @@ public:
|
||||
/// <param name="method">The method.</param>
|
||||
void Unbind(Signature method)
|
||||
{
|
||||
#if DELEGATE_USE_ATOMIC
|
||||
FunctionType f(method);
|
||||
Unbind(f);
|
||||
#else
|
||||
if (_functions == nullptr)
|
||||
return;
|
||||
FunctionType f(method);
|
||||
ScopeLock lock(*_locker);
|
||||
_functions->Remove(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,9 +13,7 @@ namespace FlaxEngine.TypeConverters
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
@@ -23,9 +21,7 @@ namespace FlaxEngine.TypeConverters
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Double2Converter : TypeConverter
|
||||
internal class Double2Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Double2(double.Parse(v[0], culture), double.Parse(v[1], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Double3Converter : TypeConverter
|
||||
internal class Double3Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Double3(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Double4Converter : TypeConverter
|
||||
internal class Double4Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Double4(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture), double.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Float2Converter : TypeConverter
|
||||
internal class Float2Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Float2(float.Parse(v[0], culture), float.Parse(v[1], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Float3Converter : TypeConverter
|
||||
internal class Float3Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Float3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,15 +7,13 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Float4Converter : TypeConverter
|
||||
internal class VectorConverter : TypeConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
@@ -23,18 +21,32 @@ namespace FlaxEngine.TypeConverters
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
internal static string[] GetParts(string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
if (v.Length == 1)
|
||||
{
|
||||
// When converting from ToString()
|
||||
v = str.Split(' ');
|
||||
for (int i = 0; i < v.Length; i++)
|
||||
v[i] = v[i].Substring(v[i].IndexOf(':') + 1);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Float4Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Float4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Int2Converter : TypeConverter
|
||||
internal class Int2Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Int2(int.Parse(v[0], culture), int.Parse(v[1], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Int3Converter : TypeConverter
|
||||
internal class Int3Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Int3(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Int4Converter : TypeConverter
|
||||
internal class Int4Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Int4(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture), int.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class QuaternionConverter : TypeConverter
|
||||
internal class QuaternionConverter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Quaternion(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Vector2Converter : TypeConverter
|
||||
internal class Vector2Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Vector2(float.Parse(v[0], culture), float.Parse(v[1], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Vector3Converter : TypeConverter
|
||||
internal class Vector3Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Vector3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -7,34 +7,14 @@ using System.Globalization;
|
||||
|
||||
namespace FlaxEngine.TypeConverters
|
||||
{
|
||||
internal class Vector4Converter : TypeConverter
|
||||
internal class Vector4Converter : VectorConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
string[] v = str.Split(',');
|
||||
string[] v = GetParts(str);
|
||||
return new Vector4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -646,6 +646,12 @@ inline Vector2Base<T> operator/(typename TOtherFloat<T>::Type a, const Vector2Ba
|
||||
return Vector2Base<T>(a) / b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline uint32 GetHash(const Vector2Base<T>& key)
|
||||
{
|
||||
return (*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y;
|
||||
}
|
||||
|
||||
namespace Math
|
||||
{
|
||||
template<typename T>
|
||||
|
||||
@@ -977,6 +977,12 @@ inline Vector3Base<T> operator/(typename TOtherFloat<T>::Type a, const Vector3Ba
|
||||
return Vector3Base<T>(a) / b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline uint32 GetHash(const Vector3Base<T>& key)
|
||||
{
|
||||
return (((*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y) * 397) ^ *(uint32*)&key.Z;
|
||||
}
|
||||
|
||||
namespace Math
|
||||
{
|
||||
template<typename T>
|
||||
|
||||
@@ -552,6 +552,12 @@ inline Vector4Base<T> operator/(typename TOtherFloat<T>::Type a, const Vector4Ba
|
||||
return Vector4Base<T>(a) / b;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline uint32 GetHash(const Vector4Base<T>& key)
|
||||
{
|
||||
return (((((*(uint32*)&key.X * 397) ^ *(uint32*)&key.Y) * 397) ^ *(uint32*)&key.Z) * 397) ^*(uint32*)&key.W;
|
||||
}
|
||||
|
||||
namespace Math
|
||||
{
|
||||
template<typename T>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Collections/Dictionary.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Scripting/ScriptingObject.h"
|
||||
|
||||
|
||||
@@ -7,15 +7,15 @@ Version::Version(int32 major, int32 minor, int32 build, int32 revision)
|
||||
{
|
||||
_major = Math::Max(major, 0);
|
||||
_minor = Math::Max(minor, 0);
|
||||
_build = Math::Max(build, 0);
|
||||
_revision = Math::Max(revision, 0);
|
||||
_build = Math::Max(build, -1);
|
||||
_revision = Math::Max(revision, -1);
|
||||
}
|
||||
|
||||
Version::Version(int32 major, int32 minor, int32 build)
|
||||
{
|
||||
_major = Math::Max(major, 0);
|
||||
_minor = Math::Max(minor, 0);
|
||||
_build = Math::Max(build, 0);
|
||||
_build = Math::Max(build, -1);
|
||||
_revision = -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,10 +51,14 @@ namespace Utilities
|
||||
int32 i = 0;
|
||||
double dblSUnits = static_cast<double>(units);
|
||||
for (; static_cast<uint64>(units / static_cast<double>(divider)) > 0; i++, units /= divider)
|
||||
dblSUnits = units / static_cast<double>(divider);
|
||||
dblSUnits = (double)units / (double)divider;
|
||||
if (i >= sizes.Length())
|
||||
i = 0;
|
||||
return String::Format(TEXT("{0} {1}"), RoundTo2DecimalPlaces(dblSUnits), sizes[i]);
|
||||
String text = String::Format(TEXT("{}"), RoundTo2DecimalPlaces(dblSUnits));
|
||||
const int32 dot = text.FindLast('.');
|
||||
if (dot != -1)
|
||||
text = text.Left(dot + 3);
|
||||
return String::Format(TEXT("{0} {1}"), text, sizes[i]);
|
||||
}
|
||||
|
||||
// Converts size of the file (in bytes) to the best fitting string
|
||||
|
||||
@@ -1302,7 +1302,8 @@ namespace FlaxEngine.Interop
|
||||
#if !USE_AOT
|
||||
internal bool TryGetDelegate(out Invoker.MarshalAndInvokeDelegate outDeleg, out object outDelegInvoke)
|
||||
{
|
||||
if (invokeDelegate == null)
|
||||
// Skip using in-built delegate for value types (eg. Transform) to properly handle instance value passing to method
|
||||
if (invokeDelegate == null && !method.DeclaringType.IsValueType)
|
||||
{
|
||||
List<Type> methodTypes = new List<Type>();
|
||||
if (!method.IsStatic)
|
||||
|
||||
@@ -313,6 +313,8 @@ bool GPUDevice::Init()
|
||||
|
||||
_res->TasksManager.SetExecutor(CreateTasksExecutor());
|
||||
LOG(Info, "Total graphics memory: {0}", Utilities::BytesToText(TotalGraphicsMemory));
|
||||
if (!Limits.HasCompute)
|
||||
LOG(Warning, "Compute Shaders are not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,11 @@ bool GPUShader::Create(MemoryReadStream& stream)
|
||||
GPUShaderProgramInitializer initializer;
|
||||
#if !BUILD_RELEASE
|
||||
initializer.Owner = this;
|
||||
const StringView name = GetName();
|
||||
#else
|
||||
const StringView name;
|
||||
#endif
|
||||
const bool hasCompute = GPUDevice::Instance->Limits.HasCompute;
|
||||
for (int32 i = 0; i < shadersCount; i++)
|
||||
{
|
||||
const ShaderStage type = static_cast<ShaderStage>(stream.ReadByte());
|
||||
@@ -117,10 +121,15 @@ bool GPUShader::Create(MemoryReadStream& stream)
|
||||
stream.ReadBytes(&initializer.Bindings, sizeof(ShaderBindings));
|
||||
|
||||
// Create shader program
|
||||
if (type == ShaderStage::Compute && !hasCompute)
|
||||
{
|
||||
LOG(Warning, "Failed to create {} Shader program '{}' ({}).", ::ToString(type), String(initializer.Name), name);
|
||||
continue;
|
||||
}
|
||||
GPUShaderProgram* shader = CreateGPUShaderProgram(type, initializer, cache, cacheSize, stream);
|
||||
if (shader == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to create {} Shader program '{}'.", ::ToString(type), String(initializer.Name));
|
||||
LOG(Error, "Failed to create {} Shader program '{}' ({}).", ::ToString(type), String(initializer.Name), name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,19 @@ public:
|
||||
/// </summary>
|
||||
class GPUShaderProgramVS : public GPUShaderProgram
|
||||
{
|
||||
public:
|
||||
// Input element run-time data (see VertexShaderMeta::InputElement for compile-time data)
|
||||
PACK_STRUCT(struct InputElement
|
||||
{
|
||||
byte Type; // VertexShaderMeta::InputType
|
||||
byte Index;
|
||||
byte Format; // PixelFormat
|
||||
byte InputSlot;
|
||||
uint32 AlignedByteOffset; // Fixed value or INPUT_LAYOUT_ELEMENT_ALIGN if auto
|
||||
byte InputSlotClass; // INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA or INPUT_LAYOUT_ELEMENT_PER_INSTANCE_DATA
|
||||
uint32 InstanceDataStepRate; // 0 if per-vertex
|
||||
});
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets input layout description handle (platform dependent).
|
||||
|
||||
@@ -15,32 +15,21 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
{
|
||||
D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
|
||||
// Temporary variables
|
||||
byte Type, Format, Index, InputSlot, InputSlotClass;
|
||||
uint32 AlignedByteOffset, InstanceDataStepRate;
|
||||
|
||||
// Load Input Layout (it may be empty)
|
||||
// Load Input Layout
|
||||
byte inputLayoutSize;
|
||||
stream.ReadByte(&inputLayoutSize);
|
||||
ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS);
|
||||
D3D11_INPUT_ELEMENT_DESC inputLayoutDesc[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
for (int32 a = 0; a < inputLayoutSize; a++)
|
||||
{
|
||||
// Read description
|
||||
// TODO: maybe use struct and load at once?
|
||||
stream.ReadByte(&Type);
|
||||
stream.ReadByte(&Index);
|
||||
stream.ReadByte(&Format);
|
||||
stream.ReadByte(&InputSlot);
|
||||
stream.ReadUint32(&AlignedByteOffset);
|
||||
stream.ReadByte(&InputSlotClass);
|
||||
stream.ReadUint32(&InstanceDataStepRate);
|
||||
GPUShaderProgramVS::InputElement inputElement;
|
||||
stream.Read(inputElement);
|
||||
|
||||
// Get semantic name
|
||||
const char* semanticName = nullptr;
|
||||
// TODO: maybe use enum+mapping ?
|
||||
switch (Type)
|
||||
switch (inputElement.Type)
|
||||
{
|
||||
case 1:
|
||||
semanticName = "POSITION";
|
||||
@@ -70,7 +59,7 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
|
||||
semanticName = "BLENDWEIGHT";
|
||||
break;
|
||||
default:
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type);
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", inputElement.Type);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -78,12 +67,12 @@ GPUShaderProgram* GPUShaderDX11::CreateGPUShaderProgram(ShaderStage type, const
|
||||
inputLayoutDesc[a] =
|
||||
{
|
||||
semanticName,
|
||||
static_cast<UINT>(Index),
|
||||
static_cast<DXGI_FORMAT>(Format),
|
||||
static_cast<UINT>(InputSlot),
|
||||
static_cast<UINT>(AlignedByteOffset),
|
||||
static_cast<D3D11_INPUT_CLASSIFICATION>(InputSlotClass),
|
||||
static_cast<UINT>(InstanceDataStepRate)
|
||||
static_cast<UINT>(inputElement.Index),
|
||||
static_cast<DXGI_FORMAT>(inputElement.Format),
|
||||
static_cast<UINT>(inputElement.InputSlot),
|
||||
static_cast<UINT>(inputElement.AlignedByteOffset),
|
||||
static_cast<D3D11_INPUT_CLASSIFICATION>(inputElement.InputSlotClass),
|
||||
static_cast<UINT>(inputElement.InstanceDataStepRate)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -20,32 +20,21 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
{
|
||||
D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
|
||||
// Temporary variables
|
||||
byte Type, Format, Index, InputSlot, InputSlotClass;
|
||||
uint32 AlignedByteOffset, InstanceDataStepRate;
|
||||
|
||||
// Load Input Layout (it may be empty)
|
||||
byte inputLayoutSize;
|
||||
stream.ReadByte(&inputLayoutSize);
|
||||
ASSERT(inputLayoutSize <= VERTEX_SHADER_MAX_INPUT_ELEMENTS);
|
||||
D3D12_INPUT_ELEMENT_DESC inputLayout[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
for (int32 a = 0; a < inputLayoutSize; a++)
|
||||
{
|
||||
// Read description
|
||||
// TODO: maybe use struct and load at once?
|
||||
stream.ReadByte(&Type);
|
||||
stream.ReadByte(&Index);
|
||||
stream.ReadByte(&Format);
|
||||
stream.ReadByte(&InputSlot);
|
||||
stream.ReadUint32(&AlignedByteOffset);
|
||||
stream.ReadByte(&InputSlotClass);
|
||||
stream.ReadUint32(&InstanceDataStepRate);
|
||||
GPUShaderProgramVS::InputElement inputElement;
|
||||
stream.Read(inputElement);
|
||||
|
||||
// Get semantic name
|
||||
const char* semanticName = nullptr;
|
||||
// TODO: maybe use enum+mapping ?
|
||||
switch (Type)
|
||||
switch (inputElement.Type)
|
||||
{
|
||||
case 1:
|
||||
semanticName = "POSITION";
|
||||
@@ -75,7 +64,7 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
|
||||
semanticName = "BLENDWEIGHT";
|
||||
break;
|
||||
default:
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", Type);
|
||||
LOG(Fatal, "Invalid vertex shader element semantic type: {0}", inputElement.Type);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -83,12 +72,12 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const
|
||||
inputLayout[a] =
|
||||
{
|
||||
semanticName,
|
||||
static_cast<UINT>(Index),
|
||||
static_cast<DXGI_FORMAT>(Format),
|
||||
static_cast<UINT>(InputSlot),
|
||||
static_cast<UINT>(AlignedByteOffset),
|
||||
static_cast<D3D12_INPUT_CLASSIFICATION>(InputSlotClass),
|
||||
static_cast<UINT>(InstanceDataStepRate)
|
||||
static_cast<UINT>(inputElement.Index),
|
||||
static_cast<DXGI_FORMAT>(inputElement.Format),
|
||||
static_cast<UINT>(inputElement.InputSlot),
|
||||
static_cast<UINT>(inputElement.AlignedByteOffset),
|
||||
static_cast<D3D12_INPUT_CLASSIFICATION>(inputElement.InputSlotClass),
|
||||
static_cast<UINT>(inputElement.InstanceDataStepRate)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
|
||||
#if PLATFORM_DESKTOP
|
||||
#define VULKAN_UNIFORM_RING_BUFFER_SIZE 24 * 1024 * 1024
|
||||
#define VULKAN_UNIFORM_RING_BUFFER_SIZE (24 * 1024 * 1024)
|
||||
#else
|
||||
#define VULKAN_UNIFORM_RING_BUFFER_SIZE 8 * 1024 * 1024
|
||||
#define VULKAN_UNIFORM_RING_BUFFER_SIZE (8 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
UniformBufferUploaderVulkan::UniformBufferUploaderVulkan(GPUDeviceVulkan* device)
|
||||
@@ -153,10 +153,6 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
|
||||
vertexBindingDescriptions[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
}
|
||||
|
||||
// Temporary variables
|
||||
byte Type, Format, Index, InputSlot, InputSlotClass;
|
||||
uint32 AlignedByteOffset, InstanceDataStepRate;
|
||||
|
||||
// Load Input Layout (it may be empty)
|
||||
byte inputLayoutSize;
|
||||
stream.ReadByte(&inputLayoutSize);
|
||||
@@ -167,32 +163,26 @@ GPUShaderProgram* GPUShaderVulkan::CreateGPUShaderProgram(ShaderStage type, cons
|
||||
for (int32 a = 0; a < inputLayoutSize; a++)
|
||||
{
|
||||
// Read description
|
||||
// TODO: maybe use struct and load at once?
|
||||
stream.ReadByte(&Type);
|
||||
stream.ReadByte(&Index);
|
||||
stream.ReadByte(&Format);
|
||||
stream.ReadByte(&InputSlot);
|
||||
stream.ReadUint32(&AlignedByteOffset);
|
||||
stream.ReadByte(&InputSlotClass);
|
||||
stream.ReadUint32(&InstanceDataStepRate);
|
||||
GPUShaderProgramVS::InputElement inputElement;
|
||||
stream.Read(inputElement);
|
||||
|
||||
const auto size = PixelFormatExtensions::SizeInBytes((PixelFormat)Format);
|
||||
if (AlignedByteOffset != INPUT_LAYOUT_ELEMENT_ALIGN)
|
||||
offset = AlignedByteOffset;
|
||||
const auto size = PixelFormatExtensions::SizeInBytes((PixelFormat)inputElement.Format);
|
||||
if (inputElement.AlignedByteOffset != INPUT_LAYOUT_ELEMENT_ALIGN)
|
||||
offset = inputElement.AlignedByteOffset;
|
||||
|
||||
auto& vertexBindingDescription = vertexBindingDescriptions[InputSlot];
|
||||
vertexBindingDescription.binding = InputSlot;
|
||||
auto& vertexBindingDescription = vertexBindingDescriptions[inputElement.InputSlot];
|
||||
vertexBindingDescription.binding = inputElement.InputSlot;
|
||||
vertexBindingDescription.stride = Math::Max(vertexBindingDescription.stride, (uint32_t)(offset + size));
|
||||
vertexBindingDescription.inputRate = InputSlotClass == INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||
ASSERT(InstanceDataStepRate == 0 || InstanceDataStepRate == 1);
|
||||
vertexBindingDescription.inputRate = inputElement.InputSlotClass == INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||
ASSERT(inputElement.InstanceDataStepRate == 0 || inputElement.InstanceDataStepRate == 1);
|
||||
|
||||
auto& vertexAttributeDescription = vertexAttributeDescriptions[a];
|
||||
vertexAttributeDescription.location = a;
|
||||
vertexAttributeDescription.binding = InputSlot;
|
||||
vertexAttributeDescription.format = RenderToolsVulkan::ToVulkanFormat((PixelFormat)Format);
|
||||
vertexAttributeDescription.binding = inputElement.InputSlot;
|
||||
vertexAttributeDescription.format = RenderToolsVulkan::ToVulkanFormat((PixelFormat)inputElement.Format);
|
||||
vertexAttributeDescription.offset = offset;
|
||||
|
||||
bindingsCount = Math::Max(bindingsCount, (uint32)InputSlot + 1);
|
||||
bindingsCount = Math::Max(bindingsCount, (uint32)inputElement.InputSlot + 1);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
|
||||
@@ -416,7 +416,7 @@ public:
|
||||
}
|
||||
|
||||
// Load scene
|
||||
if (Level::loadScene(SceneAsset.Get()))
|
||||
if (Level::loadScene(SceneAsset))
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene {0}", SceneId);
|
||||
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, SceneId);
|
||||
@@ -816,40 +816,10 @@ bool LevelImpl::unloadScenes()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Level::loadScene(const Guid& sceneId)
|
||||
{
|
||||
const auto sceneAsset = Content::LoadAsync<JsonAsset>(sceneId);
|
||||
return loadScene(sceneAsset);
|
||||
}
|
||||
|
||||
bool Level::loadScene(const String& scenePath)
|
||||
{
|
||||
LOG(Info, "Loading scene from file. Path: \'{0}\'", scenePath);
|
||||
|
||||
// Check for missing file
|
||||
if (!FileSystem::FileExists(scenePath))
|
||||
{
|
||||
LOG(Error, "Missing scene file.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load file
|
||||
BytesContainer sceneData;
|
||||
if (File::ReadAllBytes(scenePath, sceneData))
|
||||
{
|
||||
LOG(Error, "Cannot load data from file.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return loadScene(sceneData);
|
||||
}
|
||||
|
||||
bool Level::loadScene(JsonAsset* sceneAsset)
|
||||
{
|
||||
// Keep reference to the asset (prevent unloading during action)
|
||||
AssetReference<JsonAsset> ref = sceneAsset;
|
||||
|
||||
// Wait for loaded
|
||||
if (sceneAsset == nullptr || sceneAsset->WaitForLoaded())
|
||||
{
|
||||
LOG(Error, "Cannot load scene asset.");
|
||||
@@ -879,6 +849,7 @@ bool Level::loadScene(const BytesContainer& sceneData, Scene** outScene)
|
||||
return true;
|
||||
}
|
||||
|
||||
ScopeLock lock(ScenesLock);
|
||||
return loadScene(document, outScene);
|
||||
}
|
||||
|
||||
@@ -975,6 +946,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
SceneObject** objects = sceneObjects->Get();
|
||||
if (context.Async)
|
||||
{
|
||||
ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize)
|
||||
JobSystem::Execute([&](int32 i)
|
||||
{
|
||||
i++; // Start from 1. at index [0] was scene
|
||||
@@ -992,6 +964,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
else
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||
}, objectsCount - 1);
|
||||
ScenesLock.Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1330,6 +1303,7 @@ bool Level::LoadScene(const Guid& id)
|
||||
}
|
||||
|
||||
// Load scene
|
||||
ScopeLock lock(ScenesLock);
|
||||
if (loadScene(sceneAsset))
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene {0}", id);
|
||||
|
||||
@@ -541,8 +541,8 @@ private:
|
||||
};
|
||||
|
||||
static void callActorEvent(ActorEventType eventType, Actor* a, Actor* b);
|
||||
static bool loadScene(const Guid& sceneId);
|
||||
static bool loadScene(const String& scenePath);
|
||||
|
||||
// All loadScene assume that ScenesLock has been taken by the calling thread
|
||||
static bool loadScene(JsonAsset* sceneAsset);
|
||||
static bool loadScene(const BytesContainer& sceneData, Scene** outScene = nullptr);
|
||||
static bool loadScene(rapidjson_flax::Document& document, Scene** outScene = nullptr);
|
||||
|
||||
@@ -39,7 +39,7 @@ PrefabManagerService PrefabManagerServiceInstance;
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab)
|
||||
{
|
||||
Actor* parent = Level::Scenes.Count() != 0 ? Level::Scenes.Get()[0] : nullptr;
|
||||
return SpawnPrefab(prefab, Transform::Identity, parent, nullptr);
|
||||
return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, nullptr);
|
||||
}
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Vector3& position)
|
||||
@@ -73,12 +73,12 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, const Transform
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent)
|
||||
{
|
||||
return SpawnPrefab(prefab, Transform::Identity, parent, nullptr);
|
||||
return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, nullptr);
|
||||
}
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid, const void*>* objectsCache, bool withSynchronization)
|
||||
{
|
||||
return SpawnPrefab(prefab, Transform::Identity, parent, objectsCache, withSynchronization);
|
||||
return SpawnPrefab(prefab, Transform(Vector3::Minimum), parent, objectsCache, withSynchronization);
|
||||
}
|
||||
|
||||
Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Actor* parent, Dictionary<Guid, const void*>* objectsCache, bool withSynchronization)
|
||||
@@ -191,7 +191,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, const Transform& transform, Ac
|
||||
parent->Children.Add(root);
|
||||
|
||||
// Move root to the right location
|
||||
if (transform != Transform::Identity)
|
||||
if (transform.Translation != Vector3::Minimum)
|
||||
root->SetTransform(transform);
|
||||
|
||||
// Link actors hierarchy
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "Engine/Threading/ThreadLocal.h"
|
||||
#if !BUILD_RELEASE || USE_EDITOR
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#endif
|
||||
|
||||
SceneObjectsFactory::Context::Context(ISerializeModifier* modifier)
|
||||
@@ -279,9 +280,9 @@ void SceneObjectsFactory::HandleObjectDeserializationError(const ISerializable::
|
||||
#if USE_EDITOR
|
||||
// Add dummy script
|
||||
auto* dummyScript = parent->AddScript<MissingScript>();
|
||||
const auto parentIdMember = value.FindMember("TypeName");
|
||||
if (parentIdMember != value.MemberEnd() && parentIdMember->value.IsString())
|
||||
dummyScript->MissingTypeName = parentIdMember->value.GetString();
|
||||
const auto typeNameMember = value.FindMember("TypeName");
|
||||
if (typeNameMember != value.MemberEnd() && typeNameMember->value.IsString())
|
||||
dummyScript->MissingTypeName = typeNameMember->value.GetString();
|
||||
dummyScript->Data = MoveTemp(bufferStr);
|
||||
#endif
|
||||
LOG(Warning, "Parent actor of the missing object: {0}", parent->GetName());
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include <ThirdParty/recastnavigation/DetourCrowd.h>
|
||||
|
||||
NavCrowd::NavCrowd(const SpawnParams& params)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/// <summary>
|
||||
/// Current GPU particles emitter shader version.
|
||||
/// </summary>
|
||||
#define PARTICLE_GPU_GRAPH_VERSION 9
|
||||
#define PARTICLE_GPU_GRAPH_VERSION 10
|
||||
|
||||
#if COMPILE_WITH_PARTICLE_GPU_GRAPH
|
||||
|
||||
|
||||
@@ -284,6 +284,7 @@ void ParticleEffect::UpdateSimulation(bool singleFrame)
|
||||
void ParticleEffect::Play()
|
||||
{
|
||||
_isPlaying = true;
|
||||
_isStopped = false;
|
||||
}
|
||||
|
||||
void ParticleEffect::Pause()
|
||||
@@ -293,6 +294,7 @@ void ParticleEffect::Pause()
|
||||
|
||||
void ParticleEffect::Stop()
|
||||
{
|
||||
_isStopped = true;
|
||||
_isPlaying = false;
|
||||
ResetSimulation();
|
||||
}
|
||||
@@ -448,7 +450,7 @@ void ParticleEffect::Update()
|
||||
void ParticleEffect::UpdateExecuteInEditor()
|
||||
{
|
||||
// Auto-play in Editor
|
||||
if (!Editor::IsPlayMode)
|
||||
if (!Editor::IsPlayMode && !_isStopped)
|
||||
{
|
||||
_isPlaying = true;
|
||||
Update();
|
||||
|
||||
@@ -133,7 +133,7 @@ public:
|
||||
/// <summary>
|
||||
/// The particle system instance that plays the particles simulation in the game.
|
||||
/// </summary>
|
||||
API_CLASS(Attributes="ActorContextMenu(\"New/Visuals/Particle Effects\"), ActorToolbox(\"Visuals\")")
|
||||
API_CLASS(Attributes="ActorContextMenu(\"New/Visuals/Particle Effect\"), ActorToolbox(\"Visuals\")")
|
||||
class FLAXENGINE_API ParticleEffect : public Actor
|
||||
{
|
||||
DECLARE_SCENE_OBJECT(ParticleEffect);
|
||||
@@ -185,6 +185,7 @@ private:
|
||||
Array<ParticleEffectParameter> _parameters; // Cached for scripting API
|
||||
Array<ParameterOverride> _parametersOverrides; // Cached parameter modifications to be applied to the parameters
|
||||
bool _isPlaying = false;
|
||||
bool _isStopped = false;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -1318,6 +1318,8 @@ void ParticlesSystem::Job(int32 index)
|
||||
emitterInstance.Buffer = nullptr;
|
||||
}
|
||||
}
|
||||
// Stop playing effect.
|
||||
effect->Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1332,7 +1334,8 @@ void ParticlesSystem::Job(int32 index)
|
||||
auto emitter = particleSystem->Emitters[track.AsEmitter.Index].Get();
|
||||
auto& data = instance.Emitters[track.AsEmitter.Index];
|
||||
ASSERT(emitter && emitter->IsLoaded());
|
||||
ASSERT(emitter->Capacity != 0 && emitter->Graph.Layout.Size != 0);
|
||||
if (emitter->Capacity == 0 || emitter->Graph.Layout.Size == 0)
|
||||
continue;
|
||||
PROFILE_CPU_ASSET(emitter);
|
||||
|
||||
// Calculate new time position
|
||||
|
||||
@@ -95,14 +95,17 @@ void Cloth::SetFabric(const FabricSettings& value)
|
||||
void Cloth::Rebuild()
|
||||
{
|
||||
#if WITH_CLOTH
|
||||
if (_cloth)
|
||||
{
|
||||
// Remove old
|
||||
if (IsDuringPlay())
|
||||
PhysicsBackend::RemoveCloth(GetPhysicsScene()->GetPhysicsScene(), _cloth);
|
||||
DestroyCloth();
|
||||
}
|
||||
|
||||
// Create new
|
||||
CreateCloth();
|
||||
if (IsDuringPlay())
|
||||
if (IsDuringPlay() && _cloth)
|
||||
PhysicsBackend::AddCloth(GetPhysicsScene()->GetPhysicsScene(), _cloth);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -232,13 +232,13 @@ private:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the mesh to use for the cloth simulation (single mesh from specific LOD).
|
||||
/// Gets the mesh to use for the cloth simulation (single mesh from specific LOD). Always from the parent static or animated model actor.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(0), EditorDisplay(\"Cloth\")")
|
||||
ModelInstanceActor::MeshReference GetMesh() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the mesh to use for the cloth simulation (single mesh from specific LOD).
|
||||
/// Sets the mesh to use for the cloth simulation (single mesh from specific LOD). Always from the parent static or animated model actor.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetMesh(const ModelInstanceActor::MeshReference& value);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "PhysicsColliderActor.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
#include "RigidBody.h"
|
||||
|
||||
PhysicsColliderActor::PhysicsColliderActor(const SpawnParams& params)
|
||||
|
||||
@@ -117,9 +117,7 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c
|
||||
{
|
||||
// Skip sending events to removed actors
|
||||
if (pairHeader.flags & (PxContactPairHeaderFlag::eREMOVED_ACTOR_0 | PxContactPairHeaderFlag::eREMOVED_ACTOR_1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Collision c;
|
||||
PxContactPairExtraDataIterator j(pairHeader.extraDataStream, pairHeader.extraDataStreamSize);
|
||||
@@ -193,7 +191,7 @@ void SimulationEventCallback::onContact(const PxContactPairHeader& pairHeader, c
|
||||
RemovedCollisions.Add(c);
|
||||
}
|
||||
}
|
||||
ASSERT(!j.nextItemSet());
|
||||
//ASSERT(!j.nextItemSet());
|
||||
}
|
||||
|
||||
void SimulationEventCallback::onTrigger(PxTriggerPair* pairs, PxU32 count)
|
||||
|
||||
@@ -48,6 +48,7 @@ CPUInfo Cpu;
|
||||
String UserLocale;
|
||||
double SecondsPerCycle;
|
||||
NSAutoreleasePool* AutoreleasePool = nullptr;
|
||||
int32 AutoreleasePoolInterval = 0;
|
||||
|
||||
float ApplePlatform::ScreenScale = 1.0f;
|
||||
|
||||
@@ -245,13 +246,40 @@ void ApplePlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int3
|
||||
millisecond = time.tv_usec / 1000;
|
||||
}
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
|
||||
void ApplePlatform::Log(const StringView& msg)
|
||||
{
|
||||
#if !BUILD_RELEASE && !USE_EDITOR
|
||||
#if !USE_EDITOR
|
||||
NSLog(@"%s", StringAsANSI<>(*msg, msg.Length()).Get());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ApplePlatform::IsDebuggerPresent()
|
||||
{
|
||||
// Reference: https://developer.apple.com/library/archive/qa/qa1361/_index.html
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
|
||||
// Initialize the flags so that, if sysctl fails for some bizarre reason, we get a predictable result
|
||||
info.kp_proc.p_flag = 0;
|
||||
|
||||
// Initialize mib, which tells sysctl the info we want, in this case we're looking for information about a specific process ID
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
|
||||
// Call sysctl
|
||||
size_t size = sizeof(info);
|
||||
sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
|
||||
|
||||
// We're being debugged if the P_TRACED flag is set
|
||||
return ((info.kp_proc.p_flag & P_TRACED) != 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool ApplePlatform::Init()
|
||||
{
|
||||
if (UnixPlatform::Init())
|
||||
@@ -332,9 +360,13 @@ bool ApplePlatform::Init()
|
||||
|
||||
void ApplePlatform::Tick()
|
||||
{
|
||||
// TODO: do it once every X fames
|
||||
AutoreleasePoolInterval++;
|
||||
if (AutoreleasePoolInterval >= 60)
|
||||
{
|
||||
AutoreleasePoolInterval = 0;
|
||||
[AutoreleasePool drain];
|
||||
AutoreleasePool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
void ApplePlatform::BeforeExit()
|
||||
@@ -343,6 +375,11 @@ void ApplePlatform::BeforeExit()
|
||||
|
||||
void ApplePlatform::Exit()
|
||||
{
|
||||
if (AutoreleasePool)
|
||||
{
|
||||
[AutoreleasePool drain];
|
||||
AutoreleasePool = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplePlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
@@ -369,9 +406,7 @@ bool ApplePlatform::GetHasFocus()
|
||||
if (window->IsFocused())
|
||||
return true;
|
||||
}
|
||||
|
||||
// Default to true if has no windows open
|
||||
return WindowsManager::Windows.IsEmpty();
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApplePlatform::CreateGuid(Guid& result)
|
||||
|
||||
@@ -79,7 +79,10 @@ public:
|
||||
static uint64 GetClockFrequency();
|
||||
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
#if !BUILD_RELEASE
|
||||
static void Log(const StringView& msg);
|
||||
static bool IsDebuggerPresent();
|
||||
#endif
|
||||
static bool Init();
|
||||
static void Tick();
|
||||
static void BeforeExit();
|
||||
|
||||
@@ -59,9 +59,11 @@ void WindowsManagerService::Update()
|
||||
// Update windows
|
||||
const float deltaTime = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
for (Window* win : WindowsManager::Windows)
|
||||
Array<Window*, InlinedAllocation<32>> windows;
|
||||
windows.Add(WindowsManager::Windows);
|
||||
for (Window* win : windows)
|
||||
{
|
||||
if (win && win->IsVisible())
|
||||
if (win->IsVisible())
|
||||
win->OnUpdate(deltaTime);
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
@@ -71,7 +73,8 @@ void WindowsManagerService::Dispose()
|
||||
{
|
||||
// Close remaining windows
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
auto windows = WindowsManager::Windows;
|
||||
Array<Window*, InlinedAllocation<32>> windows;
|
||||
windows.Add(WindowsManager::Windows);
|
||||
for (Window* win : windows)
|
||||
{
|
||||
win->Close(ClosingReason::EngineExit);
|
||||
|
||||
@@ -2622,9 +2622,7 @@ bool LinuxPlatform::GetHasFocus()
|
||||
if (window->IsFocused())
|
||||
return true;
|
||||
}
|
||||
|
||||
// Default to true if has no windows open
|
||||
return WindowsManager::Windows.IsEmpty();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinuxPlatform::CanOpenUrl(const StringView& url)
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
|
||||
#include "../Window.h"
|
||||
#include "Engine/Platform/Apple/AppleUtils.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/IGuiData.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Platform/CriticalSection.h"
|
||||
#include "Engine/Threading/ThreadPoolTask.h"
|
||||
#include "Engine/Threading/ThreadPool.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#endif
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
@@ -14,6 +21,37 @@
|
||||
#include <AppKit/AppKit.h>
|
||||
#include <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
#if USE_EDITOR
|
||||
// Data for drawing window while doing drag&drop on Mac (engine is paused during platform tick)
|
||||
CriticalSection MacDragLocker;
|
||||
NSDraggingSession* MacDragSession = nullptr;
|
||||
class DoDragDropJob* MacDragJob = nullptr;
|
||||
|
||||
class DoDragDropJob : public ThreadPoolTask
|
||||
{
|
||||
public:
|
||||
int64 ExitFlag = 0;
|
||||
|
||||
bool Run() override
|
||||
{
|
||||
while (Platform::AtomicRead(&ExitFlag) == 0)
|
||||
{
|
||||
Engine::OnDraw();
|
||||
Platform::Sleep(20);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
inline bool IsWindowInvalid(Window* win)
|
||||
{
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
const bool hasWindow = WindowsManager::Windows.Contains(win);
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
return !hasWindow || !win;
|
||||
}
|
||||
|
||||
KeyboardKeys GetKey(NSEvent* event)
|
||||
{
|
||||
switch ([event keyCode])
|
||||
@@ -268,17 +306,20 @@ NSDragOperation GetDragDropOperation(DragDropEffect dragDropEffect)
|
||||
// Handle resizing to be sure that content has valid size when window was resized
|
||||
[self windowDidResize:notification];
|
||||
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Window->OnGotFocus();
|
||||
}
|
||||
|
||||
- (void)windowDidResignKey:(NSNotification*)notification
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Window->OnLostFocus();
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification*)notification
|
||||
{
|
||||
[self setDelegate: nil];
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Window->Close(ClosingReason::User);
|
||||
}
|
||||
|
||||
@@ -311,7 +352,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
@end
|
||||
|
||||
@interface MacViewImpl : NSView
|
||||
@interface MacViewImpl : NSView <NSDraggingSource, NSPasteboardItemDataProvider>
|
||||
{
|
||||
MacWindow* Window;
|
||||
NSTrackingArea* TrackingArea;
|
||||
@@ -375,6 +416,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)keyDown:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
KeyboardKeys key = GetKey(event);
|
||||
if (key != KeyboardKeys::None)
|
||||
Input::Keyboard->OnKeyDown(key, Window);
|
||||
@@ -405,6 +447,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)keyUp:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
KeyboardKeys key = GetKey(event);
|
||||
if (key != KeyboardKeys::None)
|
||||
Input::Keyboard->OnKeyUp(key, Window);
|
||||
@@ -412,6 +455,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)flagsChanged:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
int32 modMask;
|
||||
int32 keyCode = [event keyCode];
|
||||
if (keyCode == 0x36 || keyCode == 0x37)
|
||||
@@ -437,6 +481,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)scrollWheel:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
double deltaX = [event scrollingDeltaX];
|
||||
double deltaY = [event scrollingDeltaY];
|
||||
@@ -451,32 +496,39 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)mouseMoved:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
if (!Window->IsMouseTracking() && !IsMouseOver)
|
||||
if (Window->IsMouseTracking())
|
||||
return; // Skip mouse events when tracking mouse (handled in MacWindow::OnUpdate)
|
||||
if (!IsMouseOver)
|
||||
return;
|
||||
Input::Mouse->OnMouseMove(Window->ClientToScreen(mousePos), Window);
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
IsMouseOver = true;
|
||||
Window->SetIsMouseOver(true);
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
IsMouseOver = false;
|
||||
Window->SetIsMouseOver(false);
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
mousePos = Window->ClientToScreen(mousePos);
|
||||
MouseButton mouseButton = MouseButton::Left;
|
||||
if ([event clickCount] == 2)
|
||||
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
|
||||
Input::Mouse->OnMouseDoubleClick(mousePos, mouseButton, Window);
|
||||
else
|
||||
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);
|
||||
Input::Mouse->OnMouseDown(mousePos, mouseButton, Window);
|
||||
}
|
||||
|
||||
- (void)mouseDragged:(NSEvent*)event
|
||||
@@ -486,13 +538,28 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)mouseUp:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
mousePos = Window->ClientToScreen(mousePos);
|
||||
MouseButton mouseButton = MouseButton::Left;
|
||||
Input::Mouse->OnMouseUp(Window->ClientToScreen(mousePos), mouseButton, Window);
|
||||
Input::Mouse->OnMouseUp(mousePos, mouseButton, Window);
|
||||
|
||||
// Redirect event to any window that tracks the mouse (eg. dock window in Editor)
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
for (auto* win : WindowsManager::Windows)
|
||||
{
|
||||
if (win->IsVisible() && win->IsMouseTracking() && win != Window)
|
||||
{
|
||||
Input::Mouse->OnMouseUp(mousePos, mouseButton, win);
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
}
|
||||
|
||||
- (void)rightMouseDown:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
MouseButton mouseButton = MouseButton::Right;
|
||||
if ([event clickCount] == 2)
|
||||
@@ -508,6 +575,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)rightMouseUp:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
MouseButton mouseButton = MouseButton::Right;
|
||||
Input::Mouse->OnMouseUp(Window->ClientToScreen(mousePos), mouseButton, Window);
|
||||
@@ -515,6 +583,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)otherMouseDown:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
MouseButton mouseButton;
|
||||
switch ([event buttonNumber])
|
||||
@@ -544,6 +613,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)otherMouseUp:(NSEvent*)event
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Float2 mousePos = GetMousePosition(Window, event);
|
||||
MouseButton mouseButton;
|
||||
switch ([event buttonNumber])
|
||||
@@ -565,6 +635,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return NSDragOperationNone;
|
||||
Float2 mousePos;
|
||||
MacDropData dropData;
|
||||
GetDragDropData(Window, sender, mousePos, dropData);
|
||||
@@ -580,6 +651,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return NSDragOperationNone;
|
||||
Float2 mousePos;
|
||||
MacDropData dropData;
|
||||
GetDragDropData(Window, sender, mousePos, dropData);
|
||||
@@ -590,6 +662,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return NO;
|
||||
Float2 mousePos;
|
||||
MacDropData dropData;
|
||||
GetDragDropData(Window, sender, mousePos, dropData);
|
||||
@@ -600,9 +673,38 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
|
||||
|
||||
- (void)draggingExited:(id<NSDraggingInfo>)sender
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
Window->OnDragLeave();
|
||||
}
|
||||
|
||||
- (NSDragOperation)draggingSession:(NSDraggingSession*)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return NSDragOperationNone;
|
||||
return NSDragOperationMove;
|
||||
}
|
||||
|
||||
- (void)draggingSession:(NSDraggingSession*)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Stop background worker once the drag ended
|
||||
MacDragLocker.Lock();
|
||||
if (MacDragSession && MacDragSession == session)
|
||||
{
|
||||
Platform::AtomicStore(&MacDragJob->ExitFlag, 1);
|
||||
MacDragJob->Wait();
|
||||
MacDragSession = nullptr;
|
||||
MacDragJob = nullptr;
|
||||
}
|
||||
MacDragLocker.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)pasteboard:(nullable NSPasteboard*)pasteboard item:(NSPasteboardItem*)item provideDataForType:(NSPasteboardType)type
|
||||
{
|
||||
if (IsWindowInvalid(Window)) return;
|
||||
[pasteboard setString:(NSString*)AppleUtils::ToString(Window->GetDragText()) forType:NSPasteboardTypeString];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
MacWindow::MacWindow(const CreateWindowSettings& settings)
|
||||
@@ -624,6 +726,8 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
|
||||
{
|
||||
styleMask |= NSWindowStyleMaskBorderless;
|
||||
}
|
||||
if (settings.Fullscreen)
|
||||
styleMask |= NSWindowStyleMaskFullScreen;
|
||||
if (settings.HasBorder)
|
||||
{
|
||||
styleMask |= NSWindowStyleMaskTitled;
|
||||
@@ -650,9 +754,11 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
|
||||
[window setMaxSize:NSMakeSize(settings.MaximumSize.X, settings.MaximumSize.Y)];
|
||||
[window setOpaque:!settings.SupportsTransparency];
|
||||
[window setContentView:view];
|
||||
if (settings.AllowInput)
|
||||
[window setAcceptsMouseMovedEvents:YES];
|
||||
[window setDelegate:window];
|
||||
_window = window;
|
||||
_view = view;
|
||||
if (settings.AllowDragAndDrop)
|
||||
{
|
||||
[view registerForDraggedTypes:@[NSPasteboardTypeFileURL, NSPasteboardTypeString]];
|
||||
@@ -664,8 +770,6 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
|
||||
layer.contentsScale = screenScale;
|
||||
|
||||
// TODO: impl Parent for MacWindow
|
||||
// TODO: impl StartPosition for MacWindow
|
||||
// TODO: impl Fullscreen for MacWindow
|
||||
// TODO: impl ShowInTaskbar for MacWindow
|
||||
// TODO: impl IsTopmost for MacWindow
|
||||
}
|
||||
@@ -676,6 +780,7 @@ MacWindow::~MacWindow()
|
||||
[window close];
|
||||
[window release];
|
||||
_window = nullptr;
|
||||
_view = nullptr;
|
||||
}
|
||||
|
||||
void MacWindow::CheckForResize(float width, float height)
|
||||
@@ -699,7 +804,6 @@ void MacWindow::SetIsMouseOver(bool value)
|
||||
// Refresh cursor typet
|
||||
SetCursor(CursorType::Default);
|
||||
SetCursor(cursor);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -714,6 +818,22 @@ void* MacWindow::GetNativePtr() const
|
||||
return _window;
|
||||
}
|
||||
|
||||
void MacWindow::OnUpdate(float dt)
|
||||
{
|
||||
if (IsMouseTracking())
|
||||
{
|
||||
// Keep sending mouse movement events no matter if window has focus
|
||||
Float2 mousePos = Platform::GetMousePosition();
|
||||
if (_mouseTrackPos != mousePos)
|
||||
{
|
||||
_mouseTrackPos = mousePos;
|
||||
Input::Mouse->OnMouseMove(mousePos, this);
|
||||
}
|
||||
}
|
||||
|
||||
WindowBase::OnUpdate(dt);
|
||||
}
|
||||
|
||||
void MacWindow::Show()
|
||||
{
|
||||
if (!_visible)
|
||||
@@ -728,7 +848,10 @@ void MacWindow::Show()
|
||||
|
||||
// Show
|
||||
NSWindow* window = (NSWindow*)_window;
|
||||
if (_settings.AllowInput)
|
||||
[window makeKeyAndOrderFront:window];
|
||||
else
|
||||
[window orderFront:window];
|
||||
if (_settings.ActivateWhenFirstShown)
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
_focused = true;
|
||||
@@ -746,7 +869,7 @@ void MacWindow::Hide()
|
||||
|
||||
// Hide
|
||||
NSWindow* window = (NSWindow*)_window;
|
||||
[window orderOut:nil];
|
||||
[window orderOut:window];
|
||||
|
||||
// Base
|
||||
WindowBase::Hide();
|
||||
@@ -782,14 +905,9 @@ void MacWindow::Restore()
|
||||
[window zoom:nil];
|
||||
}
|
||||
|
||||
bool MacWindow::IsClosed() const
|
||||
{
|
||||
return _window != nullptr;
|
||||
}
|
||||
|
||||
bool MacWindow::IsForegroundWindow() const
|
||||
{
|
||||
return Platform::GetHasFocus() && IsFocused();
|
||||
return IsFocused() && Platform::GetHasFocus();
|
||||
}
|
||||
|
||||
void MacWindow::BringToFront(bool force)
|
||||
@@ -808,14 +926,13 @@ void MacWindow::SetClientBounds(const Rectangle& clientArea)
|
||||
NSWindow* window = (NSWindow*)_window;
|
||||
if (!window)
|
||||
return;
|
||||
const float screenScale = MacPlatform::ScreenScale;
|
||||
|
||||
NSRect oldRect = [window frame];
|
||||
NSRect newRect = NSMakeRect(0, 0, clientArea.Size.X, clientArea.Size.Y);
|
||||
NSRect newRect = NSMakeRect(0, 0, clientArea.Size.X / screenScale, clientArea.Size.Y / screenScale);
|
||||
newRect = [window frameRectForContentRect:newRect];
|
||||
|
||||
//newRect.origin.x = oldRect.origin.x;
|
||||
//newRect.origin.y = NSMaxY(oldRect) - newRect.size.height;
|
||||
|
||||
Float2 pos = AppleUtils::PosToCoca(clientArea.Location);
|
||||
Float2 pos = AppleUtils::PosToCoca(clientArea.Location) / screenScale;
|
||||
Float2 titleSize = GetWindowTitleSize(this);
|
||||
newRect.origin.x = pos.X + titleSize.X;
|
||||
newRect.origin.y = pos.Y - newRect.size.height + titleSize.Y;
|
||||
@@ -909,8 +1026,63 @@ void MacWindow::SetTitle(const StringView& title)
|
||||
|
||||
DragDropEffect MacWindow::DoDragDrop(const StringView& data)
|
||||
{
|
||||
// TODO: implement using beginDraggingSession and NSDraggingSource
|
||||
return DragDropEffect::None;
|
||||
NSWindow* window = (NSWindow*)_window;
|
||||
MacViewImpl* view = (MacViewImpl*)_view;
|
||||
_dragText = data;
|
||||
|
||||
// Create mouse drag event
|
||||
NSEvent* event = [NSEvent
|
||||
mouseEventWithType:NSEventTypeLeftMouseDragged
|
||||
location:window.mouseLocationOutsideOfEventStream
|
||||
modifierFlags:0
|
||||
timestamp:NSApp.currentEvent.timestamp
|
||||
windowNumber:window.windowNumber
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:1
|
||||
pressure:1.0];
|
||||
|
||||
// Create drag item
|
||||
NSPasteboardItem* pasteItem = [NSPasteboardItem new];
|
||||
[pasteItem setDataProvider:view forTypes:[NSArray arrayWithObjects:NSPasteboardTypeString, nil]];
|
||||
NSDraggingItem* dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pasteItem];
|
||||
[dragItem setDraggingFrame:NSMakeRect(event.locationInWindow.x, event.locationInWindow.y, 1, 1) contents:nil];
|
||||
|
||||
// Start dragging session
|
||||
NSDraggingSession* draggingSession = [view beginDraggingSessionWithItems:[NSArray arrayWithObject:dragItem] event:event source:view];
|
||||
DragDropEffect result = DragDropEffect::None;
|
||||
|
||||
#if USE_EDITOR
|
||||
// Create background worker that will keep updating GUI (perform rendering)
|
||||
MacDragLocker.Lock();
|
||||
ASSERT(!MacDragSession && !MacDragJob);
|
||||
MacDragSession = draggingSession;
|
||||
MacDragJob = New<DoDragDropJob>();
|
||||
Task::StartNew(MacDragJob);
|
||||
MacDragLocker.Unlock();
|
||||
while (MacDragJob->GetState() == TaskState::Queued)
|
||||
Platform::Sleep(1);
|
||||
// TODO: maybe wait for the drag end to return result?
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MacWindow::StartTrackingMouse(bool useMouseScreenOffset)
|
||||
{
|
||||
if (_isTrackingMouse || !_window)
|
||||
return;
|
||||
_isTrackingMouse = true;
|
||||
_trackingMouseOffset = Float2::Zero;
|
||||
_isUsingMouseOffset = useMouseScreenOffset;
|
||||
_mouseTrackPos = Float2::Minimum;
|
||||
}
|
||||
|
||||
void MacWindow::EndTrackingMouse()
|
||||
{
|
||||
if (!_isTrackingMouse || !_window)
|
||||
return;
|
||||
_isTrackingMouse = false;
|
||||
}
|
||||
|
||||
void MacWindow::SetCursor(CursorType type)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "Engine/Platform/Base/WindowBase.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the window class for Mac platform.
|
||||
@@ -13,28 +14,32 @@
|
||||
class FLAXENGINE_API MacWindow : public WindowBase
|
||||
{
|
||||
private:
|
||||
|
||||
void* _window;
|
||||
void* _window = nullptr;
|
||||
void* _view = nullptr;
|
||||
bool _isMouseOver = false;
|
||||
Float2 _mouseTrackPos = Float2::Minimum;
|
||||
String _dragText;
|
||||
|
||||
public:
|
||||
|
||||
MacWindow(const CreateWindowSettings& settings);
|
||||
~MacWindow();
|
||||
|
||||
void CheckForResize(float width, float height);
|
||||
void SetIsMouseOver(bool value);
|
||||
const String& GetDragText() const
|
||||
{
|
||||
return _dragText;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [WindowBase]
|
||||
void* GetNativePtr() const override;
|
||||
void OnUpdate(float dt) override;
|
||||
void Show() override;
|
||||
void Hide() override;
|
||||
void Minimize() override;
|
||||
void Maximize() override;
|
||||
void Restore() override;
|
||||
bool IsClosed() const override;
|
||||
bool IsForegroundWindow() const override;
|
||||
void BringToFront(bool force = false) override;
|
||||
void SetClientBounds(const Rectangle& clientArea) override;
|
||||
@@ -50,6 +55,8 @@ public:
|
||||
void Focus() override;
|
||||
void SetTitle(const StringView& title) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data) override;
|
||||
void StartTrackingMouse(bool useMouseScreenOffset) override;
|
||||
void EndTrackingMouse() override;
|
||||
void SetCursor(CursorType type) override;
|
||||
};
|
||||
|
||||
|
||||
@@ -96,7 +96,11 @@ static bool CreateEndPointFromAddr(sockaddr* addr, NetworkEndPoint& endPoint)
|
||||
return true;
|
||||
}
|
||||
char strPort[6];
|
||||
#if __APPLE__
|
||||
snprintf(strPort, sizeof(strPort), "%d", port);
|
||||
#else
|
||||
sprintf(strPort, "%d", port);
|
||||
#endif
|
||||
endPoint.IPVersion = addr->sa_family == AF_INET6 ? NetworkIPVersion::IPv6 : NetworkIPVersion::IPv4;
|
||||
memcpy(endPoint.Data, addr, size);
|
||||
return false;
|
||||
|
||||
@@ -16,16 +16,13 @@ class FLAXENGINE_API Win32CriticalSection
|
||||
friend Win32ConditionVariable;
|
||||
|
||||
private:
|
||||
|
||||
mutable Windows::CRITICAL_SECTION _criticalSection;
|
||||
|
||||
private:
|
||||
|
||||
Win32CriticalSection(const Win32CriticalSection&);
|
||||
Win32CriticalSection& operator=(const Win32CriticalSection&);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Win32CriticalSection"/> class.
|
||||
/// </summary>
|
||||
@@ -43,18 +40,13 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Locks the critical section.
|
||||
/// </summary>
|
||||
void Lock() const
|
||||
{
|
||||
// Spin first before entering critical section, causing ring-0 transition and context switch
|
||||
if (Windows::TryEnterCriticalSection(&_criticalSection) == 0)
|
||||
{
|
||||
Windows::EnterCriticalSection(&_criticalSection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to enter a critical section without blocking. If the call is successful, the calling thread takes ownership of the critical section.
|
||||
|
||||
@@ -451,6 +451,7 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
|
||||
default:
|
||||
break;
|
||||
}
|
||||
flags |= MB_TASKMODAL;
|
||||
|
||||
// Show dialog
|
||||
int result = MessageBoxW(parent ? static_cast<HWND>(parent->GetNativePtr()) : nullptr, String(text).GetText(), String(caption).GetText(), flags);
|
||||
|
||||
@@ -410,7 +410,7 @@ bool iOSWindow::IsClosed() const
|
||||
|
||||
bool iOSWindow::IsForegroundWindow() const
|
||||
{
|
||||
return Platform::GetHasFocus() && IsFocused();
|
||||
return IsFocused() && Platform::GetHasFocus();
|
||||
}
|
||||
|
||||
void iOSWindow::BringToFront(bool force)
|
||||
|
||||
@@ -202,16 +202,14 @@ GPUTexture* DepthOfFieldPass::getDofBokehShape(DepthOfFieldSettings& dofSettings
|
||||
|
||||
void DepthOfFieldPass::Render(RenderContext& renderContext, GPUTexture*& frame, GPUTexture*& tmp)
|
||||
{
|
||||
if (!_platformSupportsDoF || checkIfSkipPass())
|
||||
DepthOfFieldSettings& dofSettings = renderContext.List->Settings.DepthOfField;
|
||||
const bool useDoF = EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::DepthOfField) && dofSettings.Enabled;
|
||||
if (!useDoF || !_platformSupportsDoF || checkIfSkipPass())
|
||||
return;
|
||||
auto device = GPUDevice::Instance;
|
||||
auto context = device->GetMainContext();
|
||||
const auto depthBuffer = renderContext.Buffers->DepthBuffer;
|
||||
const auto shader = _shader->GetShader();
|
||||
DepthOfFieldSettings& dofSettings = renderContext.List->Settings.DepthOfField;
|
||||
const bool useDoF = _platformSupportsDoF && EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::DepthOfField) && dofSettings.Enabled;
|
||||
if (!useDoF)
|
||||
return;
|
||||
PROFILE_GPU_CPU("Depth Of Field");
|
||||
|
||||
context->ResetSR();
|
||||
|
||||
@@ -35,7 +35,6 @@ PACK_STRUCT(struct EyeAdaptationData {
|
||||
|
||||
void EyeAdaptationPass::Render(RenderContext& renderContext, GPUTexture* colorBuffer)
|
||||
{
|
||||
// Cache data
|
||||
auto device = GPUDevice::Instance;
|
||||
auto context = device->GetMainContext();
|
||||
auto& view = renderContext.View;
|
||||
@@ -45,12 +44,8 @@ void EyeAdaptationPass::Render(RenderContext& renderContext, GPUTexture* colorBu
|
||||
//const float frameDelta = Time::ElapsedGameTime.GetTotalSeconds();
|
||||
const float frameDelta = time - renderContext.Buffers->LastEyeAdaptationTime;
|
||||
renderContext.Buffers->LastEyeAdaptationTime = 0.0f;
|
||||
|
||||
// Optionally skip the rendering
|
||||
if (checkIfSkipPass() || (view.Flags & ViewFlags::EyeAdaptation) == ViewFlags::None || settings.Mode == EyeAdaptationMode::None)
|
||||
{
|
||||
if ((view.Flags & ViewFlags::EyeAdaptation) == ViewFlags::None || settings.Mode == EyeAdaptationMode::None || checkIfSkipPass())
|
||||
return;
|
||||
}
|
||||
|
||||
PROFILE_GPU_CPU("Eye Adaptation");
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ void MotionBlurPass::RenderDebug(RenderContext& renderContext, GPUTextureView* f
|
||||
{
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
const auto motionVectors = renderContext.Buffers->MotionVectors;
|
||||
if (!motionVectors->IsAllocated() || checkIfSkipPass())
|
||||
if (!motionVectors || !motionVectors->IsAllocated() || checkIfSkipPass())
|
||||
{
|
||||
context->Draw(frame);
|
||||
return;
|
||||
|
||||
@@ -195,7 +195,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input,
|
||||
bool useLensFlares = EnumHasAnyFlags(view.Flags, ViewFlags::LensFlares) && settings.LensFlares.Intensity > 0.0f && useBloom;
|
||||
|
||||
// Ensure to have valid data and if at least one effect should be applied
|
||||
if (checkIfSkipPass() || !(useBloom || useToneMapping || useCameraArtifacts))
|
||||
if (!(useBloom || useToneMapping || useCameraArtifacts) || checkIfSkipPass())
|
||||
{
|
||||
// Resources are missing. Do not perform rendering. Just copy raw frame
|
||||
context->SetViewportAndScissors((float)output->Width(), (float)output->Height());
|
||||
|
||||
@@ -1270,7 +1270,11 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
|
||||
// Invoke the method
|
||||
MObject* exception = nullptr;
|
||||
#if USE_NETCORE // NetCore uses the same path for both virtual and non-virtual calls
|
||||
MObject* resultObject = mMethod->Invoke(mInstance, params, &exception);
|
||||
#else
|
||||
MObject* resultObject = withInterfaces ? mMethod->InvokeVirtual((MObject*)mInstance, params, &exception) : mMethod->Invoke(mInstance, params, &exception);
|
||||
#endif
|
||||
if (exception)
|
||||
{
|
||||
MException ex(exception);
|
||||
|
||||
@@ -320,7 +320,7 @@ namespace FlaxEngine
|
||||
internal static partial Object Internal_Create2(string typeName);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance, IntPtr theKlass);
|
||||
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance, IntPtr typeClass);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
|
||||
internal static partial void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <mono/metadata/metadata.h>
|
||||
#include <mono/metadata/threads.h>
|
||||
#include <mono/metadata/reflection.h>
|
||||
#include <mono/metadata/mono-gc.h>
|
||||
#include <mono/metadata/mono-private-unstable.h>
|
||||
typedef char char_t;
|
||||
#define DOTNET_HOST_MONO_DEBUG 0
|
||||
@@ -522,7 +523,8 @@ void MCore::GC::FreeMemory(void* ptr, bool coTaskMem)
|
||||
|
||||
void MCore::Thread::Attach()
|
||||
{
|
||||
#if DOTNET_HOST_MONO
|
||||
// TODO: find a way to properly register native thread so Mono Stop The World (stw) won't freeze when native threads (eg. Job System) are running native code only
|
||||
#if DOTNET_HOST_MONO && 0
|
||||
if (!IsInMainThread() && !mono_domain_get())
|
||||
{
|
||||
mono_thread_attach(MonoDomainHandle);
|
||||
@@ -2056,7 +2058,7 @@ bool InitHostfxr()
|
||||
// Setup debugger
|
||||
{
|
||||
int32 debuggerLogLevel = 0;
|
||||
if (CommandLine::Options.MonoLog.IsTrue())
|
||||
if (CommandLine::Options.MonoLog.IsTrue() || DOTNET_HOST_MONO_DEBUG)
|
||||
{
|
||||
LOG(Info, "Using detailed Mono logging");
|
||||
mono_trace_set_level_string("debug");
|
||||
@@ -2139,6 +2141,7 @@ bool InitHostfxr()
|
||||
LOG(Fatal, "Failed to initialize Mono.");
|
||||
return true;
|
||||
}
|
||||
mono_gc_init_finalizer_thread();
|
||||
|
||||
// Log info
|
||||
char* buildInfo = mono_get_runtime_build_info();
|
||||
|
||||
@@ -460,16 +460,15 @@ bool ShaderCompiler::WriteCustomDataVS(ShaderCompilationContext* context, Shader
|
||||
auto& element = layout[a];
|
||||
if (!layoutVisible[a])
|
||||
continue;
|
||||
|
||||
// TODO: serialize whole struct?
|
||||
|
||||
output->WriteByte(static_cast<byte>(element.Type));
|
||||
output->WriteByte(element.Index);
|
||||
output->WriteByte(static_cast<byte>(element.Format));
|
||||
output->WriteByte(element.InputSlot);
|
||||
output->WriteUint32(element.AlignedByteOffset);
|
||||
output->WriteByte(element.InputSlotClass);
|
||||
output->WriteUint32(element.InstanceDataStepRate);
|
||||
GPUShaderProgramVS::InputElement data;
|
||||
data.Type = static_cast<byte>(element.Type);
|
||||
data.Index = element.Index;
|
||||
data.Format = static_cast<byte>(element.Format);
|
||||
data.InputSlot = element.InputSlot;
|
||||
data.AlignedByteOffset = element.AlignedByteOffset;
|
||||
data.InputSlotClass = element.InputSlotClass;
|
||||
data.InstanceDataStepRate = element.InstanceDataStepRate;
|
||||
output->Write(data);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
Binary file not shown.
@@ -222,7 +222,7 @@ ${PBXResourcesGroup}
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = ${HeaderSearchPaths};
|
||||
HEADER_SEARCH_PATHS = "${HeaderSearchPaths}";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
@@ -275,7 +275,7 @@ ${PBXResourcesGroup}
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = ${HeaderSearchPaths};
|
||||
HEADER_SEARCH_PATHS = "${HeaderSearchPaths}";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
|
||||
2
Source/ThirdParty/stb/stb_image_write.h
vendored
2
Source/ThirdParty/stb/stb_image_write.h
vendored
@@ -758,6 +758,8 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f
|
||||
|
||||
#ifdef __STDC_WANT_SECURE_LIB__
|
||||
len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
|
||||
#elif __APPLE__
|
||||
len = snprintf(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
|
||||
#else
|
||||
len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x);
|
||||
#endif
|
||||
|
||||
9
Source/ThirdParty/volk/volk.Build.cs
vendored
9
Source/ThirdParty/volk/volk.Build.cs
vendored
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
@@ -62,4 +63,12 @@ public class volk : ThirdPartyModule
|
||||
Log.ErrorOnce("Missing VulkanSDK.", ref _missingSDKError);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
base.GetFilesToDeploy(files);
|
||||
|
||||
files.Add(Path.Combine(FolderPath, "volk.h"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3135,14 +3135,17 @@ namespace Flax.Build.Bindings
|
||||
contents.AppendLine("#pragma once");
|
||||
contents.AppendLine();
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_NAME \"{binaryModuleName}\"");
|
||||
if (version.Build == -1)
|
||||
if (version.Build <= 0)
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION Version({version.Major}, {version.Minor})");
|
||||
else
|
||||
else if (version.Revision <= 0)
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION Version({version.Major}, {version.Minor}, {version.Build})");
|
||||
else
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION Version({version.Major}, {version.Minor}, {version.Build}, {version.Revision})");
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_TEXT \"{version}\"");
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_MAJOR {version.Major}");
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_MINOR {version.Minor}");
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_BUILD {version.Build}");
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_REVISION {version.Revision}");
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_COMPANY \"{project.Company}\"");
|
||||
contents.AppendLine($"#define {binaryModuleNameUpper}_COPYRIGHT \"{project.Copyright}\"");
|
||||
contents.AppendLine();
|
||||
|
||||
@@ -44,6 +44,12 @@ namespace Flax.Build
|
||||
/// </summary>
|
||||
[CommandLine("deployCertPass", "Certificate file password for binaries signing.")]
|
||||
public static string DeployCertPass;
|
||||
|
||||
/// <summary>
|
||||
/// Apple keychain profile name to use for app notarize action (installed locally).
|
||||
/// </summary>
|
||||
[CommandLine("deployKeychainProfile", "Apple keychain profile name to use for app notarize action (installed locally).")]
|
||||
public static string DeployKeychainProfile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,12 +72,6 @@ namespace Flax.Deploy
|
||||
{
|
||||
Initialize();
|
||||
|
||||
if (Configuration.DeployEditor)
|
||||
{
|
||||
BuildEditor();
|
||||
Deployment.Editor.Package();
|
||||
}
|
||||
|
||||
if (Configuration.DeployPlatforms)
|
||||
{
|
||||
if (Configuration.BuildPlatforms == null || Configuration.BuildPlatforms.Length == 0)
|
||||
@@ -94,6 +94,12 @@ namespace Flax.Deploy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Configuration.DeployEditor)
|
||||
{
|
||||
BuildEditor();
|
||||
Deployment.Editor.Package();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -183,6 +189,10 @@ namespace Flax.Deploy
|
||||
{
|
||||
if (Platform.IsPlatformSupported(platform, architecture))
|
||||
{
|
||||
Log.Info(string.Empty);
|
||||
Log.Info($"Build {platform} {architecture} platform");
|
||||
Log.Info(string.Empty);
|
||||
|
||||
foreach (var configuration in Configurations)
|
||||
{
|
||||
FlaxBuild.Build(Globals.EngineRoot, "FlaxGame", platform, architecture, configuration);
|
||||
|
||||
@@ -127,6 +127,33 @@ namespace Flax.Deploy
|
||||
// Deploy project
|
||||
DeployFile(RootPath, OutputPath, "Flax.flaxproj");
|
||||
|
||||
// When deploying Editor with Platforms at once then bundle them inside it
|
||||
if (Configuration.DeployPlatforms && Platforms.PackagedPlatforms != null)
|
||||
{
|
||||
foreach (var platform in Platforms.PackagedPlatforms)
|
||||
{
|
||||
Log.Info(string.Empty);
|
||||
Log.Info($"Bunding {platform} platform with Editor");
|
||||
Log.Info(string.Empty);
|
||||
|
||||
string platformName = platform.ToString();
|
||||
string platformFiles = Path.Combine(Deployer.PackageOutputPath, platformName);
|
||||
string platformData = Path.Combine(OutputPath, "Source", "Platforms", platformName);
|
||||
if (Directory.Exists(platformFiles))
|
||||
{
|
||||
// Copy deployed files
|
||||
Utilities.DirectoryCopy(platformFiles, platformData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Extract deployed files
|
||||
var packageZipPath = Path.Combine(Deployer.PackageOutputPath, platformName + ".zip");
|
||||
Log.Verbose(packageZipPath + " -> " + platformData);
|
||||
System.IO.Compression.ZipFile.ExtractToDirectory(packageZipPath, platformData, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Package Editor into macOS app
|
||||
if (Platform.BuildTargetPlatform == TargetPlatform.Mac)
|
||||
{
|
||||
@@ -164,12 +191,34 @@ namespace Flax.Deploy
|
||||
var defaultEditorConfig = "Development";
|
||||
var ediotrBinariesPath = Path.Combine(appContentsPath, "Binaries/Editor/Mac", defaultEditorConfig);
|
||||
Utilities.DirectoryCopy(ediotrBinariesPath, appBinariesPath, true, true);
|
||||
|
||||
// Sign app resources
|
||||
CodeSign(appPath);
|
||||
|
||||
// Build a disk image
|
||||
var dmgPath = Path.Combine(Deployer.PackageOutputPath, "FlaxEditor.dmg");
|
||||
Log.Info(string.Empty);
|
||||
Log.Info("Building disk image...");
|
||||
if (File.Exists(dmgPath))
|
||||
File.Delete(dmgPath);
|
||||
Utilities.Run("hdiutil", $"create -srcFolder \"{appPath}\" -o \"{dmgPath}\"", null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError);
|
||||
CodeSign(dmgPath);
|
||||
Log.Info("Output disk image size: " + Utilities.GetFileSize(dmgPath));
|
||||
|
||||
// Notarize disk image
|
||||
if (!string.IsNullOrEmpty(Configuration.DeployKeychainProfile))
|
||||
{
|
||||
Log.Info(string.Empty);
|
||||
Log.Info("Notarizing disk image...");
|
||||
Utilities.Run("xcrun", $"notarytool submit \"{dmgPath}\" --wait --keychain-profile \"{Configuration.DeployKeychainProfile}\"", null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError);
|
||||
Utilities.Run("xcrun", $"stapler staple \"{dmgPath}\"", null, null, Utilities.RunOptions.Default | Utilities.RunOptions.ThrowExceptionOnError);
|
||||
Log.Info("App notarized for macOS distribution!");
|
||||
}
|
||||
}
|
||||
|
||||
// Compress
|
||||
if (Configuration.DontCompress)
|
||||
return;
|
||||
|
||||
Log.Info(string.Empty);
|
||||
Log.Info("Compressing editor files...");
|
||||
string editorPackageZipPath;
|
||||
@@ -299,6 +348,7 @@ namespace Flax.Deploy
|
||||
Utilities.Run("strip", "FlaxEditor.dylib", null, dst, Utilities.RunOptions.None);
|
||||
Utilities.Run("strip", "libMoltenVK.dylib", null, dst, Utilities.RunOptions.None);
|
||||
|
||||
// Sign binaries
|
||||
CodeSign(Path.Combine(dst, "FlaxEditor"));
|
||||
CodeSign(Path.Combine(dst, "FlaxEditor.dylib"));
|
||||
CodeSign(Path.Combine(dst, "libMoltenVK.dylib"));
|
||||
|
||||
@@ -10,8 +10,13 @@ namespace Flax.Deploy
|
||||
{
|
||||
public class Platforms
|
||||
{
|
||||
internal static List<TargetPlatform> PackagedPlatforms;
|
||||
|
||||
public static void Package(TargetPlatform platform)
|
||||
{
|
||||
if (PackagedPlatforms == null)
|
||||
PackagedPlatforms = new List<TargetPlatform>();
|
||||
PackagedPlatforms.Add(platform);
|
||||
var platformsRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms");
|
||||
|
||||
Log.Info(string.Empty);
|
||||
@@ -50,6 +55,20 @@ namespace Flax.Deploy
|
||||
CodeSign(Path.Combine(binaries, "FlaxGame.exe"));
|
||||
CodeSign(Path.Combine(binaries, "FlaxEngine.CSharp.dll"));
|
||||
}
|
||||
else if (platform == TargetPlatform.Mac)
|
||||
{
|
||||
var binaries = Path.Combine(dst, "Binaries", "Game", "arm64", "Debug");
|
||||
CodeSign(Path.Combine(binaries, "FlaxGame"));
|
||||
CodeSign(Path.Combine(binaries, "FlaxGame.dylib"));
|
||||
|
||||
binaries = Path.Combine(dst, "Binaries", "Game", "arm64", "Development");
|
||||
CodeSign(Path.Combine(binaries, "FlaxGame"));
|
||||
CodeSign(Path.Combine(binaries, "FlaxGame.dylib"));
|
||||
|
||||
binaries = Path.Combine(dst, "Binaries", "Game", "arm64", "Release");
|
||||
CodeSign(Path.Combine(binaries, "FlaxGame"));
|
||||
CodeSign(Path.Combine(binaries, "FlaxGame.dylib"));
|
||||
}
|
||||
|
||||
// Don't distribute engine deps
|
||||
Utilities.DirectoryDelete(Path.Combine(dst, "Binaries", "ThirdParty"));
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Flax.Deploy
|
||||
if (!string.IsNullOrEmpty(Configuration.Compiler))
|
||||
cmdLine += " -compiler=" + Configuration.Compiler;
|
||||
|
||||
Log.Info($"Building {target} for {platform} {architecture} {configuration}...");
|
||||
int result = Utilities.Run(flaxBuildTool, cmdLine, null, root);
|
||||
if (result != 0)
|
||||
{
|
||||
|
||||
@@ -53,10 +53,19 @@ namespace Flax.Build.Platforms
|
||||
/// <param name="signIdenity">App code signing idenity name (from local Mac keychain). Use 'security find-identity -v -p codesigning' to list possible options.</param>
|
||||
public static void CodeSign(string file, string signIdenity)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
var isDirectory = Directory.Exists(file);
|
||||
if (!isDirectory && !File.Exists(file))
|
||||
throw new FileNotFoundException("Missing file to sign.", file);
|
||||
string cmdLine = string.Format("--force --timestamp -s \"{0}\" \"{1}\"", signIdenity, file);
|
||||
if (string.IsNullOrEmpty(Path.GetExtension(file)))
|
||||
if (isDirectory)
|
||||
{
|
||||
// Automatically sign contents
|
||||
cmdLine += " --deep";
|
||||
}
|
||||
{
|
||||
// Enable the hardened runtime
|
||||
cmdLine += " --options=runtime";
|
||||
}
|
||||
{
|
||||
// Add entitlements file with some settings for the app execution
|
||||
cmdLine += string.Format(" --entitlements \"{0}\"", Path.Combine(Globals.EngineRoot, "Source/Platforms/Mac/Default.entitlements"));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user