Merge remote-tracking branch 'origin/1.10' into sdl_platform_1.10
This commit is contained in:
@@ -213,38 +213,59 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
}
|
}
|
||||||
FileSystem::NormalizePath(srcDotnet);
|
FileSystem::NormalizePath(srcDotnet);
|
||||||
LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), srcDotnet);
|
LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), srcDotnet);
|
||||||
|
const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms"));
|
||||||
|
String packFolder = srcDotnet / TEXT("../../../");
|
||||||
|
|
||||||
// Get major Version
|
// Get .NET runtime version
|
||||||
Array<String> pathParts;
|
|
||||||
srcDotnet.Split('/', pathParts);
|
|
||||||
String version;
|
String version;
|
||||||
for (int i = 0; i < pathParts.Count(); i++)
|
|
||||||
{
|
{
|
||||||
if (pathParts[i] == TEXT("runtimes"))
|
// Detect from path provided by build tool
|
||||||
|
Array<String> pathParts;
|
||||||
|
srcDotnet.Split('/', pathParts);
|
||||||
|
for (int32 i = 1; i < pathParts.Count(); i++)
|
||||||
{
|
{
|
||||||
Array<String> versionParts;
|
if (pathParts[i] == TEXT("runtimes"))
|
||||||
pathParts[i - 1].Split('.', versionParts);
|
|
||||||
if (!versionParts.IsEmpty())
|
|
||||||
{
|
{
|
||||||
const String majorVersion = versionParts[0].TrimTrailing();
|
Array<String> versionParts;
|
||||||
int32 versionNum;
|
pathParts[i - 1].Split('.', versionParts);
|
||||||
StringUtils::Parse(*majorVersion, majorVersion.Length(), &versionNum);
|
if (!versionParts.IsEmpty())
|
||||||
if (Math::IsInRange(versionNum, GAME_BUILD_DOTNET_RUNTIME_MIN_VER, GAME_BUILD_DOTNET_RUNTIME_MAX_VER)) // Check for major part
|
{
|
||||||
version = majorVersion;
|
const String majorVersion = versionParts[0].TrimTrailing();
|
||||||
|
int32 versionNum;
|
||||||
|
StringUtils::Parse(*majorVersion, majorVersion.Length(), &versionNum);
|
||||||
|
if (Math::IsInRange(versionNum, GAME_BUILD_DOTNET_RUNTIME_MIN_VER, GAME_BUILD_DOTNET_RUNTIME_MAX_VER)) // Check for major part
|
||||||
|
version = majorVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (version.IsEmpty())
|
||||||
|
{
|
||||||
|
if (srcDotnetFromEngine)
|
||||||
|
{
|
||||||
|
// Detect version from runtime files inside Engine Platform folder
|
||||||
|
for (int32 i = GAME_BUILD_DOTNET_RUNTIME_MAX_VER; i >= GAME_BUILD_DOTNET_RUNTIME_MIN_VER; i--)
|
||||||
|
{
|
||||||
|
// Check runtime files inside Engine Platform folder
|
||||||
|
String testPath1 = srcDotnet / String::Format(TEXT("lib/net{}.0"), i);
|
||||||
|
String testPath2 = packFolder / String::Format(TEXT("Dotnet/lib/net{}.0"), i);
|
||||||
|
if ((FileSystem::DirectoryExists(testPath1) && FileSystem::GetDirectorySize(testPath1) > 0) ||
|
||||||
|
(FileSystem::DirectoryExists(testPath2) && FileSystem::GetDirectorySize(testPath2) > 0))
|
||||||
|
{
|
||||||
|
version = StringUtils::ToString(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (version.IsEmpty())
|
||||||
|
{
|
||||||
|
data.Error(TEXT("Failed to find supported .NET version for the current host platform."));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version.IsEmpty())
|
|
||||||
{
|
|
||||||
data.Error(TEXT("Failed to find supported .NET version for the current host platform."));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deploy runtime files
|
// Deploy runtime files
|
||||||
const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll");
|
const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll");
|
||||||
const bool srcDotnetFromEngine = srcDotnet.Contains(TEXT("Source/Platforms"));
|
|
||||||
String packFolder = srcDotnet / TEXT("../../../");
|
|
||||||
String dstDotnetLibs = dstDotnet, srcDotnetLibs = srcDotnet;
|
String dstDotnetLibs = dstDotnet, srcDotnetLibs = srcDotnet;
|
||||||
StringUtils::PathRemoveRelativeParts(packFolder);
|
StringUtils::PathRemoveRelativeParts(packFolder);
|
||||||
if (usAOT)
|
if (usAOT)
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
if (assetReference != null)
|
if (assetReference != null)
|
||||||
{
|
{
|
||||||
if (assetReference.UseSmallPicker)
|
if (assetReference.UseSmallPicker)
|
||||||
height = 32;
|
height = 36;
|
||||||
if (string.IsNullOrEmpty(assetReference.TypeName))
|
if (string.IsNullOrEmpty(assetReference.TypeName))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
var frameRect = new Rectangle(0, 0, Width, 16);
|
var frameRect = new Rectangle(0, 0, Width, 16);
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
frameRect.Width -= 16;
|
frameRect.Width -= 16;
|
||||||
if (_supportsPickDropDown)
|
if (_supportsPickDropDown && isEnabled)
|
||||||
frameRect.Width -= 16;
|
frameRect.Width -= 16;
|
||||||
var nameRect = new Rectangle(2, 1, frameRect.Width - 4, 14);
|
var nameRect = new Rectangle(2, 1, frameRect.Width - 4, 14);
|
||||||
var button1Rect = new Rectangle(nameRect.Right + 2, 1, 14, 14);
|
var button1Rect = new Rectangle(nameRect.Right + 2, 1, 14, 14);
|
||||||
@@ -240,7 +240,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw picker button
|
// Draw picker button
|
||||||
if (_supportsPickDropDown)
|
if (_supportsPickDropDown && isEnabled)
|
||||||
{
|
{
|
||||||
var pickerRect = isSelected ? button2Rect : button1Rect;
|
var pickerRect = isSelected ? button2Rect : button1Rect;
|
||||||
Render2D.DrawSprite(style.ArrowDown, pickerRect, isEnabled && pickerRect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
Render2D.DrawSprite(style.ArrowDown, pickerRect, isEnabled && pickerRect.Contains(_mousePos) ? style.Foreground : style.ForegroundGrey);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
float height = 48;
|
float height = 48;
|
||||||
if (assetReference.UseSmallPicker)
|
if (assetReference.UseSmallPicker)
|
||||||
height = 32;
|
height = 36;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(assetReference.TypeName))
|
if (string.IsNullOrEmpty(assetReference.TypeName))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ namespace FlaxEditor.Gizmo
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The inner minimum of the multiscale
|
/// The inner minimum of the multiscale
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float InnerExtend = AxisOffset + 0.5f;
|
private const float InnerExtend = AxisOffset;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The outer maximum of the multiscale
|
/// The outer maximum of the multiscale
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float OuterExtend = AxisOffset * 3.5f;
|
private const float OuterExtend = AxisOffset + 1.25f;
|
||||||
|
|
||||||
// Cube with the size AxisThickness, then moves it along the axis (AxisThickness) and finally makes it really long (AxisLength)
|
// Cube with the size AxisThickness, then moves it along the axis (AxisThickness) and finally makes it really long (AxisLength)
|
||||||
private BoundingBox XAxisBox = new BoundingBox(new Vector3(-AxisThickness), new Vector3(AxisThickness)).MakeOffsetted(AxisOffset * Vector3.UnitX).Merge(AxisLength * Vector3.UnitX);
|
private BoundingBox XAxisBox = new BoundingBox(new Vector3(-AxisThickness), new Vector3(AxisThickness)).MakeOffsetted(AxisOffset * Vector3.UnitX).Merge(AxisLength * Vector3.UnitX);
|
||||||
@@ -75,6 +75,11 @@ namespace FlaxEditor.Gizmo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ScaleSnapEnabled = false;
|
public bool ScaleSnapEnabled = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if enable absolute grid snapping (snaps objects to world-space grid, not the one relative to gizmo location)
|
||||||
|
/// </summary>
|
||||||
|
public bool AbsoluteSnapEnabled = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Translation snap value
|
/// Translation snap value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
#if USE_LARGE_WORLDS
|
#if USE_LARGE_WORLDS
|
||||||
using Real = System.Double;
|
using Real = System.Double;
|
||||||
|
using Mathr = FlaxEngine.Mathd;
|
||||||
#else
|
#else
|
||||||
using Real = System.Single;
|
using Real = System.Single;
|
||||||
|
using Mathr = FlaxEngine.Mathf;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@@ -40,8 +42,10 @@ namespace FlaxEditor.Gizmo
|
|||||||
private Vector3 _intersectPosition;
|
private Vector3 _intersectPosition;
|
||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
private bool _isDuplicating;
|
private bool _isDuplicating;
|
||||||
|
private bool _hasAbsoluteSnapped;
|
||||||
|
|
||||||
private bool _isTransforming;
|
private bool _isTransforming;
|
||||||
|
private bool _isSelected;
|
||||||
private Vector3 _lastIntersectionPosition;
|
private Vector3 _lastIntersectionPosition;
|
||||||
|
|
||||||
private Quaternion _rotationDelta = Quaternion.Identity;
|
private Quaternion _rotationDelta = Quaternion.Identity;
|
||||||
@@ -269,7 +273,17 @@ namespace FlaxEditor.Gizmo
|
|||||||
_intersectPosition = ray.GetPoint(intersection);
|
_intersectPosition = ray.GetPoint(intersection);
|
||||||
if (!_lastIntersectionPosition.IsZero)
|
if (!_lastIntersectionPosition.IsZero)
|
||||||
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
||||||
delta = new Vector3(0, _tDelta.Y, _tDelta.Z);
|
if (isScaling)
|
||||||
|
{
|
||||||
|
var tDeltaAbs = Vector3.Abs(_tDelta);
|
||||||
|
var maxDelta = Math.Max(tDeltaAbs.Y, tDeltaAbs.Z);
|
||||||
|
var sign = Math.Sign(tDeltaAbs.Y > tDeltaAbs.Z ? _tDelta.Y : _tDelta.Z);
|
||||||
|
delta = new Vector3(0, maxDelta * sign, maxDelta * sign);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delta = new Vector3(0, _tDelta.Y, _tDelta.Z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -280,7 +294,17 @@ namespace FlaxEditor.Gizmo
|
|||||||
_intersectPosition = ray.GetPoint(intersection);
|
_intersectPosition = ray.GetPoint(intersection);
|
||||||
if (!_lastIntersectionPosition.IsZero)
|
if (!_lastIntersectionPosition.IsZero)
|
||||||
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
||||||
delta = new Vector3(_tDelta.X, _tDelta.Y, 0);
|
if (isScaling)
|
||||||
|
{
|
||||||
|
var tDeltaAbs = Vector3.Abs(_tDelta);
|
||||||
|
var maxDelta = Math.Max(tDeltaAbs.X, tDeltaAbs.Y);
|
||||||
|
var sign = Math.Sign(tDeltaAbs.X > tDeltaAbs.Y ? _tDelta.X : _tDelta.Y);
|
||||||
|
delta = new Vector3(maxDelta * sign, maxDelta * sign, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delta = new Vector3(_tDelta.X, _tDelta.Y, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -291,7 +315,17 @@ namespace FlaxEditor.Gizmo
|
|||||||
_intersectPosition = ray.GetPoint(intersection);
|
_intersectPosition = ray.GetPoint(intersection);
|
||||||
if (!_lastIntersectionPosition.IsZero)
|
if (!_lastIntersectionPosition.IsZero)
|
||||||
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
||||||
delta = new Vector3(_tDelta.X, 0, _tDelta.Z);
|
if (isScaling)
|
||||||
|
{
|
||||||
|
var tDeltaAbs = Vector3.Abs(_tDelta);
|
||||||
|
var maxDelta = Math.Max(tDeltaAbs.X, tDeltaAbs.Z);
|
||||||
|
var sign = Math.Sign(tDeltaAbs.X > tDeltaAbs.Z ? _tDelta.X : _tDelta.Z);
|
||||||
|
delta = new Vector3(maxDelta * sign, 0, maxDelta * sign);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delta = new Vector3(_tDelta.X, 0, _tDelta.Z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -305,7 +339,24 @@ namespace FlaxEditor.Gizmo
|
|||||||
if (!_lastIntersectionPosition.IsZero)
|
if (!_lastIntersectionPosition.IsZero)
|
||||||
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
_tDelta = _intersectPosition - _lastIntersectionPosition;
|
||||||
}
|
}
|
||||||
delta = _tDelta;
|
if (isScaling)
|
||||||
|
{
|
||||||
|
var tDeltaAbs = Vector3.Abs(_tDelta);
|
||||||
|
var maxDelta = Math.Max(tDeltaAbs.X, tDeltaAbs.Y);
|
||||||
|
maxDelta = Math.Max(maxDelta, tDeltaAbs.Z);
|
||||||
|
Real sign = 0;
|
||||||
|
if (Mathf.NearEqual(maxDelta, tDeltaAbs.X))
|
||||||
|
sign = Math.Sign(_tDelta.X);
|
||||||
|
else if (Mathf.NearEqual(maxDelta, tDeltaAbs.Y))
|
||||||
|
sign = Math.Sign(_tDelta.Y);
|
||||||
|
else if (Mathf.NearEqual(maxDelta, tDeltaAbs.Z))
|
||||||
|
sign = Math.Sign(_tDelta.Z);
|
||||||
|
delta = new Vector3(maxDelta * sign);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delta = _tDelta;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,6 +369,7 @@ namespace FlaxEditor.Gizmo
|
|||||||
if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping)
|
if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping)
|
||||||
{
|
{
|
||||||
var snapValue = new Vector3(isScaling ? ScaleSnapValue : TranslationSnapValue);
|
var snapValue = new Vector3(isScaling ? ScaleSnapValue : TranslationSnapValue);
|
||||||
|
|
||||||
_translationScaleSnapDelta += delta;
|
_translationScaleSnapDelta += delta;
|
||||||
if (!isScaling && snapValue.X < 0.0f)
|
if (!isScaling && snapValue.X < 0.0f)
|
||||||
{
|
{
|
||||||
@@ -336,11 +388,29 @@ namespace FlaxEditor.Gizmo
|
|||||||
else
|
else
|
||||||
snapValue.Z = (Real)b.Minimum.Z - b.Maximum.Z;
|
snapValue.Z = (Real)b.Minimum.Z - b.Maximum.Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector3 absoluteDelta = Vector3.Zero;
|
||||||
|
if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled && ActiveTransformSpace == TransformSpace.World)
|
||||||
|
{
|
||||||
|
// Remove delta to offset local-space grid into the world-space grid
|
||||||
|
_hasAbsoluteSnapped = true;
|
||||||
|
Vector3 currentTranslationScale = isScaling ? GetSelectedTransform(0).Scale : GetSelectedTransform(0).Translation;
|
||||||
|
absoluteDelta = currentTranslationScale - new Vector3(
|
||||||
|
Mathr.Round(currentTranslationScale.X / snapValue.X) * snapValue.X,
|
||||||
|
Mathr.Round(currentTranslationScale.Y / snapValue.Y) * snapValue.Y,
|
||||||
|
Mathr.Round(currentTranslationScale.Z / snapValue.Z) * snapValue.Z);
|
||||||
|
}
|
||||||
|
|
||||||
delta = new Vector3(
|
delta = new Vector3(
|
||||||
(int)(_translationScaleSnapDelta.X / snapValue.X) * snapValue.X,
|
(int)(_translationScaleSnapDelta.X / snapValue.X) * snapValue.X,
|
||||||
(int)(_translationScaleSnapDelta.Y / snapValue.Y) * snapValue.Y,
|
(int)(_translationScaleSnapDelta.Y / snapValue.Y) * snapValue.Y,
|
||||||
(int)(_translationScaleSnapDelta.Z / snapValue.Z) * snapValue.Z);
|
(int)(_translationScaleSnapDelta.Z / snapValue.Z) * snapValue.Z);
|
||||||
_translationScaleSnapDelta -= delta;
|
_translationScaleSnapDelta -= delta;
|
||||||
|
delta -= absoluteDelta;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_hasAbsoluteSnapped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_activeMode == Mode.Translate)
|
if (_activeMode == Mode.Translate)
|
||||||
@@ -370,12 +440,33 @@ namespace FlaxEditor.Gizmo
|
|||||||
if (RotationSnapEnabled || Owner.UseSnapping)
|
if (RotationSnapEnabled || Owner.UseSnapping)
|
||||||
{
|
{
|
||||||
float snapValue = RotationSnapValue * Mathf.DegreesToRadians;
|
float snapValue = RotationSnapValue * Mathf.DegreesToRadians;
|
||||||
|
|
||||||
|
float absoluteDelta = 0.0f;
|
||||||
|
if (!_hasAbsoluteSnapped && AbsoluteSnapEnabled && ActiveTransformSpace == TransformSpace.World)
|
||||||
|
{
|
||||||
|
// Remove delta to offset world-space grid into the local-space grid
|
||||||
|
_hasAbsoluteSnapped = true;
|
||||||
|
float currentAngle = 0.0f;
|
||||||
|
switch (_activeAxis)
|
||||||
|
{
|
||||||
|
case Axis.X: currentAngle = GetSelectedTransform(0).Orientation.EulerAngles.X; break;
|
||||||
|
case Axis.Y: currentAngle = GetSelectedTransform(0).Orientation.EulerAngles.Y; break;
|
||||||
|
case Axis.Z: currentAngle = GetSelectedTransform(0).Orientation.EulerAngles.Z; break;
|
||||||
|
}
|
||||||
|
absoluteDelta = currentAngle - (Mathf.Round(currentAngle / RotationSnapValue) * RotationSnapValue);
|
||||||
|
}
|
||||||
|
|
||||||
_rotationSnapDelta += delta;
|
_rotationSnapDelta += delta;
|
||||||
|
|
||||||
float snapped = Mathf.Round(_rotationSnapDelta / snapValue) * snapValue;
|
float snapped = Mathf.Round(_rotationSnapDelta / snapValue) * snapValue;
|
||||||
_rotationSnapDelta -= snapped;
|
_rotationSnapDelta -= snapped;
|
||||||
|
|
||||||
delta = snapped;
|
delta = snapped;
|
||||||
|
delta -= absoluteDelta * Mathf.DegreesToRadians;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_hasAbsoluteSnapped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_activeAxis)
|
switch (_activeAxis)
|
||||||
@@ -408,7 +499,7 @@ namespace FlaxEditor.Gizmo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool IsControllingMouse => _isTransforming;
|
public override bool IsControllingMouse => _isTransforming || _isSelected;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Update(float dt)
|
public override void Update(float dt)
|
||||||
@@ -433,6 +524,7 @@ namespace FlaxEditor.Gizmo
|
|||||||
// Check if user is holding left mouse button and any axis is selected
|
// Check if user is holding left mouse button and any axis is selected
|
||||||
if (isLeftBtnDown && _activeAxis != Axis.None)
|
if (isLeftBtnDown && _activeAxis != Axis.None)
|
||||||
{
|
{
|
||||||
|
_isSelected = true; // setting later is too late, need to set here for rubber band selection in GizmoViewport
|
||||||
switch (_activeMode)
|
switch (_activeMode)
|
||||||
{
|
{
|
||||||
case Mode.Translate:
|
case Mode.Translate:
|
||||||
@@ -450,6 +542,7 @@ namespace FlaxEditor.Gizmo
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
_isSelected = false;
|
||||||
// If nothing selected, try to select any axis
|
// If nothing selected, try to select any axis
|
||||||
if (!isLeftBtnDown && !Owner.IsRightMouseButtonDown)
|
if (!isLeftBtnDown && !Owner.IsRightMouseButtonDown)
|
||||||
{
|
{
|
||||||
@@ -517,6 +610,7 @@ namespace FlaxEditor.Gizmo
|
|||||||
// Clear cache
|
// Clear cache
|
||||||
_accMoveDelta = Vector3.Zero;
|
_accMoveDelta = Vector3.Zero;
|
||||||
_lastIntersectionPosition = _intersectPosition = Vector3.Zero;
|
_lastIntersectionPosition = _intersectPosition = Vector3.Zero;
|
||||||
|
_isSelected = false;
|
||||||
EndTransforming();
|
EndTransforming();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
277
Source/Editor/Gizmo/ViewportRubberBandSelector.cs
Normal file
277
Source/Editor/Gizmo/ViewportRubberBandSelector.cs
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FlaxEditor.Gizmo;
|
||||||
|
using FlaxEditor.SceneGraph;
|
||||||
|
using FlaxEditor.Viewport;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
|
namespace FlaxEngine.Gizmo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class for adding viewport rubber band selection.
|
||||||
|
/// </summary>
|
||||||
|
public class ViewportRubberBandSelector
|
||||||
|
{
|
||||||
|
private bool _isMosueCaptured;
|
||||||
|
private bool _isRubberBandSpanning;
|
||||||
|
private bool _tryStartRubberBand;
|
||||||
|
private Float2 _cachedStartingMousePosition;
|
||||||
|
private Rectangle _rubberBandRect;
|
||||||
|
private Rectangle _lastRubberBandRect;
|
||||||
|
private List<ActorNode> _nodesCache;
|
||||||
|
private List<SceneGraphNode> _hitsCache;
|
||||||
|
private IGizmoOwner _owner;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a rubber band selector with a designated gizmo owner.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="owner">The gizmo owner.</param>
|
||||||
|
public ViewportRubberBandSelector(IGizmoOwner owner)
|
||||||
|
{
|
||||||
|
_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers the start of a rubber band selection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if selection started, otherwise false.</returns>
|
||||||
|
public bool TryStartingRubberBandSelection()
|
||||||
|
{
|
||||||
|
if (!_isRubberBandSpanning && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
|
||||||
|
{
|
||||||
|
_tryStartRubberBand = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Release the rubber band selection.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns true if rubber band is currently spanning</returns>
|
||||||
|
public bool ReleaseRubberBandSelection()
|
||||||
|
{
|
||||||
|
if (_isMosueCaptured)
|
||||||
|
{
|
||||||
|
_isMosueCaptured = false;
|
||||||
|
_owner.Viewport.EndMouseCapture();
|
||||||
|
}
|
||||||
|
if (_tryStartRubberBand)
|
||||||
|
{
|
||||||
|
_tryStartRubberBand = false;
|
||||||
|
}
|
||||||
|
if (_isRubberBandSpanning)
|
||||||
|
{
|
||||||
|
_isRubberBandSpanning = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to create a rubber band selection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="canStart">Whether the creation can start.</param>
|
||||||
|
/// <param name="mousePosition">The current mouse position.</param>
|
||||||
|
/// <param name="viewFrustum">The view frustum.</param>
|
||||||
|
public void TryCreateRubberBand(bool canStart, Float2 mousePosition, BoundingFrustum viewFrustum)
|
||||||
|
{
|
||||||
|
if (_isRubberBandSpanning && !canStart)
|
||||||
|
{
|
||||||
|
_isRubberBandSpanning = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_tryStartRubberBand && (Mathf.Abs(_owner.MouseDelta.X) > 0.1f || Mathf.Abs(_owner.MouseDelta.Y) > 0.1f) && canStart)
|
||||||
|
{
|
||||||
|
_isRubberBandSpanning = true;
|
||||||
|
_cachedStartingMousePosition = mousePosition;
|
||||||
|
_rubberBandRect = new Rectangle(_cachedStartingMousePosition, Float2.Zero);
|
||||||
|
_tryStartRubberBand = false;
|
||||||
|
}
|
||||||
|
else if (_isRubberBandSpanning && !_owner.Gizmos.Active.IsControllingMouse && !_owner.IsRightMouseButtonDown)
|
||||||
|
{
|
||||||
|
_rubberBandRect.Width = mousePosition.X - _cachedStartingMousePosition.X;
|
||||||
|
_rubberBandRect.Height = mousePosition.Y - _cachedStartingMousePosition.Y;
|
||||||
|
if (_lastRubberBandRect != _rubberBandRect)
|
||||||
|
{
|
||||||
|
if (!_isMosueCaptured)
|
||||||
|
{
|
||||||
|
_isMosueCaptured = true;
|
||||||
|
_owner.Viewport.StartMouseCapture();
|
||||||
|
}
|
||||||
|
UpdateRubberBand(ref viewFrustum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ViewportProjection
|
||||||
|
{
|
||||||
|
private Viewport _viewport;
|
||||||
|
private Matrix _viewProjection;
|
||||||
|
|
||||||
|
public void Init(EditorViewport editorViewport)
|
||||||
|
{
|
||||||
|
// Inline EditorViewport.ProjectPoint to save on calculation for large set of points
|
||||||
|
_viewport = new Viewport(0, 0, editorViewport.Width, editorViewport.Height);
|
||||||
|
var frustum = editorViewport.ViewFrustum;
|
||||||
|
_viewProjection = frustum.Matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ProjectPoint(ref Vector3 worldSpaceLocation, out Float2 viewportSpaceLocation)
|
||||||
|
{
|
||||||
|
_viewport.Project(ref worldSpaceLocation, ref _viewProjection, out var projected);
|
||||||
|
viewportSpaceLocation = new Float2((float)projected.X, (float)projected.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRubberBand(ref BoundingFrustum viewFrustum)
|
||||||
|
{
|
||||||
|
Profiler.BeginEvent("UpdateRubberBand");
|
||||||
|
|
||||||
|
// Select rubberbanded rect actor nodes
|
||||||
|
var adjustedRect = _rubberBandRect;
|
||||||
|
_lastRubberBandRect = _rubberBandRect;
|
||||||
|
if (adjustedRect.Width < 0 || adjustedRect.Height < 0)
|
||||||
|
{
|
||||||
|
// Make sure we have a well-formed rectangle i.e. size is positive and X/Y is upper left corner
|
||||||
|
var size = adjustedRect.Size;
|
||||||
|
adjustedRect.X = Mathf.Min(adjustedRect.X, adjustedRect.X + adjustedRect.Width);
|
||||||
|
adjustedRect.Y = Mathf.Min(adjustedRect.Y, adjustedRect.Y + adjustedRect.Height);
|
||||||
|
size.X = Mathf.Abs(size.X);
|
||||||
|
size.Y = Mathf.Abs(size.Y);
|
||||||
|
adjustedRect.Size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get hits from graph nodes
|
||||||
|
if (_nodesCache == null)
|
||||||
|
_nodesCache = new List<ActorNode>();
|
||||||
|
else
|
||||||
|
_nodesCache.Clear();
|
||||||
|
var nodes = _nodesCache;
|
||||||
|
_owner.SceneGraphRoot.GetAllChildActorNodes(nodes);
|
||||||
|
if (_hitsCache == null)
|
||||||
|
_hitsCache = new List<SceneGraphNode>();
|
||||||
|
else
|
||||||
|
_hitsCache.Clear();
|
||||||
|
var hits = _hitsCache;
|
||||||
|
|
||||||
|
// Process all nodes
|
||||||
|
var projection = new ViewportProjection();
|
||||||
|
projection.Init(_owner.Viewport);
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
// Check for custom can select code
|
||||||
|
if (!node.CanSelectActorNodeWithSelector())
|
||||||
|
continue;
|
||||||
|
var a = node.Actor;
|
||||||
|
|
||||||
|
// Skip actor if outside of view frustum
|
||||||
|
var actorBox = a.EditorBox;
|
||||||
|
if (viewFrustum.Contains(actorBox) == ContainmentType.Disjoint)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get valid selection points
|
||||||
|
var points = node.GetActorSelectionPoints();
|
||||||
|
if (LoopOverPoints(points, ref adjustedRect, ref projection))
|
||||||
|
{
|
||||||
|
if (a.HasPrefabLink && _owner is not PrefabWindowViewport)
|
||||||
|
hits.Add(_owner.SceneGraphRoot.Find(a.GetPrefabRoot()));
|
||||||
|
else
|
||||||
|
hits.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process selection
|
||||||
|
if (_owner.IsControlDown)
|
||||||
|
{
|
||||||
|
var newSelection = new List<SceneGraphNode>();
|
||||||
|
var currentSelection = new List<SceneGraphNode>(_owner.SceneGraphRoot.SceneContext.Selection);
|
||||||
|
newSelection.AddRange(currentSelection);
|
||||||
|
foreach (var hit in hits)
|
||||||
|
{
|
||||||
|
if (currentSelection.Contains(hit))
|
||||||
|
newSelection.Remove(hit);
|
||||||
|
else
|
||||||
|
newSelection.Add(hit);
|
||||||
|
}
|
||||||
|
_owner.Select(newSelection);
|
||||||
|
}
|
||||||
|
else if (Input.GetKey(KeyboardKeys.Shift))
|
||||||
|
{
|
||||||
|
var newSelection = new List<SceneGraphNode>();
|
||||||
|
var currentSelection = new List<SceneGraphNode>(_owner.SceneGraphRoot.SceneContext.Selection);
|
||||||
|
newSelection.AddRange(hits);
|
||||||
|
newSelection.AddRange(currentSelection);
|
||||||
|
_owner.Select(newSelection);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_owner.Select(hits);
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LoopOverPoints(Vector3[] points, ref Rectangle adjustedRect, ref ViewportProjection projection)
|
||||||
|
{
|
||||||
|
Profiler.BeginEvent("LoopOverPoints");
|
||||||
|
bool containsAllPoints = points.Length != 0;
|
||||||
|
for (int i = 0; i < points.Length; i++)
|
||||||
|
{
|
||||||
|
projection.ProjectPoint(ref points[i], out var loc);
|
||||||
|
if (!adjustedRect.Contains(loc))
|
||||||
|
{
|
||||||
|
containsAllPoints = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Profiler.EndEvent();
|
||||||
|
return containsAllPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to draw the rubber band. Begins render 2D.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The GPU Context.</param>
|
||||||
|
/// <param name="target">The GPU texture target.</param>
|
||||||
|
/// <param name="targetDepth">The GPU texture target depth.</param>
|
||||||
|
public void Draw(GPUContext context, GPUTexture target, GPUTexture targetDepth)
|
||||||
|
{
|
||||||
|
// Draw RubberBand for rect selection
|
||||||
|
if (!_isRubberBandSpanning)
|
||||||
|
return;
|
||||||
|
Render2D.Begin(context, target, targetDepth);
|
||||||
|
Draw2D();
|
||||||
|
Render2D.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to draw the rubber band. Use if already rendering 2D context.
|
||||||
|
/// </summary>
|
||||||
|
public void Draw2D()
|
||||||
|
{
|
||||||
|
if (!_isRubberBandSpanning)
|
||||||
|
return;
|
||||||
|
Render2D.FillRectangle(_rubberBandRect, Style.Current.Selection);
|
||||||
|
Render2D.DrawRectangle(_rubberBandRect, Style.Current.SelectionBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Immediately stops the rubber band.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if rubber band was active before stopping.</returns>
|
||||||
|
public bool StopRubberBand()
|
||||||
|
{
|
||||||
|
if (_isMosueCaptured)
|
||||||
|
{
|
||||||
|
_isMosueCaptured = false;
|
||||||
|
_owner.Viewport.EndMouseCapture();
|
||||||
|
}
|
||||||
|
var result = _tryStartRubberBand;
|
||||||
|
_isRubberBandSpanning = false;
|
||||||
|
_tryStartRubberBand = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -606,6 +606,11 @@ void ManagedEditor::WipeOutLeftoverSceneObjects()
|
|||||||
{
|
{
|
||||||
if (sceneObject->HasParent())
|
if (sceneObject->HasParent())
|
||||||
continue; // Skip sub-objects
|
continue; // Skip sub-objects
|
||||||
|
auto* actor = Cast<Actor>(sceneObject);
|
||||||
|
if (!actor)
|
||||||
|
actor = sceneObject->GetParent();
|
||||||
|
if (actor && actor->HasTag(TEXT("__EditorInternal")))
|
||||||
|
continue; // Skip internal objects used by Editor (eg. EditorScene)
|
||||||
|
|
||||||
LOG(Error, "Object '{}' (ID={}, Type={}) is still in memory after play end but should be destroyed (memory leak).", sceneObject->GetNamePath(), sceneObject->GetID(), sceneObject->GetType().ToString());
|
LOG(Error, "Object '{}' (ID={}, Type={}) is still in memory after play end but should be destroyed (memory leak).", sceneObject->GetNamePath(), sceneObject->GetID(), sceneObject->GetType().ToString());
|
||||||
sceneObject->DeleteObject();
|
sceneObject->DeleteObject();
|
||||||
|
|||||||
@@ -182,6 +182,54 @@ namespace FlaxEditor.SceneGraph
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all nested actor nodes under this actor node.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An array of ActorNodes</returns>
|
||||||
|
public ActorNode[] GetAllChildActorNodes()
|
||||||
|
{
|
||||||
|
var nodes = new List<ActorNode>();
|
||||||
|
GetAllChildActorNodes(nodes);
|
||||||
|
return nodes.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all nested actor nodes under this actor node.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodes">The output list to fill with results.</param>
|
||||||
|
public void GetAllChildActorNodes(List<ActorNode> nodes)
|
||||||
|
{
|
||||||
|
var children = ChildNodes;
|
||||||
|
if (children == null)
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < children.Count; i++)
|
||||||
|
{
|
||||||
|
if (children[i] is ActorNode node)
|
||||||
|
{
|
||||||
|
nodes.Add(node);
|
||||||
|
node.GetAllChildActorNodes(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether an actor node can be selected with a selector.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the actor node can be selected</returns>
|
||||||
|
public virtual bool CanSelectActorNodeWithSelector()
|
||||||
|
{
|
||||||
|
return Actor && Actor.HideFlags is not (HideFlags.DontSelect or HideFlags.FullyHidden) && Actor is not EmptyActor && IsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The selection points used to check if an actor node can be selected.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The points to use if the actor can be selected.</returns>
|
||||||
|
public virtual Vector3[] GetActorSelectionPoints()
|
||||||
|
{
|
||||||
|
return Actor.EditorBox.GetCorners();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this actor can be used to create prefab from it (as a root).
|
/// Gets a value indicating whether this actor can be used to create prefab from it (as a root).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -58,5 +58,11 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
|
|
||||||
return Camera.Internal_IntersectsItselfEditor(FlaxEngine.Object.GetUnmanagedPtr(_actor), ref ray.Ray, out distance);
|
return Camera.Internal_IntersectsItselfEditor(FlaxEngine.Object.GetUnmanagedPtr(_actor), ref ray.Ray, out distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Vector3[] GetActorSelectionPoints()
|
||||||
|
{
|
||||||
|
return [Actor.Position];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanSelectActorNodeWithSelector()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the scene.
|
/// Gets the scene.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
public sealed class StaticModelNode : ActorNode
|
public sealed class StaticModelNode : ActorNode
|
||||||
{
|
{
|
||||||
private Dictionary<IntPtr, Float3[]> _vertices;
|
private Dictionary<IntPtr, Float3[]> _vertices;
|
||||||
|
private Vector3[] _selectionPoints;
|
||||||
|
private Transform _selectionPointsTransform;
|
||||||
|
private Model _selectionPointsModel;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public StaticModelNode(Actor actor)
|
public StaticModelNode(Actor actor)
|
||||||
@@ -31,6 +34,16 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDispose()
|
||||||
|
{
|
||||||
|
_vertices = null;
|
||||||
|
_selectionPoints = null;
|
||||||
|
_selectionPointsModel = null;
|
||||||
|
|
||||||
|
base.OnDispose();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnVertexSnap(ref Ray ray, Real hitDistance, out Vector3 result)
|
public override bool OnVertexSnap(ref Ray ray, Real hitDistance, out Vector3 result)
|
||||||
{
|
{
|
||||||
@@ -91,6 +104,45 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
contextMenu.AddButton("Add collider", () => OnAddMeshCollider(window)).Enabled = ((StaticModel)Actor).Model != null;
|
contextMenu.AddButton("Add collider", () => OnAddMeshCollider(window)).Enabled = ((StaticModel)Actor).Model != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Vector3[] GetActorSelectionPoints()
|
||||||
|
{
|
||||||
|
if (Actor is StaticModel sm && sm.Model)
|
||||||
|
{
|
||||||
|
// Try to use cache
|
||||||
|
var model = sm.Model;
|
||||||
|
var transform = Actor.Transform;
|
||||||
|
if (_selectionPoints != null &&
|
||||||
|
_selectionPointsTransform == transform &&
|
||||||
|
_selectionPointsModel == model)
|
||||||
|
return _selectionPoints;
|
||||||
|
Profiler.BeginEvent("GetActorSelectionPoints");
|
||||||
|
|
||||||
|
// Check collision proxy points for more accurate selection
|
||||||
|
var vecPoints = new List<Vector3>();
|
||||||
|
var m = model.LODs[0];
|
||||||
|
foreach (var mesh in m.Meshes)
|
||||||
|
{
|
||||||
|
var points = mesh.GetCollisionProxyPoints();
|
||||||
|
vecPoints.EnsureCapacity(vecPoints.Count + points.Length);
|
||||||
|
for (int i = 0; i < points.Length; i++)
|
||||||
|
{
|
||||||
|
vecPoints.Add(transform.LocalToWorld(points[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Profiler.EndEvent();
|
||||||
|
if (vecPoints.Count != 0)
|
||||||
|
{
|
||||||
|
_selectionPoints = vecPoints.ToArray();
|
||||||
|
_selectionPointsTransform = transform;
|
||||||
|
_selectionPointsModel = model;
|
||||||
|
return _selectionPoints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base.GetActorSelectionPoints();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnAddMeshCollider(EditorWindow window)
|
private void OnAddMeshCollider(EditorWindow window)
|
||||||
{
|
{
|
||||||
// Allow collider to be added to evey static model selection
|
// Allow collider to be added to evey static model selection
|
||||||
|
|||||||
@@ -78,5 +78,11 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
if (Actor is UICanvas uiCanvas && uiCanvas.Is3D)
|
if (Actor is UICanvas uiCanvas && uiCanvas.Is3D)
|
||||||
DebugDraw.DrawWireBox(uiCanvas.Bounds, Color.BlueViolet);
|
DebugDraw.DrawWireBox(uiCanvas.Bounds, Color.BlueViolet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanSelectActorNodeWithSelector()
|
||||||
|
{
|
||||||
|
return Actor is UICanvas uiCanvas && uiCanvas.Is3D && base.CanSelectActorNodeWithSelector();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,31 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
control.PerformLayout();
|
control.PerformLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanSelectActorNodeWithSelector()
|
||||||
|
{
|
||||||
|
// Check if control and skip if canvas is 2D
|
||||||
|
if (Actor is not UIControl uiControl)
|
||||||
|
return false;
|
||||||
|
UICanvas canvas = null;
|
||||||
|
var controlParent = uiControl.Parent;
|
||||||
|
while (controlParent != null && controlParent is not Scene)
|
||||||
|
{
|
||||||
|
if (controlParent is UICanvas uiCanvas)
|
||||||
|
{
|
||||||
|
canvas = uiCanvas;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
controlParent = controlParent.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canvas != null)
|
||||||
|
{
|
||||||
|
if (canvas.Is2D)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return base.CanSelectActorNodeWithSelector();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
/// <param name="filterText">The filter text.</param>
|
/// <param name="filterText">The filter text.</param>
|
||||||
public void UpdateFilter(string filterText)
|
public void UpdateFilter(string filterText)
|
||||||
{
|
{
|
||||||
// SKip hidden actors
|
// Skip hidden actors
|
||||||
var actor = Actor;
|
var actor = Actor;
|
||||||
if (actor != null && (actor.HideFlags & HideFlags.HideInHierarchy) != 0)
|
if (actor != null && (actor.HideFlags & HideFlags.HideInHierarchy) != 0)
|
||||||
return;
|
return;
|
||||||
@@ -238,7 +238,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Actor !=null)
|
if (Actor != null)
|
||||||
{
|
{
|
||||||
var actorTypeText = trimmedFilter.Replace("a:", "", StringComparison.OrdinalIgnoreCase).Trim();
|
var actorTypeText = trimmedFilter.Replace("a:", "", StringComparison.OrdinalIgnoreCase).Trim();
|
||||||
var name = TypeUtils.GetTypeDisplayName(Actor.GetType());
|
var name = TypeUtils.GetTypeDisplayName(Actor.GetType());
|
||||||
@@ -248,6 +248,26 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Check for control type
|
||||||
|
else if (trimmedFilter.Contains("c:", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (trimmedFilter.Equals("c:", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (Actor != null)
|
||||||
|
hasFilter = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Actor != null && Actor is UIControl uic && uic.Control != null)
|
||||||
|
{
|
||||||
|
var controlTypeText = trimmedFilter.Replace("c:", "", StringComparison.OrdinalIgnoreCase).Trim();
|
||||||
|
var name = TypeUtils.GetTypeDisplayName(uic.Control.GetType());
|
||||||
|
var nameNoSpaces = name.Replace(" ", "");
|
||||||
|
if (name.Contains(controlTypeText, StringComparison.OrdinalIgnoreCase) || nameNoSpaces.Contains(controlTypeText, StringComparison.OrdinalIgnoreCase))
|
||||||
|
hasFilter = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Match text
|
// Match text
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -419,8 +419,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0),
|
NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0),
|
||||||
NodeElementArchetype.Factory.Input(1, "Scale", true, typeof(float), 1, 0),
|
NodeElementArchetype.Factory.Input(1, "Scale", true, typeof(float), 1, 0),
|
||||||
NodeElementArchetype.Factory.Input(2, "Blend", true, typeof(float), 2, 1),
|
NodeElementArchetype.Factory.Input(2, "Blend", true, typeof(float), 2, 1),
|
||||||
NodeElementArchetype.Factory.Input(3, "Offset", true, typeof(Float2), 3, 2),
|
NodeElementArchetype.Factory.Input(3, "Offset", true, typeof(Float2), 6, 2),
|
||||||
NodeElementArchetype.Factory.Output(0, "Color", typeof(Float4), 5),
|
NodeElementArchetype.Factory.Output(0, "Color", typeof(Float4), 3),
|
||||||
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 4, "Sampler"),
|
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 4, "Sampler"),
|
||||||
NodeElementArchetype.Factory.ComboBox(50, Surface.Constants.LayoutOffsetY * 4 - 1, 100, 3, typeof(CommonSamplerType)),
|
NodeElementArchetype.Factory.ComboBox(50, Surface.Constants.LayoutOffsetY * 4 - 1, 100, 3, typeof(CommonSamplerType)),
|
||||||
NodeElementArchetype.Factory.Text(155, Surface.Constants.LayoutOffsetY * 4, "Local"),
|
NodeElementArchetype.Factory.Text(155, Surface.Constants.LayoutOffsetY * 4, "Local"),
|
||||||
@@ -485,8 +485,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0),
|
NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0),
|
||||||
NodeElementArchetype.Factory.Input(1, "Scale", true, typeof(float), 1, 0),
|
NodeElementArchetype.Factory.Input(1, "Scale", true, typeof(float), 1, 0),
|
||||||
NodeElementArchetype.Factory.Input(2, "Blend", true, typeof(float), 2, 1),
|
NodeElementArchetype.Factory.Input(2, "Blend", true, typeof(float), 2, 1),
|
||||||
NodeElementArchetype.Factory.Input(3, "Offset", true, typeof(Float2), 3, 2),
|
NodeElementArchetype.Factory.Input(3, "Offset", true, typeof(Float2), 6, 2),
|
||||||
NodeElementArchetype.Factory.Output(0, "Vector", typeof(Float3), 5),
|
NodeElementArchetype.Factory.Output(0, "Vector", typeof(Float3), 3),
|
||||||
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 4, "Sampler"),
|
NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 4, "Sampler"),
|
||||||
NodeElementArchetype.Factory.ComboBox(50, Surface.Constants.LayoutOffsetY * 4 - 1, 100, 3, typeof(CommonSamplerType)),
|
NodeElementArchetype.Factory.ComboBox(50, Surface.Constants.LayoutOffsetY * 4 - 1, 100, 3, typeof(CommonSamplerType)),
|
||||||
NodeElementArchetype.Factory.Text(155, Surface.Constants.LayoutOffsetY * 4, "Local"),
|
NodeElementArchetype.Factory.Text(155, Surface.Constants.LayoutOffsetY * 4, "Local"),
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ EditorScene::EditorScene(const SpawnParams& params)
|
|||||||
SceneBeginData beginData;
|
SceneBeginData beginData;
|
||||||
EditorScene::BeginPlay(&beginData);
|
EditorScene::BeginPlay(&beginData);
|
||||||
beginData.OnDone();
|
beginData.OnDone();
|
||||||
|
|
||||||
|
// Mark as internal to prevent collection in ManagedEditor::WipeOutLeftoverSceneObjects
|
||||||
|
Tags.Add(Tags::Get(TEXT("__EditorInternal")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorScene::Update()
|
void EditorScene::Update()
|
||||||
|
|||||||
@@ -138,7 +138,9 @@ namespace FlaxEditor.Viewport
|
|||||||
if (useProjectCache)
|
if (useProjectCache)
|
||||||
{
|
{
|
||||||
// Initialize snapping enabled from cached values
|
// Initialize snapping enabled from cached values
|
||||||
if (editor.ProjectCache.TryGetCustomData("TranslateSnapState", out bool cachedBool))
|
if (editor.ProjectCache.TryGetCustomData("AbsoluteSnapState", out bool cachedBool))
|
||||||
|
transformGizmo.AbsoluteSnapEnabled = cachedBool;
|
||||||
|
if (editor.ProjectCache.TryGetCustomData("TranslateSnapState", out cachedBool))
|
||||||
transformGizmo.TranslationSnapEnable = cachedBool;
|
transformGizmo.TranslationSnapEnable = cachedBool;
|
||||||
if (editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedBool))
|
if (editor.ProjectCache.TryGetCustomData("RotationSnapState", out cachedBool))
|
||||||
transformGizmo.RotationSnapEnabled = cachedBool;
|
transformGizmo.RotationSnapEnabled = cachedBool;
|
||||||
@@ -162,13 +164,31 @@ namespace FlaxEditor.Viewport
|
|||||||
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
|
TooltipText = $"Gizmo transform space (world or local) ({inputOptions.ToggleTransformSpace})",
|
||||||
Parent = transformSpaceWidget
|
Parent = transformSpaceWidget
|
||||||
};
|
};
|
||||||
|
transformSpaceWidget.Parent = viewport;
|
||||||
|
|
||||||
|
// Absolute snapping widget
|
||||||
|
var absoluteSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
var enableAbsoluteSnapping = new ViewportWidgetButton("A", SpriteHandle.Invalid, null, true)
|
||||||
|
{
|
||||||
|
Checked = transformGizmo.AbsoluteSnapEnabled,
|
||||||
|
TooltipText = "Enable absolute grid snapping (world-space absolute grid, rather than object-relative grid)",
|
||||||
|
Parent = absoluteSnappingWidget
|
||||||
|
};
|
||||||
|
enableAbsoluteSnapping.Toggled += _ =>
|
||||||
|
{
|
||||||
|
transformGizmo.AbsoluteSnapEnabled = !transformGizmo.AbsoluteSnapEnabled;
|
||||||
|
if (useProjectCache)
|
||||||
|
editor.ProjectCache.SetCustomData("AbsoluteSnapState", transformGizmo.AbsoluteSnapEnabled);
|
||||||
|
};
|
||||||
|
absoluteSnappingWidget.Parent = viewport;
|
||||||
|
|
||||||
transformSpaceToggle.Toggled += _ =>
|
transformSpaceToggle.Toggled += _ =>
|
||||||
{
|
{
|
||||||
transformGizmo.ToggleTransformSpace();
|
transformGizmo.ToggleTransformSpace();
|
||||||
if (useProjectCache)
|
if (useProjectCache)
|
||||||
editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString());
|
editor.ProjectCache.SetCustomData("TransformSpaceState", transformGizmo.ActiveTransformSpace.ToString());
|
||||||
|
absoluteSnappingWidget.Visible = transformGizmo.ActiveTransformSpace == TransformGizmoBase.TransformSpace.World;
|
||||||
};
|
};
|
||||||
transformSpaceWidget.Parent = viewport;
|
|
||||||
|
|
||||||
// Scale snapping widget
|
// Scale snapping widget
|
||||||
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
var scaleSnappingWidget = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
@@ -383,17 +403,17 @@ namespace FlaxEditor.Viewport
|
|||||||
gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate;
|
gizmoModeRotate.Checked = mode == TransformGizmoBase.Mode.Rotate;
|
||||||
gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale;
|
gizmoModeScale.Checked = mode == TransformGizmoBase.Mode.Scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup input actions
|
// Setup input actions
|
||||||
viewport.InputActions.Add(options => options.TranslateMode, () =>
|
viewport.InputActions.Add(options => options.TranslateMode, () =>
|
||||||
{
|
{
|
||||||
viewport.GetInput(out var input);
|
viewport.GetInput(out var input);
|
||||||
if (input.IsMouseRightDown)
|
if (input.IsMouseRightDown)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate;
|
transformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate;
|
||||||
});
|
});
|
||||||
viewport.InputActions.Add(options => options.RotateMode, () =>
|
viewport.InputActions.Add(options => options.RotateMode, () =>
|
||||||
{
|
{
|
||||||
viewport.GetInput(out var input);
|
viewport.GetInput(out var input);
|
||||||
if (input.IsMouseRightDown)
|
if (input.IsMouseRightDown)
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ using FlaxEditor.Gizmo;
|
|||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Tools;
|
||||||
using FlaxEditor.Viewport.Modes;
|
using FlaxEditor.Viewport.Modes;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.Gizmo;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEngine.Tools;
|
||||||
|
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport
|
namespace FlaxEditor.Viewport
|
||||||
@@ -107,6 +111,7 @@ namespace FlaxEditor.Viewport
|
|||||||
private double _lockedFocusOffset;
|
private double _lockedFocusOffset;
|
||||||
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
||||||
private EditorSpritesRenderer _editorSpritesRenderer;
|
private EditorSpritesRenderer _editorSpritesRenderer;
|
||||||
|
private ViewportRubberBandSelector _rubberBandSelector;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drag and drop handlers
|
/// Drag and drop handlers
|
||||||
@@ -213,6 +218,9 @@ namespace FlaxEditor.Viewport
|
|||||||
TransformGizmo.ApplyTransformation += ApplyTransform;
|
TransformGizmo.ApplyTransformation += ApplyTransform;
|
||||||
TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate;
|
TransformGizmo.Duplicate += _editor.SceneEditing.Duplicate;
|
||||||
Gizmos.Active = TransformGizmo;
|
Gizmos.Active = TransformGizmo;
|
||||||
|
|
||||||
|
// Add rubber band selector
|
||||||
|
_rubberBandSelector = new ViewportRubberBandSelector(this);
|
||||||
|
|
||||||
// Add grid
|
// Add grid
|
||||||
Grid = new GridGizmo(this);
|
Grid = new GridGizmo(this);
|
||||||
@@ -367,7 +375,10 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
Gizmos[i].Draw(ref renderContext);
|
Gizmos[i].Draw(ref renderContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw RubberBand for rect selection
|
||||||
|
_rubberBandSelector.Draw(context, target, targetDepth);
|
||||||
|
|
||||||
// Draw selected objects debug shapes and visuals
|
// Draw selected objects debug shapes and visuals
|
||||||
if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw)
|
if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw)
|
||||||
{
|
{
|
||||||
@@ -478,6 +489,22 @@ namespace FlaxEditor.Viewport
|
|||||||
TransformGizmo.EndTransforming();
|
TransformGizmo.EndTransforming();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnLostFocus()
|
||||||
|
{
|
||||||
|
base.OnLostFocus();
|
||||||
|
|
||||||
|
_rubberBandSelector.StopRubberBand();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnMouseLeave()
|
||||||
|
{
|
||||||
|
base.OnMouseLeave();
|
||||||
|
|
||||||
|
_rubberBandSelector.StopRubberBand();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Focuses the viewport on the current selection of the gizmo.
|
/// Focuses the viewport on the current selection of the gizmo.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -576,6 +603,24 @@ namespace FlaxEditor.Viewport
|
|||||||
base.OrientViewport(ref orientation);
|
base.OrientViewport(ref orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnMouseMove(Float2 location)
|
||||||
|
{
|
||||||
|
base.OnMouseMove(location);
|
||||||
|
|
||||||
|
// Don't allow rubber band selection when gizmo is controlling mouse, vertex painting mode, or cloth painting is enabled
|
||||||
|
bool canStart = !((Gizmos.Active.IsControllingMouse || Gizmos.Active is VertexPaintingGizmo || Gizmos.Active is ClothPaintingGizmo) || IsControllingMouse || IsRightMouseButtonDown || IsAltKeyDown);
|
||||||
|
_rubberBandSelector.TryCreateRubberBand(canStart, _viewMousePos, ViewFrustum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnLeftMouseButtonDown()
|
||||||
|
{
|
||||||
|
base.OnLeftMouseButtonDown();
|
||||||
|
|
||||||
|
_rubberBandSelector.TryStartingRubberBandSelection();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnLeftMouseButtonUp()
|
protected override void OnLeftMouseButtonUp()
|
||||||
{
|
{
|
||||||
@@ -583,8 +628,12 @@ namespace FlaxEditor.Viewport
|
|||||||
if (_prevInput.IsControllingMouse || !Bounds.Contains(ref _viewMousePos))
|
if (_prevInput.IsControllingMouse || !Bounds.Contains(ref _viewMousePos))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Try to pick something with the current gizmo
|
// Select rubberbanded rect actor nodes or pick with gizmo
|
||||||
Gizmos.Active?.Pick();
|
if (!_rubberBandSelector.ReleaseRubberBandSelection())
|
||||||
|
{
|
||||||
|
// Try to pick something with the current gizmo
|
||||||
|
Gizmos.Active?.Pick();
|
||||||
|
}
|
||||||
|
|
||||||
// Keep focus
|
// Keep focus
|
||||||
Focus();
|
Focus();
|
||||||
@@ -592,6 +641,22 @@ namespace FlaxEditor.Viewport
|
|||||||
base.OnLeftMouseButtonUp();
|
base.OnLeftMouseButtonUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseUp(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Handle mouse going up when using rubber band with mouse capture that click up outside the view
|
||||||
|
if (button == MouseButton.Left && !new Rectangle(Float2.Zero, Size).Contains(ref location))
|
||||||
|
{
|
||||||
|
_rubberBandSelector.ReleaseRubberBandSelection();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
private readonly AnimationWindow _window;
|
private readonly AnimationWindow _window;
|
||||||
private AnimationGraph _animGraph;
|
private AnimationGraph _animGraph;
|
||||||
|
|
||||||
|
public SkinnedModel BaseModel;
|
||||||
|
|
||||||
public Preview(AnimationWindow window)
|
public Preview(AnimationWindow window)
|
||||||
: base(true)
|
: base(true)
|
||||||
{
|
{
|
||||||
@@ -46,10 +48,11 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
Object.Destroy(ref _animGraph);
|
Object.Destroy(ref _animGraph);
|
||||||
if (!model)
|
if (!model)
|
||||||
return;
|
return;
|
||||||
|
var baseModel = BaseModel ?? model;
|
||||||
|
|
||||||
// Use virtual animation graph to playback the animation
|
// Use virtual animation graph to playback the animation
|
||||||
_animGraph = FlaxEngine.Content.CreateVirtualAsset<AnimationGraph>();
|
_animGraph = FlaxEngine.Content.CreateVirtualAsset<AnimationGraph>();
|
||||||
_animGraph.InitAsAnimation(model, _window.Asset, true, true);
|
_animGraph.InitAsAnimation(baseModel, _window.Asset, true, true);
|
||||||
PreviewActor.AnimationGraph = _animGraph;
|
PreviewActor.AnimationGraph = _animGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +72,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
BaseModel = null;
|
||||||
Object.Destroy(ref _animGraph);
|
Object.Destroy(ref _animGraph);
|
||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
@@ -148,6 +152,24 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ShowBaseModel => PreviewModel != null;
|
||||||
|
|
||||||
|
[EditorDisplay("Preview"), NoSerialize, AssetReference(true), VisibleIf(nameof(ShowBaseModel))]
|
||||||
|
[Tooltip("The skinned model to use as a retarget source. Animation will be played using its skeleton and retarget into the Preview Model.")]
|
||||||
|
public SkinnedModel BaseModel
|
||||||
|
{
|
||||||
|
get => Window?._preview?.BaseModel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Window == null || PreviewModel == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Reinit
|
||||||
|
Window._preview.BaseModel = value;
|
||||||
|
Window._preview.SetModel(PreviewModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void OnLoad(AnimationWindow window)
|
public void OnLoad(AnimationWindow window)
|
||||||
{
|
{
|
||||||
// Link
|
// Link
|
||||||
@@ -230,7 +252,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
private ToolStripButton _undoButton;
|
private ToolStripButton _undoButton;
|
||||||
private ToolStripButton _redoButton;
|
private ToolStripButton _redoButton;
|
||||||
private bool _isWaitingForTimelineLoad;
|
private bool _isWaitingForTimelineLoad;
|
||||||
private SkinnedModel _initialPreviewModel;
|
private SkinnedModel _initialPreviewModel, _initialBaseModel;
|
||||||
private float _initialPanel2Splitter = 0.6f;
|
private float _initialPanel2Splitter = 0.6f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -322,7 +344,12 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_properties.PreviewModel = _initialPreviewModel;
|
_properties.PreviewModel = _initialPreviewModel;
|
||||||
_panel2.SplitterValue = _initialPanel2Splitter;
|
_panel2.SplitterValue = _initialPanel2Splitter;
|
||||||
_initialPreviewModel = null;
|
_initialPreviewModel = null;
|
||||||
|
if (_initialBaseModel)
|
||||||
|
{
|
||||||
|
_properties.BaseModel = _initialBaseModel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_initialBaseModel = null;
|
||||||
|
|
||||||
base.OnAssetLoaded();
|
base.OnAssetLoaded();
|
||||||
}
|
}
|
||||||
@@ -412,6 +439,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
writer.WriteAttributeString("ShowPreviewValues", _timeline.ShowPreviewValues.ToString());
|
writer.WriteAttributeString("ShowPreviewValues", _timeline.ShowPreviewValues.ToString());
|
||||||
if (_properties.PreviewModel)
|
if (_properties.PreviewModel)
|
||||||
writer.WriteAttributeString("PreviewModel", _properties.PreviewModel.ID.ToString());
|
writer.WriteAttributeString("PreviewModel", _properties.PreviewModel.ID.ToString());
|
||||||
|
if (_properties.BaseModel)
|
||||||
|
writer.WriteAttributeString("BaseModel", _properties.BaseModel.ID.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -427,6 +456,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_timeline.ShowPreviewValues = value3;
|
_timeline.ShowPreviewValues = value3;
|
||||||
if (Guid.TryParse(node.GetAttribute("PreviewModel"), out Guid value4))
|
if (Guid.TryParse(node.GetAttribute("PreviewModel"), out Guid value4))
|
||||||
_initialPreviewModel = FlaxEngine.Content.LoadAsync<SkinnedModel>(value4);
|
_initialPreviewModel = FlaxEngine.Content.LoadAsync<SkinnedModel>(value4);
|
||||||
|
if (Guid.TryParse(node.GetAttribute("BaseModel"), out value4))
|
||||||
|
_initialBaseModel = FlaxEngine.Content.LoadAsync<SkinnedModel>(value4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System.IO;
|
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.Content.Import;
|
using FlaxEditor.Content.Import;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace FlaxEditor.Windows
|
|||||||
{
|
{
|
||||||
private readonly RenderOutputControl _viewport;
|
private readonly RenderOutputControl _viewport;
|
||||||
private readonly GameRoot _guiRoot;
|
private readonly GameRoot _guiRoot;
|
||||||
private bool _showGUI = true;
|
private bool _showGUI = true, _editGUI = true;
|
||||||
private bool _showDebugDraw = false;
|
private bool _showDebugDraw = false;
|
||||||
private bool _audioMuted = false;
|
private bool _audioMuted = false;
|
||||||
private float _audioVolume = 1;
|
private float _audioVolume = 1;
|
||||||
@@ -84,6 +84,22 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether allow editing game GUI in the view or keep it visible-only.
|
||||||
|
/// </summary>
|
||||||
|
public bool EditGUI
|
||||||
|
{
|
||||||
|
get => _editGUI;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != _editGUI)
|
||||||
|
{
|
||||||
|
_editGUI = value;
|
||||||
|
_guiRoot.Editable = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether show Debug Draw shapes in the view or keep it hidden.
|
/// Gets or sets a value indicating whether show Debug Draw shapes in the view or keep it hidden.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -275,8 +291,9 @@ namespace FlaxEditor.Windows
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private class GameRoot : UIEditorRoot
|
private class GameRoot : UIEditorRoot
|
||||||
{
|
{
|
||||||
public override bool EnableInputs => !Time.GamePaused && Editor.IsPlayMode;
|
internal bool Editable = true;
|
||||||
public override bool EnableSelecting => !Editor.IsPlayMode || Time.GamePaused;
|
public override bool EnableInputs => !Time.GamePaused && Editor.IsPlayMode && Editable;
|
||||||
|
public override bool EnableSelecting => (!Editor.IsPlayMode || Time.GamePaused) && Editable;
|
||||||
public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo;
|
public override TransformGizmo TransformGizmo => Editor.Instance.MainTransformGizmo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,6 +687,13 @@ namespace FlaxEditor.Windows
|
|||||||
checkbox.StateChanged += x => ShowGUI = x.Checked;
|
checkbox.StateChanged += x => ShowGUI = x.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Edit GUI
|
||||||
|
{
|
||||||
|
var button = menu.AddButton("Edit GUI");
|
||||||
|
var checkbox = new CheckBox(140, 2, EditGUI) { Parent = button };
|
||||||
|
checkbox.StateChanged += x => EditGUI = x.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
// Show Debug Draw
|
// Show Debug Draw
|
||||||
{
|
{
|
||||||
var button = menu.AddButton("Show Debug Draw");
|
var button = menu.AddButton("Show Debug Draw");
|
||||||
@@ -1196,6 +1220,7 @@ namespace FlaxEditor.Windows
|
|||||||
public override void OnLayoutSerialize(XmlWriter writer)
|
public override void OnLayoutSerialize(XmlWriter writer)
|
||||||
{
|
{
|
||||||
writer.WriteAttributeString("ShowGUI", ShowGUI.ToString());
|
writer.WriteAttributeString("ShowGUI", ShowGUI.ToString());
|
||||||
|
writer.WriteAttributeString("EditGUI", EditGUI.ToString());
|
||||||
writer.WriteAttributeString("ShowDebugDraw", ShowDebugDraw.ToString());
|
writer.WriteAttributeString("ShowDebugDraw", ShowDebugDraw.ToString());
|
||||||
writer.WriteAttributeString("DefaultViewportScaling", JsonSerializer.Serialize(_defaultViewportScaling));
|
writer.WriteAttributeString("DefaultViewportScaling", JsonSerializer.Serialize(_defaultViewportScaling));
|
||||||
writer.WriteAttributeString("CustomViewportScaling", JsonSerializer.Serialize(_customViewportScaling));
|
writer.WriteAttributeString("CustomViewportScaling", JsonSerializer.Serialize(_customViewportScaling));
|
||||||
@@ -1206,6 +1231,8 @@ namespace FlaxEditor.Windows
|
|||||||
{
|
{
|
||||||
if (bool.TryParse(node.GetAttribute("ShowGUI"), out bool value1))
|
if (bool.TryParse(node.GetAttribute("ShowGUI"), out bool value1))
|
||||||
ShowGUI = value1;
|
ShowGUI = value1;
|
||||||
|
if (bool.TryParse(node.GetAttribute("EditGUI"), out value1))
|
||||||
|
EditGUI = value1;
|
||||||
if (bool.TryParse(node.GetAttribute("ShowDebugDraw"), out value1))
|
if (bool.TryParse(node.GetAttribute("ShowDebugDraw"), out value1))
|
||||||
ShowDebugDraw = value1;
|
ShowDebugDraw = value1;
|
||||||
if (node.HasAttribute("CustomViewportScaling"))
|
if (node.HasAttribute("CustomViewportScaling"))
|
||||||
@@ -1231,6 +1258,7 @@ namespace FlaxEditor.Windows
|
|||||||
public override void OnLayoutDeserialize()
|
public override void OnLayoutDeserialize()
|
||||||
{
|
{
|
||||||
ShowGUI = true;
|
ShowGUI = true;
|
||||||
|
EditGUI = true;
|
||||||
ShowDebugDraw = false;
|
ShowDebugDraw = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ namespace FlaxEditor.Windows
|
|||||||
if (Level.ScenesCount > 1)
|
if (Level.ScenesCount > 1)
|
||||||
return;
|
return;
|
||||||
_actorScrollValues.Clear();
|
_actorScrollValues.Clear();
|
||||||
|
if (LockObjects)
|
||||||
|
{
|
||||||
|
LockObjects = false;
|
||||||
|
Presenter.Deselect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnScrollValueChanged()
|
private void OnScrollValueChanged()
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt)
|
|||||||
context.EmptyNodes.Length = 0.0f;
|
context.EmptyNodes.Length = 0.0f;
|
||||||
context.EmptyNodes.Nodes.Resize(_skeletonNodesCount, false);
|
context.EmptyNodes.Nodes.Resize(_skeletonNodesCount, false);
|
||||||
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
for (int32 i = 0; i < _skeletonNodesCount; i++)
|
||||||
context.EmptyNodes.Nodes[i] = skeleton.Nodes[i].LocalTransform;
|
context.EmptyNodes.Nodes.Get()[i] = skeleton.Nodes.Get()[i].LocalTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the animation graph and gather skeleton nodes transformations in nodes local space
|
// Update the animation graph and gather skeleton nodes transformations in nodes local space
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ namespace ALC
|
|||||||
struct SourceData
|
struct SourceData
|
||||||
{
|
{
|
||||||
AudioDataInfo Format;
|
AudioDataInfo Format;
|
||||||
|
float Pan;
|
||||||
bool Spatial;
|
bool Spatial;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,6 +107,26 @@ namespace ALC
|
|||||||
|
|
||||||
namespace Source
|
namespace Source
|
||||||
{
|
{
|
||||||
|
void SetupSpatial(uint32 sourceID, float pan, bool spatial)
|
||||||
|
{
|
||||||
|
alSourcei(sourceID, AL_SOURCE_RELATIVE, !spatial); // Non-spatial sounds use AL_POSITION for panning
|
||||||
|
#ifdef AL_SOFT_source_spatialize
|
||||||
|
alSourcei(sourceID, AL_SOURCE_SPATIALIZE_SOFT, spatial || Math::Abs(pan) > ZeroTolerance ? AL_TRUE : AL_FALSE); // Fix multi-channel sources played as spatial or non-spatial sources played with panning
|
||||||
|
#endif
|
||||||
|
if (spatial)
|
||||||
|
{
|
||||||
|
#ifdef AL_EXT_STEREO_ANGLES
|
||||||
|
const float panAngle = pan * PI_HALF;
|
||||||
|
const ALfloat panAngles[2] = { (ALfloat)(PI / 6.0 - panAngle), (ALfloat)(-PI / 6.0 - panAngle) }; // Angles are specified counter-clockwise in radians
|
||||||
|
alSourcefv(sourceID, AL_STEREO_ANGLES, panAngles);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alSource3f(sourceID, AL_POSITION, pan, 0, -sqrtf(1.0f - pan * pan));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Rebuild(uint32& sourceID, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
|
void Rebuild(uint32& sourceID, const Vector3& position, const Quaternion& orientation, float volume, float pitch, float pan, bool loop, bool spatial, float attenuation, float minDistance, float doppler)
|
||||||
{
|
{
|
||||||
ASSERT_LOW_LAYER(sourceID == 0);
|
ASSERT_LOW_LAYER(sourceID == 0);
|
||||||
@@ -116,11 +137,8 @@ namespace ALC
|
|||||||
alSourcef(sourceID, AL_PITCH, pitch);
|
alSourcef(sourceID, AL_PITCH, pitch);
|
||||||
alSourcef(sourceID, AL_SEC_OFFSET, 0.0f);
|
alSourcef(sourceID, AL_SEC_OFFSET, 0.0f);
|
||||||
alSourcei(sourceID, AL_LOOPING, loop);
|
alSourcei(sourceID, AL_LOOPING, loop);
|
||||||
alSourcei(sourceID, AL_SOURCE_RELATIVE, !spatial); // Non-spatial sounds use AL_POSITION for panning
|
|
||||||
alSourcei(sourceID, AL_BUFFER, 0);
|
alSourcei(sourceID, AL_BUFFER, 0);
|
||||||
#ifdef AL_SOFT_source_spatialize
|
SetupSpatial(sourceID, pan, spatial);
|
||||||
alSourcei(sourceID, AL_SOURCE_SPATIALIZE_SOFT, AL_TRUE); // Always spatialize, fixes multi-channel played as spatial
|
|
||||||
#endif
|
|
||||||
if (spatial)
|
if (spatial)
|
||||||
{
|
{
|
||||||
alSourcef(sourceID, AL_ROLLOFF_FACTOR, attenuation);
|
alSourcef(sourceID, AL_ROLLOFF_FACTOR, attenuation);
|
||||||
@@ -128,18 +146,12 @@ namespace ALC
|
|||||||
alSourcef(sourceID, AL_REFERENCE_DISTANCE, FLAX_DST_TO_OAL(minDistance));
|
alSourcef(sourceID, AL_REFERENCE_DISTANCE, FLAX_DST_TO_OAL(minDistance));
|
||||||
alSource3f(sourceID, AL_POSITION, FLAX_POS_TO_OAL(position));
|
alSource3f(sourceID, AL_POSITION, FLAX_POS_TO_OAL(position));
|
||||||
alSource3f(sourceID, AL_VELOCITY, FLAX_VEL_TO_OAL(Vector3::Zero));
|
alSource3f(sourceID, AL_VELOCITY, FLAX_VEL_TO_OAL(Vector3::Zero));
|
||||||
#ifdef AL_EXT_STEREO_ANGLES
|
|
||||||
const float panAngle = pan * PI_HALF;
|
|
||||||
const ALfloat panAngles[2] = { (ALfloat)(PI / 6.0 - panAngle), (ALfloat)(-PI / 6.0 - panAngle) }; // Angles are specified counter-clockwise in radians
|
|
||||||
alSourcefv(sourceID, AL_STEREO_ANGLES, panAngles);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
alSourcef(sourceID, AL_ROLLOFF_FACTOR, 0.0f);
|
alSourcef(sourceID, AL_ROLLOFF_FACTOR, 0.0f);
|
||||||
alSourcef(sourceID, AL_DOPPLER_FACTOR, 1.0f);
|
alSourcef(sourceID, AL_DOPPLER_FACTOR, 1.0f);
|
||||||
alSourcef(sourceID, AL_REFERENCE_DISTANCE, 0.0f);
|
alSourcef(sourceID, AL_REFERENCE_DISTANCE, 0.0f);
|
||||||
alSource3f(sourceID, AL_POSITION, pan, 0, -sqrtf(1.0f - pan * pan));
|
|
||||||
alSource3f(sourceID, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
alSource3f(sourceID, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,12 +172,11 @@ namespace ALC
|
|||||||
if (Device == nullptr)
|
if (Device == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ALCint attrsHrtf[] = { ALC_HRTF_SOFT, ALC_TRUE };
|
ALCint attrList[] = { ALC_HRTF_SOFT, ALC_FALSE };
|
||||||
const ALCint* attrList = nullptr;
|
|
||||||
if (Audio::GetEnableHRTF())
|
if (Audio::GetEnableHRTF())
|
||||||
{
|
{
|
||||||
LOG(Info, "Enabling OpenAL HRTF");
|
LOG(Info, "Enabling OpenAL HRTF");
|
||||||
attrList = attrsHrtf;
|
attrList[1] = ALC_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context = alcCreateContext(Device, attrList);
|
Context = alcCreateContext(Device, attrList);
|
||||||
@@ -318,6 +329,7 @@ uint32 AudioBackendOAL::Source_Add(const AudioDataInfo& format, const Vector3& p
|
|||||||
auto& data = ALC::SourcesData[sourceID];
|
auto& data = ALC::SourcesData[sourceID];
|
||||||
data.Format = format;
|
data.Format = format;
|
||||||
data.Spatial = spatial;
|
data.Spatial = spatial;
|
||||||
|
data.Pan = pan;
|
||||||
ALC::Locker.Unlock();
|
ALC::Locker.Unlock();
|
||||||
|
|
||||||
return sourceID;
|
return sourceID;
|
||||||
@@ -370,20 +382,11 @@ void AudioBackendOAL::Source_PitchChanged(uint32 sourceID, float pitch)
|
|||||||
void AudioBackendOAL::Source_PanChanged(uint32 sourceID, float pan)
|
void AudioBackendOAL::Source_PanChanged(uint32 sourceID, float pan)
|
||||||
{
|
{
|
||||||
ALC::Locker.Lock();
|
ALC::Locker.Lock();
|
||||||
const bool spatial = ALC::SourcesData[sourceID].Spatial;
|
auto& e = ALC::SourcesData[sourceID];
|
||||||
|
e.Pan = pan;
|
||||||
|
const bool spatial = e.Spatial;
|
||||||
ALC::Locker.Unlock();
|
ALC::Locker.Unlock();
|
||||||
if (spatial)
|
ALC::Source::SetupSpatial(sourceID, pan, spatial);
|
||||||
{
|
|
||||||
#ifdef AL_EXT_STEREO_ANGLES
|
|
||||||
const float panAngle = pan * PI_HALF;
|
|
||||||
const ALfloat panAngles[2] = { (ALfloat)(PI / 6.0 - panAngle), (ALfloat)(-PI / 6.0 - panAngle) }; // Angles are specified counter-clockwise in radians
|
|
||||||
alSourcefv(sourceID, AL_STEREO_ANGLES, panAngles);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alSource3f(sourceID, AL_POSITION, pan, 0, -sqrtf(1.0f - pan * pan));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBackendOAL::Source_IsLoopingChanged(uint32 sourceID, bool loop)
|
void AudioBackendOAL::Source_IsLoopingChanged(uint32 sourceID, bool loop)
|
||||||
@@ -393,6 +396,9 @@ void AudioBackendOAL::Source_IsLoopingChanged(uint32 sourceID, bool loop)
|
|||||||
|
|
||||||
void AudioBackendOAL::Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler)
|
void AudioBackendOAL::Source_SpatialSetupChanged(uint32 sourceID, bool spatial, float attenuation, float minDistance, float doppler)
|
||||||
{
|
{
|
||||||
|
ALC::Locker.Lock();
|
||||||
|
const bool pan = ALC::SourcesData[sourceID].Spatial;
|
||||||
|
ALC::Locker.Unlock();
|
||||||
if (spatial)
|
if (spatial)
|
||||||
{
|
{
|
||||||
alSourcef(sourceID, AL_ROLLOFF_FACTOR, attenuation);
|
alSourcef(sourceID, AL_ROLLOFF_FACTOR, attenuation);
|
||||||
@@ -405,6 +411,7 @@ void AudioBackendOAL::Source_SpatialSetupChanged(uint32 sourceID, bool spatial,
|
|||||||
alSourcef(sourceID, AL_DOPPLER_FACTOR, 1.0f);
|
alSourcef(sourceID, AL_DOPPLER_FACTOR, 1.0f);
|
||||||
alSourcef(sourceID, AL_REFERENCE_DISTANCE, 0.0f);
|
alSourcef(sourceID, AL_REFERENCE_DISTANCE, 0.0f);
|
||||||
}
|
}
|
||||||
|
ALC::Source::SetupSpatial(sourceID, pan, spatial);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBackendOAL::Source_Play(uint32 sourceID)
|
void AudioBackendOAL::Source_Play(uint32 sourceID)
|
||||||
@@ -602,7 +609,7 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferID, byte* samples, const AudioDa
|
|||||||
|
|
||||||
if (!format)
|
if (!format)
|
||||||
{
|
{
|
||||||
LOG(Error, "Not suppported audio data format for OpenAL device: BitDepth={}, NumChannels={}", info.BitDepth, info.NumChannels);
|
LOG(Error, "Not supported audio data format for OpenAL device: BitDepth={}, NumChannels={}", info.BitDepth, info.NumChannels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,19 +159,19 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Split audio data into a several chunks (uniform data spread)
|
// Split audio data into a several chunks (uniform data spread)
|
||||||
const int32 minChunkSize = 1 * 1024 * 1024; // 1 MB
|
const uint32 minChunkSize = 1 * 1024 * 1024; // 1 MB
|
||||||
const int32 dataAlignment = info.NumChannels * bytesPerSample; // Ensure to never split samples in-between (eg. 24-bit that uses 3 bytes)
|
const uint32 dataAlignment = info.NumChannels * bytesPerSample * ASSET_FILE_DATA_CHUNKS; // Ensure to never split samples in-between (eg. 24-bit that uses 3 bytes)
|
||||||
const int32 chunkSize = Math::Max<int32>(minChunkSize, (int32)Math::AlignUp<uint32>(bufferSize / ASSET_FILE_DATA_CHUNKS, dataAlignment));
|
const uint32 chunkSize = Math::AlignUp(Math::Max(minChunkSize, bufferSize / ASSET_FILE_DATA_CHUNKS), dataAlignment);
|
||||||
const int32 chunksCount = Math::CeilToInt((float)bufferSize / chunkSize);
|
const int32 chunksCount = Math::CeilToInt((float)bufferSize / (float)chunkSize);
|
||||||
ASSERT(chunksCount > 0 && chunksCount <= ASSET_FILE_DATA_CHUNKS);
|
ASSERT(chunksCount > 0 && chunksCount <= ASSET_FILE_DATA_CHUNKS);
|
||||||
|
|
||||||
byte* ptr = sampleBuffer.Get();
|
byte* ptr = sampleBuffer.Get();
|
||||||
int32 size = bufferSize;
|
uint32 size = bufferSize;
|
||||||
for (int32 chunkIndex = 0; chunkIndex < chunksCount; chunkIndex++)
|
for (int32 chunkIndex = 0; chunkIndex < chunksCount; chunkIndex++)
|
||||||
{
|
{
|
||||||
if (context.AllocateChunk(chunkIndex))
|
if (context.AllocateChunk(chunkIndex))
|
||||||
return CreateAssetResult::CannotAllocateChunk;
|
return CreateAssetResult::CannotAllocateChunk;
|
||||||
const int32 t = Math::Min(size, chunkSize);
|
const uint32 t = Math::Min(size, chunkSize);
|
||||||
|
|
||||||
WRITE_DATA(chunkIndex, ptr, t);
|
WRITE_DATA(chunkIndex, ptr, t);
|
||||||
|
|
||||||
|
|||||||
@@ -520,4 +520,24 @@ PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
Array<Vector3> Mesh::GetCollisionProxyPoints() const
|
||||||
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
|
Array<Vector3> result;
|
||||||
|
#if USE_PRECISE_MESH_INTERSECTS
|
||||||
|
for (int32 i = 0; i < _collisionProxy.Triangles.Count(); i++)
|
||||||
|
{
|
||||||
|
auto triangle = _collisionProxy.Triangles.Get()[i];
|
||||||
|
result.Add(triangle.V0);
|
||||||
|
result.Add(triangle.V1);
|
||||||
|
result.Add(triangle.V2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -536,5 +536,16 @@ namespace FlaxEngine
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FLAX_EDITOR
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the collision proxy points for the mesh.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The triangle points in the collision proxy.</returns>
|
||||||
|
internal Vector3[] GetCollisionProxyPoints()
|
||||||
|
{
|
||||||
|
return Internal_GetCollisionProxyPoints(__unmanagedPtr, out _);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,5 +171,8 @@ private:
|
|||||||
API_FUNCTION(NoProxy) bool UpdateMeshUInt(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj);
|
API_FUNCTION(NoProxy) bool UpdateMeshUInt(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj);
|
||||||
API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj);
|
API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj);
|
||||||
API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI);
|
API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI);
|
||||||
|
#if USE_EDITOR
|
||||||
|
API_FUNCTION(NoProxy) Array<Vector3> GetCollisionProxyPoints() const;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public:
|
|||||||
int32 LightmapUVsIndex = -1;
|
int32 LightmapUVsIndex = -1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Global translation for this mesh to be at its local origin.
|
/// Local translation for this mesh to be at it's local origin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Vector3 OriginTranslation = Vector3::Zero;
|
Vector3 OriginTranslation = Vector3::Zero;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ ModelInstanceActor::ModelInstanceActor(const SpawnParams& params)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String ModelInstanceActor::MeshReference::ToString() const
|
||||||
|
{
|
||||||
|
return String::Format(TEXT("Actor={},LOD={},Mesh={}"), Actor ? Actor->GetNamePath() : String::Empty, LODIndex, MeshIndex);
|
||||||
|
}
|
||||||
|
|
||||||
void ModelInstanceActor::SetEntries(const Array<ModelInstanceEntry>& value)
|
void ModelInstanceActor::SetEntries(const Array<ModelInstanceEntry>& value)
|
||||||
{
|
{
|
||||||
WaitForModelLoad();
|
WaitForModelLoad();
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ API_CLASS(Abstract) class FLAXENGINE_API ModelInstanceActor : public Actor
|
|||||||
API_FIELD() int32 LODIndex = 0;
|
API_FIELD() int32 LODIndex = 0;
|
||||||
// Index of the mesh (within the LOD).
|
// Index of the mesh (within the LOD).
|
||||||
API_FIELD() int32 MeshIndex = 0;
|
API_FIELD() int32 MeshIndex = 0;
|
||||||
|
|
||||||
|
String ToString() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -647,6 +647,7 @@ void Cloth::CalculateInvMasses(Array<float>& invMasses)
|
|||||||
if (_paint.Count() != verticesCount)
|
if (_paint.Count() != verticesCount)
|
||||||
{
|
{
|
||||||
// Fix incorrect paint data
|
// Fix incorrect paint data
|
||||||
|
LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), mesh.ToString(), verticesCount);
|
||||||
int32 countBefore = _paint.Count();
|
int32 countBefore = _paint.Count();
|
||||||
_paint.Resize(verticesCount);
|
_paint.Resize(verticesCount);
|
||||||
for (int32 i = countBefore; i < verticesCount; i++)
|
for (int32 i = countBefore; i < verticesCount; i++)
|
||||||
@@ -795,7 +796,10 @@ bool Cloth::OnPreUpdate()
|
|||||||
if (!positionStream.IsValid() || !blendIndicesStream.IsValid() || !blendWeightsStream.IsValid())
|
if (!positionStream.IsValid() || !blendIndicesStream.IsValid() || !blendWeightsStream.IsValid())
|
||||||
return false;
|
return false;
|
||||||
if (verticesCount != _paint.Count())
|
if (verticesCount != _paint.Count())
|
||||||
|
{
|
||||||
|
LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), mesh.ToString(), verticesCount);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
PROFILE_CPU_NAMED("Skinned Pose");
|
PROFILE_CPU_NAMED("Skinned Pose");
|
||||||
PhysicsBackend::LockClothParticles(_cloth);
|
PhysicsBackend::LockClothParticles(_cloth);
|
||||||
const Span<const Float4> particles = PhysicsBackend::GetClothParticles(_cloth);
|
const Span<const Float4> particles = PhysicsBackend::GetClothParticles(_cloth);
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">The input text to test.</param>
|
/// <param name="text">The input text to test.</param>
|
||||||
/// <param name="layout">The layout properties.</param>
|
/// <param name="layout">The layout properties.</param>
|
||||||
/// <returns>The minimum size for that text and fot to render properly.</returns>
|
/// <returns>The minimum size for that text and font to render properly.</returns>
|
||||||
API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout);
|
API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextLayoutOptions& layout);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -408,7 +408,7 @@ public:
|
|||||||
/// <param name="text">The input text to test.</param>
|
/// <param name="text">The input text to test.</param>
|
||||||
/// <param name="textRange">The input text range (substring range of the input text parameter).</param>
|
/// <param name="textRange">The input text range (substring range of the input text parameter).</param>
|
||||||
/// <param name="layout">The layout properties.</param>
|
/// <param name="layout">The layout properties.</param>
|
||||||
/// <returns>The minimum size for that text and fot to render properly.</returns>
|
/// <returns>The minimum size for that text and font to render properly.</returns>
|
||||||
API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout)
|
API_FUNCTION() Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange, API_PARAM(Ref) const TextLayoutOptions& layout)
|
||||||
{
|
{
|
||||||
return MeasureText(textRange.Substring(text), layout);
|
return MeasureText(textRange.Substring(text), layout);
|
||||||
@@ -418,7 +418,7 @@ public:
|
|||||||
/// Measures minimum size of the rectangle that will be needed to draw given text
|
/// Measures minimum size of the rectangle that will be needed to draw given text
|
||||||
/// </summary>.
|
/// </summary>.
|
||||||
/// <param name="text">The input text to test.</param>
|
/// <param name="text">The input text to test.</param>
|
||||||
/// <returns>The minimum size for that text and fot to render properly.</returns>
|
/// <returns>The minimum size for that text and font to render properly.</returns>
|
||||||
API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text)
|
API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text)
|
||||||
{
|
{
|
||||||
return MeasureText(text, TextLayoutOptions());
|
return MeasureText(text, TextLayoutOptions());
|
||||||
@@ -429,7 +429,7 @@ public:
|
|||||||
/// </summary>.
|
/// </summary>.
|
||||||
/// <param name="text">The input text to test.</param>
|
/// <param name="text">The input text to test.</param>
|
||||||
/// <param name="textRange">The input text range (substring range of the input text parameter).</param>
|
/// <param name="textRange">The input text range (substring range of the input text parameter).</param>
|
||||||
/// <returns>The minimum size for that text and fot to render properly.</returns>
|
/// <returns>The minimum size for that text and font to render properly.</returns>
|
||||||
API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange)
|
API_FUNCTION() FORCE_INLINE Float2 MeasureText(const StringView& text, API_PARAM(Ref) const TextRange& textRange)
|
||||||
{
|
{
|
||||||
return MeasureText(textRange.Substring(text), TextLayoutOptions());
|
return MeasureText(textRange.Substring(text), TextLayoutOptions());
|
||||||
|
|||||||
@@ -482,7 +482,6 @@ bool ShadowsPass::Init()
|
|||||||
|
|
||||||
// Select format for shadow maps
|
// Select format for shadow maps
|
||||||
_shadowMapFormat = PixelFormat::Unknown;
|
_shadowMapFormat = PixelFormat::Unknown;
|
||||||
#if !PLATFORM_SWITCH // TODO: fix shadows performance issue on Switch
|
|
||||||
for (const PixelFormat format : { PixelFormat::D16_UNorm, PixelFormat::D24_UNorm_S8_UInt, PixelFormat::D32_Float })
|
for (const PixelFormat format : { PixelFormat::D16_UNorm, PixelFormat::D24_UNorm_S8_UInt, PixelFormat::D32_Float })
|
||||||
{
|
{
|
||||||
const auto formatTexture = PixelFormatExtensions::FindShaderResourceFormat(format, false);
|
const auto formatTexture = PixelFormatExtensions::FindShaderResourceFormat(format, false);
|
||||||
@@ -495,7 +494,6 @@ bool ShadowsPass::Init()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
if (_shadowMapFormat == PixelFormat::Unknown)
|
if (_shadowMapFormat == PixelFormat::Unknown)
|
||||||
LOG(Warning, "GPU doesn't support shadows rendering");
|
LOG(Warning, "GPU doesn't support shadows rendering");
|
||||||
|
|
||||||
|
|||||||
@@ -732,7 +732,12 @@ Array<ScriptingObject*, HeapAllocation> Scripting::GetObjects()
|
|||||||
{
|
{
|
||||||
Array<ScriptingObject*> objects;
|
Array<ScriptingObject*> objects;
|
||||||
_objectsLocker.Lock();
|
_objectsLocker.Lock();
|
||||||
|
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||||
|
for (const auto& e : _objectsDictionary)
|
||||||
|
objects.Add(e.Value.Ptr);
|
||||||
|
#else
|
||||||
_objectsDictionary.GetValues(objects);
|
_objectsDictionary.GetValues(objects);
|
||||||
|
#endif
|
||||||
_objectsLocker.Unlock();
|
_objectsLocker.Unlock();
|
||||||
return objects;
|
return objects;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ Ray JsonTools::GetRay(const Value& value)
|
|||||||
{
|
{
|
||||||
return Ray(
|
return Ray(
|
||||||
GetVector3(value, "Position", Vector3::Zero),
|
GetVector3(value, "Position", Vector3::Zero),
|
||||||
GetVector3(value, "Direction", Vector3::One)
|
GetVector3(value, "Direction", Vector3::Forward)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "AudioTool.h"
|
#include "AudioTool.h"
|
||||||
#include "Engine/Core/Core.h"
|
#include "Engine/Core/Core.h"
|
||||||
|
#include "Engine/Core/Math/Math.h"
|
||||||
#include "Engine/Core/Memory/Allocation.h"
|
#include "Engine/Core/Memory/Allocation.h"
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
#include "Engine/Serialization/Serialization.h"
|
#include "Engine/Serialization/Serialization.h"
|
||||||
@@ -307,8 +308,9 @@ void AudioTool::ConvertFromFloat(const float* input, int32* output, uint32 numSa
|
|||||||
{
|
{
|
||||||
for (uint32 i = 0; i < numSamples; i++)
|
for (uint32 i = 0; i < numSamples; i++)
|
||||||
{
|
{
|
||||||
const float sample = *(float*)input;
|
float sample = *(float*)input;
|
||||||
output[i] = static_cast<int32>(sample * 2147483647.0f);
|
sample = Math::Clamp(sample, -1.0f + ZeroTolerance, +1.0f - ZeroTolerance);
|
||||||
|
output[i] = static_cast<int32>(sample * 2147483648.0f);
|
||||||
input++;
|
input++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -706,11 +706,11 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
|||||||
const auto texture = eatBox(textureBox->GetParent<Node>(), textureBox->FirstConnection());
|
const auto texture = eatBox(textureBox->GetParent<Node>(), textureBox->FirstConnection());
|
||||||
const auto scale = tryGetValue(node->GetBox(1), node->Values[0]).AsFloat();
|
const auto scale = tryGetValue(node->GetBox(1), node->Values[0]).AsFloat();
|
||||||
const auto blend = tryGetValue(node->GetBox(2), node->Values[1]).AsFloat();
|
const auto blend = tryGetValue(node->GetBox(2), node->Values[1]).AsFloat();
|
||||||
const auto offset = tryGetValue(node->GetBox(3), node->Values.Count() > 2 ? node->Values[2] : Float2::Zero).AsFloat2();
|
const auto offset = tryGetValue(node->TryGetBox(6), node->Values.Count() >= 3 ? node->Values[2] : Float2::Zero).AsFloat2();
|
||||||
const bool local = node->Values.Count() >= 5 ? node->Values[4].AsBool : false;
|
const bool local = node->Values.Count() >= 5 ? node->Values[4].AsBool : false;
|
||||||
|
|
||||||
const Char* samplerName;
|
const Char* samplerName;
|
||||||
const int32 samplerIndex = node->Values[3].AsInt;
|
const int32 samplerIndex = node->Values.Count() >= 4 ? node->Values[3].AsInt : LinearWrap;
|
||||||
if (samplerIndex == TextureGroup)
|
if (samplerIndex == TextureGroup)
|
||||||
{
|
{
|
||||||
auto& textureGroupSampler = findOrAddTextureGroupSampler(node->Values[3].AsInt);
|
auto& textureGroupSampler = findOrAddTextureGroupSampler(node->Values[3].AsInt);
|
||||||
@@ -795,7 +795,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
|||||||
const auto texture = eatBox(textureBox->GetParent<Node>(), textureBox->FirstConnection());
|
const auto texture = eatBox(textureBox->GetParent<Node>(), textureBox->FirstConnection());
|
||||||
const auto scale = tryGetValue(node->GetBox(1), node->Values[0]).AsFloat();
|
const auto scale = tryGetValue(node->GetBox(1), node->Values[0]).AsFloat();
|
||||||
const auto blend = tryGetValue(node->GetBox(2), node->Values[1]).AsFloat();
|
const auto blend = tryGetValue(node->GetBox(2), node->Values[1]).AsFloat();
|
||||||
const auto offset = tryGetValue(node->GetBox(3), node->Values[2]).AsFloat2();
|
const auto offset = tryGetValue(node->GetBox(6), node->Values[2]).AsFloat2();
|
||||||
const bool local = node->Values.Count() >= 5 ? node->Values[4].AsBool : false;
|
const bool local = node->Values.Count() >= 5 ? node->Values[4].AsBool : false;
|
||||||
|
|
||||||
const Char* samplerName;
|
const Char* samplerName;
|
||||||
|
|||||||
@@ -597,24 +597,10 @@ bool ImportMesh(int32 index, ModelData& result, AssimpImporterData& data, String
|
|||||||
// Link mesh
|
// Link mesh
|
||||||
meshData->NodeIndex = nodeIndex;
|
meshData->NodeIndex = nodeIndex;
|
||||||
AssimpNode* curNode = &data.Nodes[meshData->NodeIndex];
|
AssimpNode* curNode = &data.Nodes[meshData->NodeIndex];
|
||||||
Vector3 translation = Vector3::Zero;
|
|
||||||
Vector3 scale = Vector3::One;
|
meshData->OriginTranslation = curNode->LocalTransform.Translation;
|
||||||
Quaternion rotation = Quaternion::Identity;
|
meshData->OriginOrientation = curNode->LocalTransform.Orientation;
|
||||||
|
meshData->Scaling = curNode->LocalTransform.Scale;
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
translation += curNode->LocalTransform.Translation;
|
|
||||||
scale *= curNode->LocalTransform.Scale;
|
|
||||||
rotation *= curNode->LocalTransform.Orientation;
|
|
||||||
|
|
||||||
if (curNode->ParentIndex == -1)
|
|
||||||
break;
|
|
||||||
curNode = &data.Nodes[curNode->ParentIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
meshData->OriginTranslation = translation;
|
|
||||||
meshData->OriginOrientation = rotation;
|
|
||||||
meshData->Scaling = scale;
|
|
||||||
|
|
||||||
if (result.LODs.Count() <= lodIndex)
|
if (result.LODs.Count() <= lodIndex)
|
||||||
result.LODs.Resize(lodIndex + 1);
|
result.LODs.Resize(lodIndex + 1);
|
||||||
|
|||||||
@@ -992,7 +992,8 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh*
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
// Get local transform for origin shifting translation
|
// Get local transform for origin shifting translation
|
||||||
auto translation = ToMatrix(aMesh->getGlobalTransform()).GetTranslation();
|
|
||||||
|
auto translation = Float3((float)aMesh->getLocalTranslation().x, (float)aMesh->getLocalTranslation().y, (float)aMesh->getLocalTranslation().z);
|
||||||
auto scale = data.GlobalSettings.UnitScaleFactor;
|
auto scale = data.GlobalSettings.UnitScaleFactor;
|
||||||
if (data.GlobalSettings.CoordAxis == ofbx::CoordSystem_RightHanded)
|
if (data.GlobalSettings.CoordAxis == ofbx::CoordSystem_RightHanded)
|
||||||
mesh.OriginTranslation = scale * Vector3(translation.X, translation.Y, -translation.Z);
|
mesh.OriginTranslation = scale * Vector3(translation.X, translation.Y, -translation.Z);
|
||||||
|
|||||||
@@ -1549,23 +1549,25 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
|
|||||||
|
|
||||||
// Prepare import transformation
|
// Prepare import transformation
|
||||||
Transform importTransform(options.Translation, options.Rotation, Float3(options.Scale));
|
Transform importTransform(options.Translation, options.Rotation, Float3(options.Scale));
|
||||||
if (options.UseLocalOrigin && data.LODs.HasItems() && data.LODs[0].Meshes.HasItems())
|
|
||||||
{
|
|
||||||
importTransform.Translation -= importTransform.Orientation * data.LODs[0].Meshes[0]->OriginTranslation * importTransform.Scale;
|
|
||||||
}
|
|
||||||
if (options.CenterGeometry && data.LODs.HasItems() && data.LODs[0].Meshes.HasItems())
|
|
||||||
{
|
|
||||||
// Calculate the bounding box (use LOD0 as a reference)
|
|
||||||
BoundingBox box = data.LODs[0].GetBox();
|
|
||||||
auto center = data.LODs[0].Meshes[0]->OriginOrientation * importTransform.Orientation * box.GetCenter() * importTransform.Scale * data.LODs[0].Meshes[0]->Scaling;
|
|
||||||
importTransform.Translation -= center;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the import transformation
|
// Apply the import transformation
|
||||||
if (!importTransform.IsIdentity() && data.Nodes.HasItems())
|
if ((!importTransform.IsIdentity() || options.UseLocalOrigin || options.CenterGeometry) && data.Nodes.HasItems())
|
||||||
{
|
{
|
||||||
if (options.Type == ModelType::SkinnedModel)
|
if (options.Type == ModelType::SkinnedModel)
|
||||||
{
|
{
|
||||||
|
// Setup other transform options
|
||||||
|
if (options.UseLocalOrigin && data.LODs.HasItems() && data.LODs[0].Meshes.HasItems())
|
||||||
|
{
|
||||||
|
importTransform.Translation -= importTransform.Orientation * data.LODs[0].Meshes[0]->OriginTranslation * importTransform.Scale;
|
||||||
|
}
|
||||||
|
if (options.CenterGeometry && data.LODs.HasItems() && data.LODs[0].Meshes.HasItems())
|
||||||
|
{
|
||||||
|
// Calculate the bounding box (use LOD0 as a reference)
|
||||||
|
BoundingBox box = data.LODs[0].GetBox();
|
||||||
|
auto center = data.LODs[0].Meshes[0]->OriginOrientation * importTransform.Orientation * box.GetCenter() * importTransform.Scale * data.LODs[0].Meshes[0]->Scaling;
|
||||||
|
importTransform.Translation -= center;
|
||||||
|
}
|
||||||
|
|
||||||
// Transform the root node using the import transformation
|
// Transform the root node using the import transformation
|
||||||
auto& root = data.Skeleton.RootNode();
|
auto& root = data.Skeleton.RootNode();
|
||||||
Transform meshTransform = root.LocalTransform.WorldToLocal(importTransform).LocalToWorld(root.LocalTransform);
|
Transform meshTransform = root.LocalTransform.WorldToLocal(importTransform).LocalToWorld(root.LocalTransform);
|
||||||
@@ -1596,9 +1598,31 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Transform the root node using the import transformation
|
// Transform the nodes using the import transformation
|
||||||
auto& root = data.Nodes[0];
|
if (data.LODs.HasItems() && data.LODs[0].Meshes.HasItems())
|
||||||
root.LocalTransform = importTransform.LocalToWorld(root.LocalTransform);
|
{
|
||||||
|
for (int i = 0; i < data.LODs[0].Meshes.Count(); ++i)
|
||||||
|
{
|
||||||
|
auto* meshData = data.LODs[0].Meshes[i];
|
||||||
|
Transform transform = importTransform;
|
||||||
|
if (options.UseLocalOrigin)
|
||||||
|
{
|
||||||
|
transform.Translation -= transform.Orientation * meshData->OriginTranslation * transform.Scale;
|
||||||
|
}
|
||||||
|
if (options.CenterGeometry)
|
||||||
|
{
|
||||||
|
// Calculate the bounding box (use LOD0 as a reference)
|
||||||
|
BoundingBox box = data.LODs[0].GetBox();
|
||||||
|
auto center = meshData->OriginOrientation * transform.Orientation * box.GetCenter() * transform.Scale * meshData->Scaling;
|
||||||
|
transform.Translation -= center;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 nodeIndex = meshData->NodeIndex;
|
||||||
|
|
||||||
|
auto& node = data.Nodes[nodeIndex];
|
||||||
|
node.LocalTransform = transform.LocalToWorld(node.LocalTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user