diff --git a/README.md b/README.md
index 1b0d06bf6..5e1374e02 100644
--- a/README.md
+++ b/README.md
@@ -124,3 +124,7 @@ Install `.NET SDK`, `NuGet package manager` and `NuGet targets and build tasks`
Using Flax source code is strictly governed by the Flax Engine End User License Agreement. If you don't agree to those terms, as amended from time to time, you are not permitted to access or use Flax Engine.
We welcome any contributions to Flax Engine development through pull requests on GitHub. Most of our active development is in the master branch, so we prefer to take pull requests there (particularly for new features). We try to make sure that all new code adheres to the Flax coding standards. All contributions are governed by the terms of the [EULA](https://flaxengine.com/licensing/).
+
+## SAST Tools
+
+[PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
diff --git a/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs b/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs
index eae7f9024..5d06b4a94 100644
--- a/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs
+++ b/Source/Editor/CustomEditors/Dedicated/AnimatedModelEditor.cs
@@ -1,5 +1,7 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
+using System;
+using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.Surface;
using FlaxEngine;
@@ -12,6 +14,8 @@ namespace FlaxEditor.CustomEditors.Dedicated
[CustomEditor(typeof(AnimatedModel)), DefaultEditor]
public class AnimatedModelEditor : ActorEditor
{
+ private bool _parametersAdded = false;
+
///
public override void Initialize(LayoutElementsContainer layout)
{
@@ -31,6 +35,24 @@ namespace FlaxEditor.CustomEditors.Dedicated
(instance, parameter, tag) => ((AnimatedModel)instance).GetParameterValue(parameter.Identifier),
(instance, value, parameter, tag) => ((AnimatedModel)instance).SetParameterValue(parameter.Identifier, value),
Values);
+ _parametersAdded = true;
+ }
+ }
+
+ ///
+ public override void Refresh()
+ {
+ base.Refresh();
+
+ // Check if parameters group is still showing if not in play mode and hide it.
+ if (!Editor.Instance.StateMachine.IsPlayMode && _parametersAdded)
+ {
+ var group = Layout.Children.Find(x => x is GroupElement g && g.Panel.HeaderText.Equals("Parameters", StringComparison.Ordinal));
+ if (group != null)
+ {
+ RebuildLayout();
+ _parametersAdded = false;
+ }
}
}
}
diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
index 4eb523b74..78521361a 100644
--- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs
+++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs
@@ -178,6 +178,8 @@ namespace FlaxEditor.CustomEditors.Editors
return 1;
if (Info.MetadataToken < other.Info.MetadataToken)
return -1;
+ // Keep declaration order if same metadata token.
+ return 0;
}
// By name
diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs
index 81f76d392..8fe91c252 100644
--- a/Source/Editor/Gizmo/TransformGizmoBase.cs
+++ b/Source/Editor/Gizmo/TransformGizmoBase.cs
@@ -138,18 +138,17 @@ namespace FlaxEditor.Gizmo
private void UpdateGizmoPosition()
{
+ var position = Vector3.Zero;
+
// Get gizmo pivot
switch (_activePivotType)
{
case PivotType.ObjectCenter:
if (SelectionCount > 0)
- Position = GetSelectedTransform(0).Translation;
+ position = GetSelectedTransform(0).Translation;
break;
case PivotType.SelectionCenter:
- Position = GetSelectionCenter();
- break;
- case PivotType.WorldOrigin:
- Position = Vector3.Zero;
+ position = GetSelectionCenter();
break;
}
@@ -157,11 +156,13 @@ namespace FlaxEditor.Gizmo
if (_vertexSnapObject != null)
{
Vector3 vertexSnapPoint = _vertexSnapObject.Transform.LocalToWorld(_vertexSnapPoint);
- Position += vertexSnapPoint - Position;
+ position += vertexSnapPoint - position;
}
// Apply current movement
- Position += _translationDelta;
+ position += _translationDelta;
+
+ Position = position;
}
private void UpdateMatrices()
@@ -213,10 +214,11 @@ namespace FlaxEditor.Gizmo
ray.Position = Vector3.Transform(ray.Position, invRotationMatrix);
Vector3.TransformNormal(ref ray.Direction, ref invRotationMatrix, out ray.Direction);
- var planeXY = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z);
- var planeYZ = new Plane(Vector3.Left, Vector3.Transform(Position, invRotationMatrix).X);
- var planeZX = new Plane(Vector3.Down, Vector3.Transform(Position, invRotationMatrix).Y);
- var dir = Vector3.Normalize(ray.Position - Position);
+ var position = Position;
+ var planeXY = new Plane(Vector3.Backward, Vector3.Transform(position, invRotationMatrix).Z);
+ var planeYZ = new Plane(Vector3.Left, Vector3.Transform(position, invRotationMatrix).X);
+ var planeZX = new Plane(Vector3.Down, Vector3.Transform(position, invRotationMatrix).Y);
+ var dir = Vector3.Normalize(ray.Position - position);
var planeDotXY = Mathf.Abs(Vector3.Dot(planeXY.Normal, dir));
var planeDotYZ = Mathf.Abs(Vector3.Dot(planeYZ.Normal, dir));
var planeDotZX = Mathf.Abs(Vector3.Dot(planeZX.Normal, dir));
@@ -229,8 +231,8 @@ namespace FlaxEditor.Gizmo
var plane = planeDotXY > planeDotZX ? planeXY : planeZX;
if (ray.Intersects(ref plane, out intersection))
{
- _intersectPosition = ray.Position + ray.Direction * intersection;
- if (_lastIntersectionPosition != Vector3.Zero)
+ _intersectPosition = ray.GetPoint(intersection);
+ if (!_lastIntersectionPosition.IsZero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, 0, 0);
}
@@ -241,8 +243,8 @@ namespace FlaxEditor.Gizmo
var plane = planeDotXY > planeDotYZ ? planeXY : planeYZ;
if (ray.Intersects(ref plane, out intersection))
{
- _intersectPosition = ray.Position + ray.Direction * intersection;
- if (_lastIntersectionPosition != Vector3.Zero)
+ _intersectPosition = ray.GetPoint(intersection);
+ if (!_lastIntersectionPosition.IsZero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, _tDelta.Y, 0);
}
@@ -253,8 +255,8 @@ namespace FlaxEditor.Gizmo
var plane = planeDotZX > planeDotYZ ? planeZX : planeYZ;
if (ray.Intersects(ref plane, out intersection))
{
- _intersectPosition = ray.Position + ray.Direction * intersection;
- if (_lastIntersectionPosition != Vector3.Zero)
+ _intersectPosition = ray.GetPoint(intersection);
+ if (!_lastIntersectionPosition.IsZero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, 0, _tDelta.Z);
}
@@ -264,8 +266,8 @@ namespace FlaxEditor.Gizmo
{
if (ray.Intersects(ref planeYZ, out intersection))
{
- _intersectPosition = ray.Position + ray.Direction * intersection;
- if (_lastIntersectionPosition != Vector3.Zero)
+ _intersectPosition = ray.GetPoint(intersection);
+ if (!_lastIntersectionPosition.IsZero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(0, _tDelta.Y, _tDelta.Z);
}
@@ -275,8 +277,8 @@ namespace FlaxEditor.Gizmo
{
if (ray.Intersects(ref planeXY, out intersection))
{
- _intersectPosition = ray.Position + ray.Direction * intersection;
- if (_lastIntersectionPosition != Vector3.Zero)
+ _intersectPosition = ray.GetPoint(intersection);
+ if (!_lastIntersectionPosition.IsZero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, _tDelta.Y, 0);
}
@@ -286,8 +288,8 @@ namespace FlaxEditor.Gizmo
{
if (ray.Intersects(ref planeZX, out intersection))
{
- _intersectPosition = ray.Position + ray.Direction * intersection;
- if (_lastIntersectionPosition != Vector3.Zero)
+ _intersectPosition = ray.GetPoint(intersection);
+ if (!_lastIntersectionPosition.IsZero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
delta = new Vector3(_tDelta.X, 0, _tDelta.Z);
}
@@ -299,8 +301,8 @@ namespace FlaxEditor.Gizmo
var plane = new Plane(-Vector3.Normalize(gizmoToView), gizmoToView.Length);
if (ray.Intersects(ref plane, out intersection))
{
- _intersectPosition = ray.Position + ray.Direction * intersection;
- if (_lastIntersectionPosition != Vector3.Zero)
+ _intersectPosition = ray.GetPoint(intersection);
+ if (!_lastIntersectionPosition.IsZero)
_tDelta = _intersectPosition - _lastIntersectionPosition;
}
delta = _tDelta;
@@ -412,10 +414,8 @@ namespace FlaxEditor.Gizmo
public override void Update(float dt)
{
LastDelta = Transform.Identity;
-
if (!IsActive)
return;
-
bool isLeftBtnDown = Owner.IsLeftMouseButtonDown;
// Snap to ground
@@ -516,6 +516,7 @@ namespace FlaxEditor.Gizmo
{
// Clear cache
_accMoveDelta = Vector3.Zero;
+ _lastIntersectionPosition = _intersectPosition = Vector3.Zero;
EndTransforming();
}
}
diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs
index 1055dc5a2..eb877d84a 100644
--- a/Source/Editor/Modules/ContentDatabaseModule.cs
+++ b/Source/Editor/Modules/ContentDatabaseModule.cs
@@ -717,6 +717,9 @@ namespace FlaxEditor.Modules
/// Should rebuild entire database after addition.
public void AddProxy(ContentProxy proxy, bool rebuild = false)
{
+ var oldProxy = Proxy.Find(x => x.GetType().ToString().Equals(proxy.GetType().ToString(), StringComparison.Ordinal));
+ if (oldProxy != null)
+ RemoveProxy(oldProxy);
Proxy.Insert(0, proxy);
if (rebuild)
Rebuild(true);
diff --git a/Source/Editor/Options/ViewportOptions.cs b/Source/Editor/Options/ViewportOptions.cs
index 454315b91..ceebf594b 100644
--- a/Source/Editor/Options/ViewportOptions.cs
+++ b/Source/Editor/Options/ViewportOptions.cs
@@ -24,6 +24,13 @@ namespace FlaxEditor.Options
[DefaultValue(1.0f), Limit(0.01f, 100.0f)]
[EditorDisplay("General"), EditorOrder(101), Tooltip("The mouse wheel sensitivity applied to zoom in orthographic mode.")]
public float MouseWheelSensitivity { get; set; } = 1.0f;
+
+ ///
+ /// Gets or sets whether to invert the Y rotation of the mouse in the editor viewport.
+ ///
+ [DefaultValue(false)]
+ [EditorDisplay("General"), EditorOrder(102), Tooltip("Whether to invert the Y rotation of the mouse in the editor viewport.")]
+ public bool InvertMouseYAxisRotation { get; set; } = false;
///
/// Gets or sets the total amount of steps the camera needs to go from minimum to maximum speed.
diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs
index d30fb6cbc..81014acf4 100644
--- a/Source/Editor/Viewport/EditorViewport.cs
+++ b/Source/Editor/Viewport/EditorViewport.cs
@@ -111,6 +111,9 @@ namespace FlaxEditor.Viewport
IsMouseRightDown = useMouse && window.GetMouseButton(MouseButton.Right);
IsMouseMiddleDown = useMouse && window.GetMouseButton(MouseButton.Middle);
IsMouseLeftDown = useMouse && window.GetMouseButton(MouseButton.Left);
+
+ if (WasAltDownBefore && !IsMouseLeftDown && !IsAltDown)
+ WasAltDownBefore = false;
}
///
@@ -1385,15 +1388,32 @@ namespace FlaxEditor.Viewport
///
/// Converts the mouse position to the ray (in world space of the viewport).
///
- /// The mouse position.
+ /// The mouse position (in UI space of the viewport [0; Size]).
/// The result ray.
public Ray ConvertMouseToRay(ref Float2 mousePosition)
{
- // Prepare
var viewport = new FlaxEngine.Viewport(0, 0, Width, Height);
- CreateProjectionMatrix(out var p);
+ if (viewport.Width < Mathf.Epsilon || viewport.Height < Mathf.Epsilon)
+ return ViewRay;
+
Vector3 viewOrigin = Task.View.Origin;
Float3 position = ViewPosition - viewOrigin;
+
+ // Use different logic in orthographic projection
+ if (_isOrtho)
+ {
+ var screenPosition = new Float2(mousePosition.X / viewport.Width - 0.5f, -mousePosition.Y / viewport.Height + 0.5f);
+ var orientation = ViewOrientation;
+ var direction = Float3.Forward * orientation;
+ var rayOrigin = new Vector3(screenPosition.X * viewport.Width * _orthoSize, screenPosition.Y * viewport.Height * _orthoSize, 0);
+ rayOrigin = position + Vector3.Transform(rayOrigin, orientation);
+ rayOrigin += direction * _nearPlane;
+ rayOrigin += viewOrigin;
+ return new Ray(rayOrigin, direction);
+ }
+
+ // Create view frustum
+ CreateProjectionMatrix(out var p);
CreateViewMatrix(position, out var v);
Matrix.Multiply(ref v, ref p, out var ivp);
ivp.Invert();
@@ -1404,11 +1424,7 @@ namespace FlaxEditor.Viewport
viewport.Unproject(ref nearPoint, ref ivp, out nearPoint);
viewport.Unproject(ref farPoint, ref ivp, out farPoint);
- // Create direction vector
- Vector3 direction = farPoint - nearPoint;
- direction.Normalize();
-
- return new Ray(nearPoint + viewOrigin, direction);
+ return new Ray(nearPoint + viewOrigin, Vector3.Normalize(farPoint - nearPoint));
}
///
@@ -1704,6 +1720,8 @@ namespace FlaxEditor.Viewport
// Update
moveDelta *= dt * (60.0f * 4.0f);
mouseDelta *= 0.1833f * MouseSpeed * _mouseSensitivity;
+ if (options.Viewport.InvertMouseYAxisRotation)
+ mouseDelta *= new Float2(1, -1);
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
// Move mouse back to the root position
diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs
index 6a6ea2cfa..acac4767f 100644
--- a/Source/Editor/Viewport/PrefabWindowViewport.cs
+++ b/Source/Editor/Viewport/PrefabWindowViewport.cs
@@ -627,8 +627,8 @@ namespace FlaxEditor.Viewport
}
// Debug draw all actors in prefab and collect actors
- var viewFlags = Task.ViewFlags;
- var collectActors = (viewFlags & ViewFlags.PhysicsDebug) != 0 || (viewFlags & ViewFlags.LightsDebug) != 0;
+ var view = Task.View;
+ var collectActors = (view.Flags & ViewFlags.PhysicsDebug) != 0 || view.Mode == ViewMode.PhysicsColliders || (view.Flags & ViewFlags.LightsDebug) != 0;
_debugDrawActors.Clear();
foreach (var child in SceneGraphRoot.ChildNodes)
{
@@ -641,19 +641,17 @@ namespace FlaxEditor.Viewport
}
// Draw physics debug
- if ((viewFlags & ViewFlags.PhysicsDebug) != 0)
+ if ((view.Flags & ViewFlags.PhysicsDebug) != 0 || view.Mode == ViewMode.PhysicsColliders)
{
foreach (var actor in _debugDrawActors)
{
if (actor is Collider c && c.IsActiveInHierarchy)
- {
DebugDraw.DrawColliderDebugPhysics(c, renderContext.View);
- }
}
}
// Draw lights debug
- if ((viewFlags & ViewFlags.LightsDebug) != 0)
+ if ((view.Flags & ViewFlags.LightsDebug) != 0)
{
foreach (var actor in _debugDrawActors)
{
diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs
index f6ca7e1b0..45627eaf2 100644
--- a/Source/Editor/Windows/Assets/AnimationWindow.cs
+++ b/Source/Editor/Windows/Assets/AnimationWindow.cs
@@ -80,6 +80,7 @@ namespace FlaxEditor.Windows.Assets
private AnimationWindow Window;
private Animation Asset;
private ModelImportSettings ImportSettings = new ModelImportSettings();
+ private bool EnablePreviewModelCache = true;
[EditorDisplay("Preview"), NoSerialize, AssetReference(true), Tooltip("The skinned model to preview the animation playback.")]
public SkinnedModel PreviewModel
@@ -134,6 +135,15 @@ namespace FlaxEditor.Windows.Assets
value.WaitForLoaded(500);
Window._preview.ViewportCamera.SetArcBallView(Window._preview.PreviewActor.Sphere);
}
+
+ if (EnablePreviewModelCache)
+ {
+ var customDataName = Window.GetPreviewModelCacheName();
+ if (value)
+ Window.Editor.ProjectCache.SetCustomData(customDataName, value.ID.ToString());
+ else
+ Window.Editor.ProjectCache.RemoveCustomData(customDataName);
+ }
}
}
@@ -142,6 +152,7 @@ namespace FlaxEditor.Windows.Assets
// Link
Window = window;
Asset = window.Asset;
+ EnablePreviewModelCache = true;
// Try to restore target asset import options (useful for fast reimport)
Editor.TryRestoreImportOptions(ref ImportSettings.Settings, window.Item.Path);
@@ -150,6 +161,7 @@ namespace FlaxEditor.Windows.Assets
public void OnClean()
{
// Unlink
+ EnablePreviewModelCache = false;
PreviewModel = null;
Window = null;
Asset = null;
@@ -287,12 +299,23 @@ namespace FlaxEditor.Windows.Assets
UpdateToolstrip();
}
+ private string GetPreviewModelCacheName()
+ {
+ return _asset.ID + ".PreviewModel";
+ }
+
///
protected override void OnAssetLoaded()
{
_properties.OnLoad(this);
_propertiesPresenter.BuildLayout();
ClearEditedFlag();
+ if (!_initialPreviewModel &&
+ Editor.ProjectCache.TryGetCustomData(GetPreviewModelCacheName(), out string str) &&
+ Guid.TryParse(str, out var id))
+ {
+ _initialPreviewModel = FlaxEngine.Content.LoadAsync(id);
+ }
if (_initialPreviewModel)
{
_properties.PreviewModel = _initialPreviewModel;
diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp
index 93e85f800..83907ec1f 100644
--- a/Source/Engine/Audio/AudioClip.cpp
+++ b/Source/Engine/Audio/AudioClip.cpp
@@ -396,6 +396,7 @@ void AudioClip::unload(bool isReloading)
}
StopStreaming();
+ CancelStreamingTasks();
StreamingQueue.Clear();
if (hasAnyBuffer && AudioBackend::Instance)
{
diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp
index 49a257790..2ae7756da 100644
--- a/Source/Engine/Debug/DebugDraw.cpp
+++ b/Source/Engine/Debug/DebugDraw.cpp
@@ -788,7 +788,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
Matrix vp;
Matrix::Multiply(view.View, view.Projection, vp);
Matrix::Transpose(vp, data.ViewProjection);
- data.ClipPosZBias = -0.2f; // Reduce Z-fighting artifacts (eg. editor grid)
+ data.ClipPosZBias = view.IsPerspectiveProjection() ? -0.2f : 0.0f; // Reduce Z-fighting artifacts (eg. editor grid)
data.EnableDepthTest = enableDepthTest;
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
@@ -953,11 +953,11 @@ void DebugDraw::DrawActorsTree(Actor* actor)
}
#if USE_EDITOR
+
void DebugDraw::DrawColliderDebugPhysics(Collider* collider, RenderView& view)
{
if (!collider)
return;
-
collider->DrawPhysicsDebug(view);
}
@@ -965,10 +965,11 @@ void DebugDraw::DrawLightDebug(Light* light, RenderView& view)
{
if (!light)
return;
-
light->DrawLightsDebug(view);
}
+
#endif
+
void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, float size, float duration, bool depthTest)
{
CHECK_DEBUG(direction.IsNormalized());
diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h
index 39eace7d2..9c58aa727 100644
--- a/Source/Engine/Debug/DebugDraw.h
+++ b/Source/Engine/Debug/DebugDraw.h
@@ -75,6 +75,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
///
/// The actor to start drawing at.
API_FUNCTION() static void DrawActorsTree(Actor* actor);
+
#if USE_EDITOR
///
/// Draws the physics debug shapes for the given collider. Editor Only
@@ -90,6 +91,7 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
/// The render view to draw in.
API_FUNCTION() static void DrawLightDebug(Light* light, RenderView& view);
#endif
+
///
/// Draws the lines axis from direction.
///
diff --git a/Source/Engine/Engine/InputAxis.cs b/Source/Engine/Engine/InputAxis.cs
index ca77d2465..a0c916e65 100644
--- a/Source/Engine/Engine/InputAxis.cs
+++ b/Source/Engine/Engine/InputAxis.cs
@@ -60,6 +60,7 @@ namespace FlaxEngine
~InputAxis()
{
Input.AxisValueChanged -= Handler;
+ ValueChanged = null;
}
///
@@ -68,6 +69,7 @@ namespace FlaxEngine
public void Dispose()
{
Input.AxisValueChanged -= Handler;
+ ValueChanged = null;
GC.SuppressFinalize(this);
}
diff --git a/Source/Engine/Engine/InputEvent.cs b/Source/Engine/Engine/InputEvent.cs
index 287e044f1..2728ce8da 100644
--- a/Source/Engine/Engine/InputEvent.cs
+++ b/Source/Engine/Engine/InputEvent.cs
@@ -70,6 +70,10 @@ namespace FlaxEngine
~InputEvent()
{
Input.ActionTriggered -= Handler;
+ Triggered = null;
+ Pressed = null;
+ Pressing = null;
+ Released = null;
}
private void Handler(string name, InputActionState state)
@@ -100,6 +104,10 @@ namespace FlaxEngine
public void Dispose()
{
Input.ActionTriggered -= Handler;
+ Triggered = null;
+ Pressed = null;
+ Pressing = null;
+ Released = null;
GC.SuppressFinalize(this);
}
diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp
index 0faa26f39..7d267f21b 100644
--- a/Source/Engine/Input/Input.cpp
+++ b/Source/Engine/Input/Input.cpp
@@ -157,6 +157,8 @@ void InputSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* m
config.Gamepad = JsonTools::GetEnum(v, "Gamepad", InputGamepadIndex::All);
config.PositiveButton = JsonTools::GetEnum(v, "PositiveButton", KeyboardKeys::None);
config.NegativeButton = JsonTools::GetEnum(v, "NegativeButton", KeyboardKeys::None);
+ config.GamepadPositiveButton = JsonTools::GetEnum(v, "GamepadPositiveButton", GamepadButton::None);
+ config.GamepadNegativeButton = JsonTools::GetEnum(v, "GamepadNegativeButton", GamepadButton::None);
config.DeadZone = JsonTools::GetFloat(v, "DeadZone", 0.1f);
config.Sensitivity = JsonTools::GetFloat(v, "Sensitivity", 0.4f);
config.Gravity = JsonTools::GetFloat(v, "Gravity", 1.0f);
@@ -873,8 +875,8 @@ void InputService::Update()
const AxisData& data = Axes[name];
// Get key raw value
- const bool isPositiveKey = Input::GetKey(config.PositiveButton);
- const bool isNegativeKey = Input::GetKey(config.NegativeButton);
+ const bool isPositiveKey = Input::GetKey(config.PositiveButton) || Input::GetGamepadButton(config.Gamepad, config.GamepadPositiveButton);
+ const bool isNegativeKey = Input::GetKey(config.NegativeButton) || Input::GetGamepadButton(config.Gamepad, config.GamepadNegativeButton);
float keyRawValue = 0;
if (isPositiveKey && !isNegativeKey)
{
diff --git a/Source/Engine/Input/VirtualInput.h b/Source/Engine/Input/VirtualInput.h
index 3028fc661..1adad2056 100644
--- a/Source/Engine/Input/VirtualInput.h
+++ b/Source/Engine/Input/VirtualInput.h
@@ -87,6 +87,18 @@ API_STRUCT() struct AxisConfig
API_FIELD(Attributes="EditorOrder(40)")
KeyboardKeys NegativeButton;
+ ///
+ /// The button to be pressed for movement in positive direction. Use to ignore it.
+ ///
+ API_FIELD(Attributes="EditorOrder(45)")
+ GamepadButton GamepadPositiveButton;
+
+ ///
+ /// The button to be pressed for movement in negative direction. Use to ignore it.
+ ///
+ API_FIELD(Attributes="EditorOrder(46)")
+ GamepadButton GamepadNegativeButton;
+
///
/// Any positive or negative values that are less than this number will register as zero. Useful for gamepads to specify the deadzone.
///
diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp
index 675d57664..29b980cbe 100644
--- a/Source/Engine/Level/Actors/Camera.cpp
+++ b/Source/Engine/Level/Actors/Camera.cpp
@@ -39,6 +39,7 @@ Camera::Camera(const SpawnParams& params)
, _customAspectRatio(0.0f)
, _near(10.0f)
, _far(40000.0f)
+ , _orthoSize(0.0f)
, _orthoScale(1.0f)
{
#if USE_EDITOR
@@ -120,6 +121,21 @@ void Camera::SetFarPlane(float value)
}
}
+float Camera::GetOrthographicSize() const
+{
+ return _orthoSize;
+}
+
+void Camera::SetOrthographicSize(float value)
+{
+ value = Math::Clamp(value, 0.0f, 1000000.0f);
+ if (Math::NotNearEqual(_orthoSize, value))
+ {
+ _orthoSize = value;
+ UpdateCache();
+ }
+}
+
float Camera::GetOrthographicScale() const
{
return _orthoScale;
@@ -191,39 +207,37 @@ Ray Camera::ConvertMouseToRay(const Float2& mousePosition) const
Ray Camera::ConvertMouseToRay(const Float2& mousePosition, const Viewport& viewport) const
{
-#if 1
- // Gather camera properties
+ Vector3 position = GetPosition();
+ if (viewport.Width < ZeroTolerance || viewport.Height < ZeroTolerance)
+ return Ray(position, GetDirection());
+
+ // Use different logic in orthographic projection
+ if (!_usePerspective)
+ {
+ Float2 screenPosition(mousePosition.X / viewport.Width - 0.5f, -mousePosition.Y / viewport.Height + 0.5f);
+ Quaternion orientation = GetOrientation();
+ Float3 direction = orientation * Float3::Forward;
+ const float orthoSize = (_orthoSize > 0.0f ? _orthoSize : viewport.Height) * _orthoScale;
+ const float aspect = _customAspectRatio <= 0.0f ? viewport.GetAspectRatio() : _customAspectRatio;
+ Vector3 rayOrigin(screenPosition.X * orthoSize * aspect, screenPosition.Y * orthoSize, 0);
+ rayOrigin = position + Vector3::Transform(rayOrigin, orientation);
+ rayOrigin += direction * _near;
+ return Ray(rayOrigin, direction);
+ }
+
+ // Create view frustum
Matrix v, p, ivp;
GetMatrices(v, p, viewport);
Matrix::Multiply(v, p, ivp);
ivp.Invert();
// Create near and far points
- Vector3 nearPoint(mousePosition, 0.0f);
- Vector3 farPoint(mousePosition, 1.0f);
+ Vector3 nearPoint(mousePosition, _near);
+ Vector3 farPoint(mousePosition, _far);
viewport.Unproject(nearPoint, ivp, nearPoint);
viewport.Unproject(farPoint, ivp, farPoint);
- // Create direction vector
- Vector3 direction = farPoint - nearPoint;
- direction.Normalize();
-
- return Ray(nearPoint, direction);
-#else
- // Create near and far points
- Vector3 nearPoint, farPoint;
- Matrix ivp;
- _frustum.GetInvMatrix(&ivp);
- viewport.Unproject(Vector3(mousePosition, 0.0f), ivp, nearPoint);
- viewport.Unproject(Vector3(mousePosition, 1.0f), ivp, farPoint);
-
- // Create direction vector
- Vector3 direction = farPoint - nearPoint;
- direction.Normalize();
-
- // Return result
- return Ray(nearPoint, direction);
-#endif
+ return Ray(nearPoint, Vector3::Normalize(farPoint - nearPoint));
}
Viewport Camera::GetViewport() const
@@ -264,14 +278,15 @@ void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewp
void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewport, const Vector3& origin) const
{
// Create projection matrix
+ const float aspect = _customAspectRatio <= 0.0f ? viewport.GetAspectRatio() : _customAspectRatio;
if (_usePerspective)
{
- const float aspect = _customAspectRatio <= 0.0f ? viewport.GetAspectRatio() : _customAspectRatio;
Matrix::PerspectiveFov(_fov * DegreesToRadians, aspect, _near, _far, projection);
}
else
{
- Matrix::Ortho(viewport.Width * _orthoScale, viewport.Height * _orthoScale, _near, _far, projection);
+ const float orthoSize = (_orthoSize > 0.0f ? _orthoSize : viewport.Height) * _orthoScale;
+ Matrix::Ortho(orthoSize * aspect, orthoSize, _near, _far, projection);
}
// Create view matrix
diff --git a/Source/Engine/Level/Actors/Camera.h b/Source/Engine/Level/Actors/Camera.h
index 190126e54..7da37c4b6 100644
--- a/Source/Engine/Level/Actors/Camera.h
+++ b/Source/Engine/Level/Actors/Camera.h
@@ -44,6 +44,7 @@ private:
float _customAspectRatio;
float _near;
float _far;
+ float _orthoSize;
float _orthoScale;
#if USE_EDITOR
@@ -88,7 +89,7 @@ public:
///
/// Gets the custom aspect ratio. 0 if not use custom value.
///
- API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(0.0f), Limit(0, 10, 0.01f), EditorDisplay(\"Camera\"), VisibleIf(nameof(UsePerspective))")
+ API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(0.0f), Limit(0, 10, 0.01f), EditorDisplay(\"Camera\")")
float GetCustomAspectRatio() const;
///
@@ -118,6 +119,17 @@ public:
///
API_PROPERTY() void SetFarPlane(float value);
+ ///
+ /// Gets the orthographic projection view height (width is based on the aspect ratio). Use `0` for size to be based on the viewport size.
+ ///
+ API_PROPERTY(Attributes="EditorOrder(59), DefaultValue(0.0f), Limit(0.0f), EditorDisplay(\"Camera\"), VisibleIf(nameof(UsePerspective), true)")
+ float GetOrthographicSize() const;
+
+ ///
+ /// Sets the orthographic projection view height (width is based on the aspect ratio). Use `0` for size to be based on the viewport size.
+ ///
+ API_PROPERTY() void SetOrthographicSize(float value);
+
///
/// Gets the orthographic projection scale.
///
diff --git a/Source/Engine/Level/Level.h b/Source/Engine/Level/Level.h
index 4ae1e5bae..b9c6b698a 100644
--- a/Source/Engine/Level/Level.h
+++ b/Source/Engine/Level/Level.h
@@ -123,6 +123,7 @@ public:
/// The scene object (loaded).
API_FUNCTION() static Scene* GetScene(int32 index)
{
+ CHECK_RETURN(index < GetScenesCount(), nullptr);
return Scenes[index];
}
diff --git a/Source/Engine/Particles/ParticleEmitter.cpp b/Source/Engine/Particles/ParticleEmitter.cpp
index eaeb92922..570b026c6 100644
--- a/Source/Engine/Particles/ParticleEmitter.cpp
+++ b/Source/Engine/Particles/ParticleEmitter.cpp
@@ -219,7 +219,7 @@ Asset::LoadResult ParticleEmitter::load()
ClearDependencies();
for (const auto& node : Graph.Nodes)
{
- if (node.Type == GRAPH_NODE_MAKE_TYPE(14, 300))
+ if (node.Type == GRAPH_NODE_MAKE_TYPE(14, 300) && node.Assets.Count() > 0)
{
const auto function = node.Assets[0].As();
if (function)
diff --git a/Source/Engine/UI/GUI/Common/ProgressBar.cs b/Source/Engine/UI/GUI/Common/ProgressBar.cs
index 4852865bf..67f521c4a 100644
--- a/Source/Engine/UI/GUI/Common/ProgressBar.cs
+++ b/Source/Engine/UI/GUI/Common/ProgressBar.cs
@@ -143,9 +143,11 @@ namespace FlaxEngine.GUI
if (!Mathf.NearEqual(value, _value))
{
_value = value;
- if (!UseSmoothing)
+ if (!UseSmoothing || _firstUpdate)
{
_current = _value;
+ if (_firstUpdate)
+ _firstUpdate = false;
}
}
}
@@ -169,6 +171,9 @@ namespace FlaxEngine.GUI
[EditorDisplay("Bar Style"), EditorOrder(2012), Tooltip("The brush used for progress bar drawing.")]
public IBrush BarBrush { get; set; }
+ // Used to remove initial lerp from the value on play.
+ private bool _firstUpdate = true;
+
///
/// Initializes a new instance of the class.
///
diff --git a/Source/Engine/UI/SpriteRender.cpp b/Source/Engine/UI/SpriteRender.cpp
index c7b31f358..4f28551b7 100644
--- a/Source/Engine/UI/SpriteRender.cpp
+++ b/Source/Engine/UI/SpriteRender.cpp
@@ -18,7 +18,7 @@ SpriteRender::SpriteRender(const SpawnParams& params)
{
_quadModel = Content::LoadAsyncInternal(TEXT("Engine/Models/Quad"));
Material.Loaded.Bind(this);
- Image.Changed.Bind(this);
+ Image.Changed.Bind(this);
}
Float2 SpriteRender::GetSize() const
@@ -42,8 +42,7 @@ Color SpriteRender::GetColor() const
void SpriteRender::SetColor(const Color& value)
{
_color = value;
- if (_paramColor)
- _paramColor->SetValue(value);
+ SetColorParam();
}
SpriteHandle SpriteRender::GetSprite() const
@@ -54,7 +53,7 @@ SpriteHandle SpriteRender::GetSprite() const
void SpriteRender::SetSprite(const SpriteHandle& value)
{
_sprite = value;
- SetImage();
+ SetImageParam();
}
void SpriteRender::OnMaterialLoaded()
@@ -66,6 +65,7 @@ void SpriteRender::OnMaterialLoaded()
_materialInstance->AddReference();
}
_materialInstance->SetBaseMaterial(Material);
+ _materialInstance->ResetParameters();
// Cache parameters
_paramImageMAD = _materialInstance->GetParameter(TEXT("ImageMAD"));
@@ -75,15 +75,15 @@ void SpriteRender::OnMaterialLoaded()
if (_paramImage && _paramImage->GetParameterType() != MaterialParameterType::Texture)
_paramImage = nullptr;
else if (_paramImage)
- SetImage();
+ SetImageParam();
_paramColor = _materialInstance->GetParameter(TEXT("Color"));
if (_paramColor && _paramColor->GetParameterType() != MaterialParameterType::Color && _paramColor->GetParameterType() != MaterialParameterType::Vector4 && _paramColor->GetParameterType() != MaterialParameterType::Vector3)
_paramColor = nullptr;
else if (_paramColor)
- _paramColor->SetValue(_color);
+ SetColorParam();
}
-void SpriteRender::SetImage()
+void SpriteRender::SetImageParam()
{
TextureBase* image = Image.Get();
Vector4 imageMAD(Vector2::One, Vector2::Zero);
@@ -94,9 +94,24 @@ void SpriteRender::SetImage()
imageMAD = Vector4(sprite->Area.Size, sprite->Area.Location);
}
if (_paramImage)
+ {
_paramImage->SetValue(image);
+ _paramImage->SetIsOverride(true);
+ }
if (_paramImageMAD)
+ {
_paramImageMAD->SetValue(imageMAD);
+ _paramImageMAD->SetIsOverride(true);
+ }
+}
+
+void SpriteRender::SetColorParam()
+{
+ if (_paramColor)
+ {
+ _paramColor->SetValue(_color);
+ _paramColor->SetIsOverride(true);
+ }
}
bool SpriteRender::HasContentLoaded() const
@@ -164,9 +179,8 @@ void SpriteRender::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
DESERIALIZE(DrawModes);
DESERIALIZE(SortOrder);
- SetImage();
- if (_paramColor)
- _paramColor->SetValue(_color);
+ SetImageParam();
+ SetColorParam();
}
void SpriteRender::OnLayerChanged()
diff --git a/Source/Engine/UI/SpriteRender.h b/Source/Engine/UI/SpriteRender.h
index bd7294c31..9bc0bf1dc 100644
--- a/Source/Engine/UI/SpriteRender.h
+++ b/Source/Engine/UI/SpriteRender.h
@@ -92,7 +92,8 @@ public:
private:
void OnMaterialLoaded();
- void SetImage();
+ void SetImageParam();
+ void SetColorParam();
public:
// [Actor]
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
index 68226786e..74c544ae1 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
@@ -2064,13 +2064,18 @@ namespace Flax.Build.Bindings
GenerateCSharpAttributes(buildData, contents, indent, enumInfo, true);
contents.Append(indent).Append(GenerateCSharpAccessLevel(enumInfo.Access));
contents.Append("enum ").Append(enumInfo.Name);
+ string managedType = string.Empty;
if (enumInfo.UnderlyingType != null)
- contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, enumInfo.UnderlyingType, enumInfo));
+ {
+ managedType = GenerateCSharpNativeToManaged(buildData, enumInfo.UnderlyingType, enumInfo);
+ contents.Append(" : ").Append(managedType);
+ }
contents.AppendLine();
contents.Append(indent + "{");
indent += " ";
// Entries
+ bool usedMax = false;
foreach (var entryInfo in enumInfo.Entries)
{
contents.AppendLine();
@@ -2078,7 +2083,29 @@ namespace Flax.Build.Bindings
GenerateCSharpAttributes(buildData, contents, indent, enumInfo, entryInfo.Attributes, entryInfo.Comment, true, false);
contents.Append(indent).Append(entryInfo.Name);
if (!string.IsNullOrEmpty(entryInfo.Value))
- contents.Append(" = ").Append(entryInfo.Value);
+ {
+ if (usedMax)
+ usedMax = false;
+
+ string value;
+ if (enumInfo.UnderlyingType != null)
+ {
+ value = GenerateCSharpDefaultValueNativeToManaged(buildData, entryInfo.Value, enumInfo, enumInfo.UnderlyingType, false, managedType);
+ if (value.Contains($"{managedType}.MaxValue", StringComparison.Ordinal))
+ usedMax = true;
+ }
+ else
+ {
+ value = entryInfo.Value;
+ }
+ contents.Append(" = ").Append(value);
+ }
+ // Handle case of next value after Max value being zero if a value is not defined.
+ else if (string.IsNullOrEmpty(entryInfo.Value) && usedMax)
+ {
+ contents.Append(" = ").Append('0');
+ usedMax = false;
+ }
contents.Append(',');
contents.AppendLine();
}