Merge remote-tracking branch 'origin/master' into 1.6

This commit is contained in:
Wojtek Figat
2023-06-10 23:29:22 +02:00
21 changed files with 438 additions and 45 deletions

View File

@@ -408,6 +408,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
private Type _cachedType;
private bool _anchorDropDownClosed = true;
private Button _pivotRelativeButton;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -484,13 +485,54 @@ namespace FlaxEditor.CustomEditors.Dedicated
horDown.CustomControl.Height = TextBoxBase.DefaultHeight;
GetAnchorEquality(out _cachedXEq, out _cachedYEq, valueTypes);
BuildLocationSizeOffsets(horUp, horDown, _cachedXEq, _cachedYEq, valueTypes);
BuildExtraButtons(group);
main.Space(10);
BuildAnchorsDropper(main, valueTypes);
}
private void BuildExtraButtons(VerticalPanelElement group)
{
var control = (Control)Values[0];
var pivotRelative = Editor.Instance.Windows.PropertiesWin.UIPivotRelative;
control.PivotRelative = pivotRelative;
var panel = group.CustomContainer<Panel>();
panel.CustomControl.Height = TextBoxBase.DefaultHeight;
panel.CustomControl.ClipChildren = false;
panel.CustomControl.Parent = group.ContainerControl;
_pivotRelativeButton = new Button
{
TooltipText = "Toggles UI control resizing based on where the pivot is rather than just the top-left.",
Size = new Float2(18),
Parent = panel.ContainerControl,
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Scale32),
AnchorPreset = AnchorPresets.TopRight,
X = 77,
};
SetStyle(pivotRelative);
_pivotRelativeButton.Clicked += PivotRelativeClicked;
}
private void PivotRelativeClicked()
{
var control = (Control)Values[0];
var pivotRelative = control.PivotRelative;
control.PivotRelative = !pivotRelative;
Editor.Instance.Windows.PropertiesWin.UIPivotRelative = !pivotRelative;
SetStyle(control.PivotRelative);
}
private void SetStyle(bool current)
{
var style = FlaxEngine.GUI.Style.Current;
var backgroundColor = current ? style.Foreground : style.ForegroundDisabled;
_pivotRelativeButton.SetColors(backgroundColor);
}
private void BuildAnchorsDropper(LayoutElementsContainer main, ScriptType[] valueTypes)
{
ScriptMemberInfo minInfo = valueTypes[0].GetProperty("AnchorMin");

View File

@@ -97,6 +97,7 @@ namespace FlaxEditor.CustomEditors.Editors
AnchorPreset = AnchorPresets.TopLeft,
};
_linkButton.Clicked += ToggleLink;
ToggleEnabled();
SetLinkStyle();
var x = LinkedLabel.Text.Value.Length * 7 + 5;
_linkButton.LocalX += x;
@@ -128,9 +129,38 @@ namespace FlaxEditor.CustomEditors.Editors
{
LinkValues = !LinkValues;
Editor.Instance.Windows.PropertiesWin.ScaleLinked = LinkValues;
ToggleEnabled();
SetLinkStyle();
}
/// <summary>
/// Toggles enables on value boxes.
/// </summary>
public void ToggleEnabled()
{
if (LinkValues)
{
if (Mathf.NearEqual(((Float3)Values[0]).X, 0))
{
XElement.ValueBox.Enabled = false;
}
if (Mathf.NearEqual(((Float3)Values[0]).Y, 0))
{
YElement.ValueBox.Enabled = false;
}
if (Mathf.NearEqual(((Float3)Values[0]).Z, 0))
{
ZElement.ValueBox.Enabled = false;
}
}
else
{
XElement.ValueBox.Enabled = true;
YElement.ValueBox.Enabled = true;
ZElement.ValueBox.Enabled = true;
}
}
private void SetLinkStyle()
{
var style = FlaxEngine.GUI.Style.Current;

View File

@@ -129,25 +129,39 @@ namespace FlaxEditor.CustomEditors.Editors
if (LinkValues)
{
var valueChange = 0.0f;
var valueRatio = 0.0f;
switch (_valueChanged)
{
case ValueChanged.X:
valueChange = xValue - ((Float3)Values[0]).X;
yValue += valueChange;
zValue += valueChange;
valueRatio = GetRatio(xValue, ((Float3)Values[0]).X);
if (Mathf.NearEqual(valueRatio, 0))
{
XElement.ValueBox.Enabled = false;
valueRatio = 1;
}
yValue = NewLinkedValue(yValue, valueRatio);
zValue = NewLinkedValue(zValue, valueRatio);
break;
case ValueChanged.Y:
valueChange = yValue - ((Float3)Values[0]).Y;
xValue += valueChange;
zValue += valueChange;
valueRatio = GetRatio(yValue, ((Float3)Values[0]).Y);
if (Mathf.NearEqual(valueRatio, 0))
{
YElement.ValueBox.Enabled = false;
valueRatio = 1;
}
xValue = NewLinkedValue(xValue, valueRatio);
zValue = NewLinkedValue(zValue, valueRatio);
break;
case ValueChanged.Z:
valueChange = zValue - ((Float3)Values[0]).Z;
xValue += valueChange;
yValue += valueChange;
valueRatio = GetRatio(zValue, ((Float3)Values[0]).Z);
if (Mathf.NearEqual(valueRatio, 0))
{
ZElement.ValueBox.Enabled = false;
valueRatio = 1;
}
xValue = NewLinkedValue(xValue, valueRatio);
yValue = NewLinkedValue(yValue, valueRatio);
break;
default: break;
}
}
@@ -164,6 +178,16 @@ namespace FlaxEditor.CustomEditors.Editors
SetValue(v, token);
}
private float GetRatio(float value, float initialValue)
{
return Mathf.NearEqual(initialValue, 0) ? 0 : value / initialValue;
}
private float NewLinkedValue(float value, float valueRatio)
{
return Mathf.NearEqual(value, 0) ? value : value * valueRatio;
}
/// <inheritdoc />
public override void Refresh()
{

View File

@@ -81,6 +81,7 @@ namespace FlaxEditor.Gizmo
: base(owner)
{
InitDrawing();
ModeChanged += ResetTranslationScale;
}
/// <summary>
@@ -326,6 +327,11 @@ namespace FlaxEditor.Gizmo
}
}
private void ResetTranslationScale()
{
_translationScaleSnapDelta.Normalize();
}
private void UpdateRotate(float dt)
{
float mouseDelta = _activeAxis == Axis.Y ? -Owner.MouseDelta.X : Owner.MouseDelta.X;

View File

@@ -146,7 +146,18 @@ namespace FlaxEditor.SceneGraph.Actors
Time = newTime,
Value = actor.GetSplineLocalTransform(Index),
};
var oldkeyframe = actor.GetSplineKeyframe(Index);
var newKeyframe = new BezierCurve<Transform>.Keyframe();
// copy old curve point data to new curve point
newKeyframe.Value = oldkeyframe.Value;
newKeyframe.TangentIn = oldkeyframe.TangentIn;
newKeyframe.TangentOut = oldkeyframe.TangentOut;
actor.InsertSplineLocalPoint(newIndex, newTime, action.Value);
actor.SetSplineKeyframe(newIndex, newKeyframe);
undoAction = action;
var splineNode = (SplineNode)SceneGraphFactory.FindNode(action.SplineId);
splineNode.OnUpdate();
@@ -317,6 +328,20 @@ namespace FlaxEditor.SceneGraph.Actors
var spline = (Spline)Actor;
spline.AddSplineLocalPoint(Vector3.Zero, false);
spline.AddSplineLocalPoint(new Vector3(0, 0, 100.0f));
spline.SetSplineKeyframe(0, new BezierCurve<Transform>.Keyframe()
{
Value = new Transform(Vector3.Zero, Quaternion.Identity, Vector3.One),
TangentIn = new Transform(Vector3.Backward * 100, Quaternion.Identity, Vector3.One),
TangentOut = new Transform(Vector3.Forward * 100, Quaternion.Identity, Vector3.One),
});
spline.SetSplineKeyframe(1, new BezierCurve<Transform>.Keyframe()
{
Value = new Transform(Vector3.Forward * 100, Quaternion.Identity, Vector3.One),
TangentIn = new Transform(Vector3.Backward * 100, Quaternion.Identity, Vector3.One),
TangentOut = new Transform(Vector3.Forward * 100, Quaternion.Identity, Vector3.One),
});
}
/// <inheritdoc />

View File

@@ -30,6 +30,11 @@ namespace FlaxEditor.Windows
/// </summary>
public bool ScaleLinked = false;
/// <summary>
/// Indication of if UI elements should size relative to the pivot point.
/// </summary>
public bool UIPivotRelative = true;
/// <summary>
/// Initializes a new instance of the <see cref="PropertiesWindow"/> class.
/// </summary>
@@ -66,13 +71,16 @@ namespace FlaxEditor.Windows
public override void OnLayoutSerialize(XmlWriter writer)
{
writer.WriteAttributeString("ScaleLinked", ScaleLinked.ToString());
writer.WriteAttributeString("UIPivotRelative", UIPivotRelative.ToString());
}
/// <inheritdoc />
public override void OnLayoutDeserialize(XmlElement node)
{
if (bool.TryParse(node.GetAttribute("ScaleLinked"), out bool value1))
ScaleLinked = value1;
if (bool.TryParse(node.GetAttribute("UIPivotRelative"), out value1))
UIPivotRelative = value1;
}
}
}

View File

@@ -85,18 +85,18 @@ Keyboard* Input::Keyboard = nullptr;
Array<Gamepad*, FixedAllocation<MAX_GAMEPADS>> Input::Gamepads;
Action Input::GamepadsChanged;
Array<InputDevice*, InlinedAllocation<16>> Input::CustomDevices;
Input::CharDelegate Input::CharInput;
Input::KeyboardDelegate Input::KeyDown;
Input::KeyboardDelegate Input::KeyUp;
Input::MouseButtonDelegate Input::MouseDown;
Input::MouseButtonDelegate Input::MouseUp;
Input::MouseButtonDelegate Input::MouseDoubleClick;
Input::MouseWheelDelegate Input::MouseWheel;
Input::MouseDelegate Input::MouseMove;
Delegate<Char> Input::CharInput;
Delegate<KeyboardKeys> Input::KeyDown;
Delegate<KeyboardKeys> Input::KeyUp;
Delegate<const Float2&, MouseButton> Input::MouseDown;
Delegate<const Float2&, MouseButton> Input::MouseUp;
Delegate<const Float2&, MouseButton> Input::MouseDoubleClick;
Delegate<const Float2&, float> Input::MouseWheel;
Delegate<const Float2&> Input::MouseMove;
Action Input::MouseLeave;
Input::TouchDelegate Input::TouchDown;
Input::TouchDelegate Input::TouchMove;
Input::TouchDelegate Input::TouchUp;
Delegate<const Float2&, int32> Input::TouchDown;
Delegate<const Float2&, int32> Input::TouchMove;
Delegate<const Float2&, int32> Input::TouchUp;
Delegate<StringView> Input::ActionTriggered;
Array<ActionConfig> Input::ActionMappings;
Array<AxisConfig> Input::AxisMappings;

View File

@@ -66,72 +66,66 @@ API_CLASS(Static) class FLAXENGINE_API Input
static Array<InputDevice*, InlinedAllocation<16>> CustomDevices;
public:
typedef Delegate<Char> CharDelegate;
typedef Delegate<KeyboardKeys> KeyboardDelegate;
typedef Delegate<const Float2&> MouseDelegate;
typedef Delegate<const Float2&, MouseButton> MouseButtonDelegate;
typedef Delegate<const Float2&, float> MouseWheelDelegate;
typedef Delegate<const Float2&, int32> TouchDelegate;
/// <summary>
/// Event fired on character input.
/// </summary>
static CharDelegate CharInput;
API_EVENT() static Delegate<Char> CharInput;
/// <summary>
/// Event fired on key pressed.
/// </summary>
static KeyboardDelegate KeyDown;
API_EVENT() static Delegate<KeyboardKeys> KeyDown;
/// <summary>
/// Event fired on key released.
/// </summary>
static KeyboardDelegate KeyUp;
API_EVENT() static Delegate<KeyboardKeys> KeyUp;
/// <summary>
/// Event fired when mouse button goes down.
/// </summary>
static MouseButtonDelegate MouseDown;
API_EVENT() static Delegate<const Float2&, MouseButton> MouseDown;
/// <summary>
/// Event fired when mouse button goes up.
/// </summary>
static MouseButtonDelegate MouseUp;
API_EVENT() static Delegate<const Float2&, MouseButton> MouseUp;
/// <summary>
/// Event fired when mouse button double clicks.
/// </summary>
static MouseButtonDelegate MouseDoubleClick;
API_EVENT() static Delegate<const Float2&, MouseButton> MouseDoubleClick;
/// <summary>
/// Event fired when mouse wheel is scrolling (wheel delta is normalized).
/// </summary>
static MouseWheelDelegate MouseWheel;
API_EVENT() static Delegate<const Float2&, float> MouseWheel;
/// <summary>
/// Event fired when mouse moves.
/// </summary>
static MouseDelegate MouseMove;
API_EVENT() static Delegate<const Float2&> MouseMove;
/// <summary>
/// Event fired when mouse leaves window.
/// </summary>
static Action MouseLeave;
API_EVENT() static Action MouseLeave;
/// <summary>
/// Event fired when touch action begins.
/// </summary>
static TouchDelegate TouchDown;
API_EVENT() static Delegate<const Float2&, int32> TouchDown;
/// <summary>
/// Event fired when touch action moves.
/// </summary>
static TouchDelegate TouchMove;
API_EVENT() static Delegate<const Float2&, int32> TouchMove;
/// <summary>
/// Event fired when touch action ends.
/// </summary>
static TouchDelegate TouchUp;
API_EVENT() static Delegate<const Float2&, int32> TouchUp;
public:
/// <summary>

View File

@@ -1323,6 +1323,20 @@ Actor* Actor::FindActor(const MClass* type) const
return nullptr;
}
Actor* Actor::FindActor(const MClass* type, const StringView& name) const
{
CHECK_RETURN(type, nullptr);
if (GetClass()->IsSubClassOf(type) && StringUtils::Compare(*_name, *name) == 0)
return const_cast<Actor*>(this);
for (auto child : Children)
{
const auto actor = child->FindActor(type, name);
if (actor)
return actor;
}
return nullptr;
}
Script* Actor::FindScript(const MClass* type) const
{
CHECK_RETURN(type, nullptr);

View File

@@ -259,6 +259,17 @@ namespace FlaxEngine
return FindActor(typeof(T)) as T;
}
/// <summary>
/// Tries to find the actor of the given type and name in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>
/// <param name="name">Name of the object.</param>
/// <typeparam name="T">Type of the object.</typeparam>
/// <returns>Actor instance if found, null otherwise.</returns>
public T FindActor<T>(string name) where T : Actor
{
return FindActor(typeof(T), name) as T;
}
/// <summary>
/// Searches for all actors of a specific type in this actor children list.
/// </summary>

View File

@@ -734,6 +734,14 @@ public:
/// <returns>Actor instance if found, null otherwise.</returns>
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type) const;
/// <summary>
/// Tries to find the actor of the given type and name in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <param name="name">The name of the actor.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name) const;
/// <summary>
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>
@@ -744,6 +752,17 @@ public:
return (T*)FindActor(T::GetStaticClass());
}
/// <summary>
/// Tries to find the actor of the given type and name in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>
/// <param name="name">The name of the actor.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
template<typename T>
FORCE_INLINE T* FindActor(const StringView& name) const
{
return (T*)FindActor(T::GetStaticClass(), name);
}
/// <summary>
/// Tries to find the script of the given type in this actor hierarchy (checks this actor and all children hierarchy).
/// </summary>

View File

@@ -748,6 +748,20 @@ void FindActorsRecursive(Actor* node, const Tag& tag, Array<Actor*>& result)
FindActorsRecursive(child, tag, result);
}
void FindActorsRecursiveByParentTags(Actor* node, const Array<Tag>& tags, Array<Actor*>& result)
{
for (Tag tag : tags)
{
if (node->HasTag(tag))
{
result.Add(node);
break;
}
}
for (Actor* child : node->Children)
FindActorsRecursiveByParentTags(child, tags, result);
}
Actor* Level::FindActor(const Tag& tag, Actor* root)
{
PROFILE_CPU();
@@ -781,12 +795,43 @@ Array<Actor*> Level::FindActors(const Tag& tag, Actor* root)
}
else
{
ScopeLock lock(ScenesLock);
for (Scene* scene : Scenes)
FindActorsRecursive(scene, tag, result);
}
return result;
}
Array<Actor*> Level::FindActorsByParentTag(const Tag& parentTag, Actor* root)
{
PROFILE_CPU();
Array<Actor*> result;
const Array<Tag> subTags = Tags::GetSubTags(parentTag);
if (subTags.Count() == 0)
{
return result;
}
if (subTags.Count() == 1)
{
result = FindActors(subTags[0], root);
return result;
}
if (root)
{
FindActorsRecursiveByParentTags(root, subTags, result);
}
else
{
ScopeLock lock(ScenesLock);
for (Scene* scene : Scenes)
FindActorsRecursiveByParentTags(scene, subTags, result);
}
return result;
}
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
{
PROFILE_CPU();
@@ -1266,7 +1311,7 @@ bool Level::SaveSceneToBytes(Scene* scene, rapidjson_flax::StringBuffer& outData
}
// Info
LOG(Info, "Scene saved! Time {0} ms", Math::CeilToInt(static_cast<float>((DateTime::NowUTC()- startTime).GetTotalMilliseconds())));
LOG(Info, "Scene saved! Time {0} ms", Math::CeilToInt(static_cast<float>((DateTime::NowUTC() - startTime).GetTotalMilliseconds())));
// Fire event
CallSceneEvent(SceneEventType::OnSceneSaved, scene, scene->GetID());
@@ -1446,6 +1491,16 @@ Actor* Level::FindActor(const MClass* type)
return result;
}
Actor* Level::FindActor(const MClass* type, const StringView& name)
{
CHECK_RETURN(type, nullptr);
Actor* result = nullptr;
ScopeLock lock(ScenesLock);
for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++)
result = Scenes[i]->FindActor(type, name);
return result;
}
Script* Level::FindScript(const MClass* type)
{
CHECK_RETURN(type, nullptr);

View File

@@ -66,6 +66,17 @@ namespace FlaxEngine
{
return FindActor(typeof(T)) as T;
}
/// <summary>
/// Tries to find actor of the given type and name in all loaded scenes.
/// </summary>
/// <param name="name">Name of the object.</param>
/// <typeparam name="T">Type of the object.</typeparam>
/// <returns>Found actor or null.</returns>
public static T FindActor<T>(string name) where T : Actor
{
return FindActor(typeof(T), name) as T;
}
/// <summary>
/// Tries to find actor with the given ID in all loaded scenes. It's very fast O(1) lookup.

View File

@@ -363,6 +363,14 @@ public:
/// <returns>Found actor or null.</returns>
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type);
/// <summary>
/// Tries to find the actor of the given type and name in all the loaded scenes.
/// </summary>
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
/// <param name="name">The name of the actor.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name);
/// <summary>
/// Tries to find the actor of the given type in all the loaded scenes.
/// </summary>
@@ -373,6 +381,17 @@ public:
return (T*)FindActor(T::GetStaticClass());
}
/// <summary>
/// Tries to find the actor of the given type and name in all the loaded scenes.
/// </summary>
/// <param name="name">The name of the actor.</param>
/// <returns>Actor instance if found, null otherwise.</returns>
template<typename T>
FORCE_INLINE static T* FindActor(const StringView& name)
{
return (T*)FindActor(T::GetStaticClass(), name);
}
/// <summary>
/// Tries to find the script of the given type in all the loaded scenes.
/// </summary>
@@ -479,6 +498,14 @@ public:
/// <returns>Found actors or empty if none.</returns>
API_FUNCTION() static Array<Actor*> FindActors(const Tag& tag, Actor* root = nullptr);
/// <summary>
/// Search actors using a parent parentTag.
/// </summary>
/// <param name="parentTag">The tag to search actors with subtags belonging to this tag</param>
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
/// <returns>Returns all actors that have subtags belonging to the given parent parentTag</returns>
API_FUNCTION() static Array<Actor*> FindActorsByParentTag(const Tag& parentTag, Actor* root = nullptr);
private:
// Actor API
enum class ActorEventType

View File

@@ -127,7 +127,7 @@ public:
/// <summary>
/// Gets the character up vector.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(240), DefaultValue(typeof(Vector3), \"0,1,0\"), EditorDisplay(\"Character Controller\")")
API_PROPERTY(Attributes="EditorOrder(240), DefaultValue(typeof(Vector3), \"0,1,0\"), EditorDisplay(\"Character Controller\"), Limit(-1, 1)")
Vector3 GetUpDirection() const;
/// <summary>

View File

@@ -218,6 +218,21 @@ void Physics::FlushRequests()
PhysicsBackend::FlushRequests();
}
bool Physics::LineCast(const Vector3& start, const Vector3& end, uint32 layerMask, bool hitTriggers)
{
return DefaultScene->LineCast(start, end, layerMask, hitTriggers);
}
bool Physics::LineCast(const Vector3& start, const Vector3& end, RayCastHit& hitInfo, uint32 layerMask, bool hitTriggers)
{
return DefaultScene->LineCast(start, end, hitInfo, layerMask, hitTriggers);
}
bool Physics::LineCastAll(const Vector3& start, const Vector3& end, Array<RayCastHit>& results, uint32 layerMask, bool hitTriggers)
{
return DefaultScene->LineCastAll(start, end, results, layerMask, hitTriggers);
}
bool Physics::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return DefaultScene->RayCast(origin, direction, maxDistance, layerMask, hitTriggers);
@@ -461,6 +476,33 @@ void PhysicsScene::CollectResults()
_isDuringSimulation = false;
}
bool PhysicsScene::LineCast(const Vector3& start, const Vector3& end, uint32 layerMask, bool hitTriggers)
{
Vector3 directionToEnd = end - start;
const float distanceToEnd = directionToEnd.Length();
if (distanceToEnd >= ZeroTolerance)
directionToEnd /= distanceToEnd;
return PhysicsBackend::RayCast(_scene, start, directionToEnd, distanceToEnd, layerMask, hitTriggers);
}
bool PhysicsScene::LineCast(const Vector3& start, const Vector3& end, RayCastHit& hitInfo, uint32 layerMask, bool hitTriggers)
{
Vector3 directionToEnd = end - start;
const float distanceToEnd = directionToEnd.Length();
if (distanceToEnd >= ZeroTolerance)
directionToEnd /= distanceToEnd;
return PhysicsBackend::RayCast(_scene, start, directionToEnd, hitInfo, distanceToEnd, layerMask, hitTriggers);
}
bool PhysicsScene::LineCastAll(const Vector3& start, const Vector3& end, Array<RayCastHit>& results, uint32 layerMask, bool hitTriggers)
{
Vector3 directionToEnd = end - start;
const float distanceToEnd = directionToEnd.Length();
if (distanceToEnd >= ZeroTolerance)
directionToEnd /= distanceToEnd;
return PhysicsBackend::RayCastAll(_scene, start, directionToEnd, results, distanceToEnd, layerMask, hitTriggers);
}
bool PhysicsScene::RayCast(const Vector3& origin, const Vector3& direction, const float maxDistance, uint32 layerMask, bool hitTriggers)
{
return PhysicsBackend::RayCast(_scene, origin, direction, maxDistance, layerMask, hitTriggers);

View File

@@ -95,6 +95,38 @@ public:
API_FUNCTION() static void FlushRequests();
public:
/// <summary>
/// Performs a line between two points in the scene.
/// </summary>
/// <param name="start">The start position of the line.</param>
/// <param name="end">The end position of the line.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if ray hits an matching object, otherwise false.</returns>
API_FUNCTION() static bool LineCast(const Vector3& start, const Vector3& end, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Performs a line between two points in the scene.
/// </summary>
/// <param name="start">The start position of the line.</param>
/// <param name="end">The end position of the line.</param>
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if ray hits an matching object, otherwise false.</returns>
API_FUNCTION() static bool LineCast(const Vector3& start, const Vector3& end, API_PARAM(Out) RayCastHit& hitInfo, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
// <summary>
/// Performs a line between two points in the scene, returns all hitpoints infos.
/// </summary>
/// <param name="start">The origin of the ray.</param>
/// <param name="end">The normalized direction of the ray.</param>
/// <param name="results">The result hits. Valid only when method returns true.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if ray hits an matching object, otherwise false.</returns>
API_FUNCTION() static bool LineCastAll(const Vector3& start, const Vector3& end, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Performs a raycast against objects in the scene.
/// </summary>

View File

@@ -133,6 +133,38 @@ public:
API_FUNCTION() void CollectResults();
public:
/// <summary>
/// Performs a line between two points in the scene.
/// </summary>
/// <param name="start">The start position of the line.</param>
/// <param name="end">The end position of the line.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if ray hits an matching object, otherwise false.</returns>
API_FUNCTION() bool LineCast(const Vector3& start, const Vector3& end, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Performs a line between two points in the scene.
/// </summary>
/// <param name="start">The start position of the line.</param>
/// <param name="end">The end position of the line.</param>
/// <param name="hitInfo">The result hit information. Valid only when method returns true.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if ray hits an matching object, otherwise false.</returns>
API_FUNCTION() bool LineCast(const Vector3& start, const Vector3& end, API_PARAM(Out) RayCastHit& hitInfo, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
// <summary>
/// Performs a line between two points in the scene, returns all hitpoints infos.
/// </summary>
/// <param name="start">The origin of the ray.</param>
/// <param name="end">The normalized direction of the ray.</param>
/// <param name="results">The result hits. Valid only when method returns true.</param>
/// <param name="layerMask">The layer mask used to filter the results.</param>
/// <param name="hitTriggers">If set to <c>true</c> triggers will be hit, otherwise will skip them.</param>
/// <returns>True if ray hits an matching object, otherwise false.</returns>
API_FUNCTION() bool LineCastAll(const Vector3& start, const Vector3& end, API_PARAM(Out) Array<RayCastHit, HeapAllocation>& results, uint32 layerMask = MAX_uint32, bool hitTriggers = true);
/// <summary>
/// Performs a raycast against objects in the scene.
/// </summary>

View File

@@ -1380,7 +1380,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
}
// Automatic LOD generation
if (options.GenerateLODs && data.LODs.HasItems() && options.TriangleReduction < 1.0f - ZeroTolerance)
if (options.GenerateLODs && options.LODCount > 1 && data.LODs.HasItems() && options.TriangleReduction < 1.0f - ZeroTolerance)
{
auto lodStartTime = DateTime::NowUTC();
meshopt_setAllocator(MeshOptAllocate, MeshOptDeallocate);

View File

@@ -169,6 +169,16 @@ namespace FlaxEngine.GUI
set => Bounds = new Rectangle(value + (_parent != null ? _parent.Bounds.Size * (_anchorMax + _anchorMin) * 0.5f : Float2.Zero) - _bounds.Size * _pivot, _bounds.Size);
}
/// <summary>
/// Whether to resize the UI Control based on where the pivot is rather than just the top-left.
/// </summary>
[NoSerialize, HideInEditor]
public bool PivotRelative
{
get => _pivotRelativeSizing;
set => _pivotRelativeSizing = value;
}
/// <summary>
/// Gets or sets width of the control.
/// </summary>
@@ -181,6 +191,11 @@ namespace FlaxEngine.GUI
if (Mathf.NearEqual(_bounds.Size.X, value))
return;
var bounds = new Rectangle(_bounds.Location, value, _bounds.Size.Y);
if (_pivotRelativeSizing)
{
var delta = _bounds.Size.X - value;
bounds.Location.X += delta * Pivot.X;
}
SetBounds(ref bounds);
}
}
@@ -197,6 +212,11 @@ namespace FlaxEngine.GUI
if (Mathf.NearEqual(_bounds.Size.Y, value))
return;
var bounds = new Rectangle(_bounds.Location, _bounds.Size.X, value);
if (_pivotRelativeSizing)
{
var delta = _bounds.Size.Y - value;
bounds.Location.Y += delta * Pivot.Y;
}
SetBounds(ref bounds);
}
}

View File

@@ -61,6 +61,7 @@ namespace FlaxEngine.GUI
private bool _isVisible = true;
private bool _isEnabled = true;
private bool _autoFocus = true;
private bool _pivotRelativeSizing = false;
private List<int> _touchOvers;
private RootControl.UpdateDelegate _tooltipUpdate;