Merge branch 'plemsoft-PE-1.4'

This commit is contained in:
Wojtek Figat
2022-10-29 12:04:55 +02:00
13 changed files with 177 additions and 35 deletions

View File

@@ -54,7 +54,10 @@ namespace FlaxEditor.Gizmo
if (!_isActive || !IsActive)
return;
Matrix m1, m2, m3;
// As all axisMesh have the same pivot, add a little offset to the x axisMesh, this way SortDrawCalls is able to sort the draw order
// https://github.com/FlaxEngine/FlaxEngine/issues/680
Matrix m1, m2, m3 , mx1;
bool isXAxis = _activeAxis == Axis.X || _activeAxis == Axis.XY || _activeAxis == Axis.ZX;
bool isYAxis = _activeAxis == Axis.Y || _activeAxis == Axis.XY || _activeAxis == Axis.YZ;
bool isZAxis = _activeAxis == Axis.Z || _activeAxis == Axis.YZ || _activeAxis == Axis.ZX;
@@ -70,6 +73,8 @@ namespace FlaxEditor.Gizmo
break;
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 += 0.05f;
var axisMesh = _modelTranslateAxis.LODs[0].Meshes[0];
var boxMesh = _modelBox.LODs[0].Meshes[0];
var boxSize = 10.0f;
@@ -90,7 +95,7 @@ namespace FlaxEditor.Gizmo
boxMesh.Draw(ref renderContext, _activeAxis == Axis.YZ ? _materialWireFocus : _materialWire, ref m3);
// X axis
axisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m1);
axisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref mx1);
// Y axis
Matrix.RotationZ(Mathf.PiOverTwo, out m2);
@@ -143,12 +148,15 @@ namespace FlaxEditor.Gizmo
break;
Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3);
Matrix.Multiply(ref m3, ref world, out m1);
mx1 = m1;
mx1.M41 -= 0.05f;
var axisMesh = _modelScaleAxis.LODs[0].Meshes[0];
var boxMesh = _modelBox.LODs[0].Meshes[0];
// X axis
Matrix.RotationY(-Mathf.PiOverTwo, out m2);
Matrix.Multiply(ref m2, ref m1, out m3);
Matrix.Multiply(ref m2, ref mx1, out m3);
axisMesh.Draw(ref renderContext, isXAxis ? _materialAxisFocus : _materialAxisX, ref m3);
// Y axis

View File

@@ -86,7 +86,7 @@ namespace FlaxEditor.Gizmo
/// <summary>
/// Starts the objects transforming (optionally with duplicate).
/// </summary>
protected void StartTransforming()
public void StartTransforming()
{
// Check if can start new action
var count = SelectionCount;
@@ -119,7 +119,7 @@ namespace FlaxEditor.Gizmo
/// <summary>
/// Ends the objects transforming.
/// </summary>
protected void EndTransforming()
public void EndTransforming()
{
// Check if wasn't working at all
if (!_isTransforming)
@@ -287,12 +287,29 @@ namespace FlaxEditor.Gizmo
delta *= 0.5f;
if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping)
{
float snapValue = isScaling ? ScaleSnapValue : TranslationSnapValue;
var snapValue = new Vector3(isScaling ? ScaleSnapValue : TranslationSnapValue);
_translationScaleSnapDelta += delta;
if (!isScaling && snapValue.X < 0.0f)
{
// Snap to object bounding box
GetSelectedObjectsBounds(out var b, out _);
if (b.Minimum.X < 0.0f)
snapValue.X = (Real)Math.Abs(b.Minimum.X) + b.Maximum.X;
else
snapValue.X = (Real)b.Minimum.X - b.Maximum.X;
if (b.Minimum.Y < 0.0f)
snapValue.Y = (Real)Math.Abs(b.Minimum.Y) + b.Maximum.Y;
else
snapValue.Y = (Real)b.Minimum.Y - b.Maximum.Y;
if (b.Minimum.Z < 0.0f)
snapValue.Z = (Real)Math.Abs(b.Minimum.Z) + b.Maximum.Z;
else
snapValue.Z = (Real)b.Minimum.Z - b.Maximum.Z;
}
delta = new Vector3(
(int)(_translationScaleSnapDelta.X / snapValue) * snapValue,
(int)(_translationScaleSnapDelta.Y / snapValue) * snapValue,
(int)(_translationScaleSnapDelta.Z / snapValue) * snapValue);
(int)(_translationScaleSnapDelta.X / snapValue.X) * snapValue.X,
(int)(_translationScaleSnapDelta.Y / snapValue.Y) * snapValue.Y,
(int)(_translationScaleSnapDelta.Z / snapValue.Z) * snapValue.Z);
_translationScaleSnapDelta -= delta;
}
@@ -446,10 +463,9 @@ namespace FlaxEditor.Gizmo
}
// Apply transformation (but to the parents, not whole selection pool)
if (anyValid)
if (anyValid || (!_isTransforming && Owner.UseDuplicate))
{
StartTransforming();
LastDelta = new Transform(translationDelta, rotationDelta, scaleDelta);
OnApplyTransformation(ref translationDelta, ref rotationDelta, ref scaleDelta);
}

View File

@@ -68,6 +68,10 @@ namespace FlaxEditor.Options
[EditorDisplay("Common"), EditorOrder(220)]
public InputBinding ContentFinder = new InputBinding(KeyboardKeys.O, KeyboardKeys.Control);
[DefaultValue(typeof(InputBinding), "R")]
[EditorDisplay("Common"), EditorOrder(230)]
public InputBinding RotateSelection = new InputBinding(KeyboardKeys.R);
#endregion
#region Scene

View File

@@ -319,6 +319,8 @@ namespace FlaxEditor.Viewport
{
TooltipText = "Position snapping values"
};
if (TransformGizmo.TranslationSnapValue < 0.0f)
_translateSnapping.Text = "Bounding Box";
for (int i = 0; i < EditorViewportTranslateSnapValues.Length; i++)
{
@@ -326,6 +328,9 @@ namespace FlaxEditor.Viewport
var button = translateSnappingCM.AddButton(v.ToString());
button.Tag = v;
}
var buttonBB = translateSnappingCM.AddButton("Bounding Box").LinkTooltip("Snaps the selection based on it's bounding volume");
buttonBB.Tag = -1.0f;
translateSnappingCM.ButtonClicked += OnWidgetTranslateSnapClick;
translateSnappingCM.VisibleChanged += OnWidgetTranslateSnapShowHide;
_translateSnapping.Parent = translateSnappingWidget;
@@ -378,6 +383,7 @@ namespace FlaxEditor.Viewport
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
InputActions.Add(options => options.FocusSelection, FocusSelection);
InputActions.Add(options => options.RotateSelection, RotateSelection);
InputActions.Add(options => options.Delete, _editor.SceneEditing.Delete);
}
@@ -539,28 +545,24 @@ namespace FlaxEditor.Viewport
private void OnTranslateSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.TranslationSnapEnable = !TransformGizmo.TranslationSnapEnable;
// cache value
_editor.ProjectCache.SetCustomData("TranslateSnapState", TransformGizmo.TranslationSnapEnable.ToString());
}
private void OnRotateSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.RotationSnapEnabled = !TransformGizmo.RotationSnapEnabled;
// cache value
_editor.ProjectCache.SetCustomData("RotationSnapState", TransformGizmo.RotationSnapEnabled.ToString());
}
private void OnScaleSnappingToggle(ViewportWidgetButton button)
{
TransformGizmo.ScaleSnapEnabled = !TransformGizmo.ScaleSnapEnabled;
// cache value
_editor.ProjectCache.SetCustomData("ScaleSnapState", TransformGizmo.ScaleSnapEnabled.ToString());
}
private void OnTransformSpaceToggle(ViewportWidgetButton button)
{
TransformGizmo.ToggleTransformSpace();
// cache value
_editor.ProjectCache.SetCustomData("TransformSpaceState", TransformGizmo.ActiveTransformSpace.ToString());
}
@@ -591,7 +593,6 @@ namespace FlaxEditor.Viewport
var v = (float)button.Tag;
TransformGizmo.ScaleSnapValue = v;
_scaleSnapping.Text = v.ToString();
// cache value
_editor.ProjectCache.SetCustomData("ScaleSnapValue", TransformGizmo.ScaleSnapValue.ToString("N"));
}
@@ -630,7 +631,6 @@ namespace FlaxEditor.Viewport
var v = (float)button.Tag;
TransformGizmo.RotationSnapValue = v;
_rotateSnapping.Text = v.ToString();
// cache value
_editor.ProjectCache.SetCustomData("RotationSnapValue", TransformGizmo.RotationSnapValue.ToString("N"));
}
@@ -667,8 +667,10 @@ namespace FlaxEditor.Viewport
{
var v = (float)button.Tag;
TransformGizmo.TranslationSnapValue = v;
_translateSnapping.Text = v.ToString();
// cache value
if (v < 0.0f)
_translateSnapping.Text = "Bounding Box";
else
_translateSnapping.Text = v.ToString();
_editor.ProjectCache.SetCustomData("TranslateSnapValue", TransformGizmo.TranslationSnapValue.ToString("N"));
}
@@ -696,6 +698,51 @@ namespace FlaxEditor.Viewport
Gizmos.ForEach(x => x.OnSelectionChanged(selection));
}
/// <summary>
/// Press "R" to rotate the selected gizmo objects 45 degrees.
/// </summary>
public void RotateSelection()
{
var win = (WindowRootControl)Root;
var selection = _editor.SceneEditing.Selection;
var isShiftDown = win.GetKey(KeyboardKeys.Shift);
Quaternion rotationDelta;
if (isShiftDown)
rotationDelta = Quaternion.Euler(0.0f, -45.0f, 0.0f);
else
rotationDelta = Quaternion.Euler(0.0f, 45.0f, 0.0f);
bool useObjCenter = TransformGizmo.ActivePivot == TransformGizmoBase.PivotType.ObjectCenter;
Vector3 gizmoPosition = TransformGizmo.Position;
// Rotate selected objects
bool isPlayMode = Editor.Instance.StateMachine.IsPlayMode;
TransformGizmo.StartTransforming();
for (int i = 0; i < selection.Count; i++)
{
var obj = selection[i];
if (isPlayMode && obj.CanTransform == false)
continue;
var trans = obj.Transform;
var pivotOffset = trans.Translation - gizmoPosition;
if (useObjCenter || pivotOffset.IsZero)
{
trans.Orientation *= Quaternion.Invert(trans.Orientation) * rotationDelta * trans.Orientation;
}
else
{
Matrix.RotationQuaternion(ref trans.Orientation, out var transWorld);
Matrix.RotationQuaternion(ref rotationDelta, out var deltaWorld);
Matrix world = transWorld * Matrix.Translation(pivotOffset) * deltaWorld * Matrix.Translation(-pivotOffset);
trans.SetRotation(ref world);
trans.Translation += world.TranslationVector;
}
obj.Transform = trans;
}
TransformGizmo.EndTransforming();
}
/// <summary>
/// Focuses the viewport on the current selection of the gizmo.
/// </summary>

View File

@@ -130,6 +130,10 @@ void AudioSource::Play()
{
// Request faster streaming update
Clip->RequestStreamingUpdate();
// If we are looping and streaming also update streaming buffers
if(_loop)
RequestStreamingBuffersUpdate();
}
}
else

View File

@@ -281,7 +281,6 @@ bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
auto& e = i->Value;
if (e.Info.Path == path)
{
// Validate file exists
if (!IsEntryValid(e))
{
LOG(Warning, "Missing file from registry: \'{0}\':{1}:{2}", e.Info.Path, e.Info.ID, e.Info.TypeName);
@@ -304,15 +303,11 @@ bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
{
PROFILE_CPU();
bool result = false;
ScopeLock lock(_locker);
auto e = _registry.TryGet(id);
if (e != nullptr)
{
// Validate entry
if (!IsEntryValid(*e))
{
LOG(Warning, "Missing file from registry: \'{0}\':{1}:{2}", e->Info.Path, e->Info.ID, e->Info.TypeName);
@@ -325,7 +320,6 @@ bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
info = e->Info;
}
}
return result;
}
@@ -379,8 +373,26 @@ void AssetsCache::RegisterAssets(FlaxStorage* storage)
// Check if storage contains ID which has been already registered
if (FindAsset(e.ID, info))
{
#if PLATFORM_WINDOWS
// On Windows - if you start your project using a shortcut/VS commandline -project, and using a upper/lower drive letter, it could the cache (case doesn't matter on OS)
if (StringUtils::CompareIgnoreCase(storagePath.GetText(), info.Path.GetText()) != 0)
{
LOG(Warning, "Founded duplicated asset \'{0}\'. Locations: \'{1}\' and \'{2}\'", e.ID, storagePath, info.Path);
duplicatedEntries.Add(i);
}
else
{
// Remove from registry so we can add it again later with the original ID, so we don't loose relations
for (auto j = _registry.Begin(); j.IsNotEnd(); ++j)
{
if (StringUtils::CompareIgnoreCase(j->Value.Info.Path.GetText(), storagePath.GetText()) == 0)
_registry.Remove(j);
}
}
#else
LOG(Warning, "Founded duplicated asset \'{0}\'. Locations: \'{1}\' and \'{2}\'", e.ID, storagePath, info.Path);
duplicatedEntries.Add(i);
#endif
}
}
@@ -551,7 +563,6 @@ bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPa
bool AssetsCache::IsEntryValid(Entry& e)
{
#if ENABLE_ASSETS_DISCOVERY
// Check if file exists
if (FileSystem::FileExists(e.Info.Path))
{
@@ -601,10 +612,8 @@ bool AssetsCache::IsEntryValid(Entry& e)
return false;
#else
// In game we don't care about it because all cached asset entries are valid (precached)
// Skip only entries with missing file
return e.Info.Path.HasChars();
#endif
}

View File

@@ -215,7 +215,7 @@ bool String::IsANSI() const
bool result = true;
for (int32 i = 0; i < _length; i++)
{
if (_data[i] > 255)
if (_data[i] > 127)
{
result = false;
break;

View File

@@ -868,6 +868,20 @@ ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type)
return result;
}
ScriptingObject* Scripting::TryFindObject(MClass* mclass)
{
if (mclass == nullptr)
return nullptr;
ScopeLock lock(_objectsLocker);
for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i)
{
const auto obj = i->Value;
if (obj->GetClass() == mclass)
return obj;
}
return nullptr;
}
ScriptingObject* Scripting::FindObject(const MObject* managedInstance)
{
if (managedInstance == nullptr)

View File

@@ -85,7 +85,7 @@ public:
/// <param name="fullname">The full name of the type eg: System.Int64.</param>
/// <returns>The MClass object or null if missing.</returns>
static MClass* FindClass(const StringAnsiView& fullname);
#if USE_MONO
/// <summary>
/// Finds the class from the given Mono class object within whole assembly.
@@ -144,6 +144,12 @@ public:
/// <returns>The found object or null if missing.</returns>
static ScriptingObject* FindObject(Guid id, MClass* type = nullptr);
/// <summary>
/// Tries to find the object by the given class.
/// </summary>
/// <returns>The found object or null if missing.</returns>
static ScriptingObject* TryFindObject(MClass* type);
/// <summary>
/// Tries to find the object by the given identifier.
/// </summary>

View File

@@ -0,0 +1,24 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "Engine/Tools/ModelTool/ModelTool.h"
#include <ThirdParty/catch2/catch.hpp>
TEST_CASE("ModelTool")
{
SECTION("Test DetectLodIndex")
{
CHECK(ModelTool::DetectLodIndex(TEXT("mesh")) == 0);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh LOD")) == 0);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh LOD0")) == 0);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh LOD1")) == 1);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_LOD1")) == 1);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_lod1")) == 1);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_lod2")) == 2);
CHECK(ModelTool::DetectLodIndex(TEXT("lod0")) == 0);
CHECK(ModelTool::DetectLodIndex(TEXT("lod1")) == 1);
CHECK(ModelTool::DetectLodIndex(TEXT("lod_2")) == 2);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_lod_0")) == 0);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh_lod_1")) == 1);
CHECK(ModelTool::DetectLodIndex(TEXT("mesh lod_2")) == 2);
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using Flax.Build;
using Flax.Build.NativeCpp;
/// <summary>
/// Engine tests module.
@@ -14,6 +15,14 @@ public class Tests : EngineModule
Deploy = false;
}
/// <inheritdoc />
public override void Setup(BuildOptions options)
{
base.Setup(options);
options.PrivateDependencies.Add("ModelTool");
}
/// <inheritdoc />
public override void GetFilesToDeploy(List<string> files)
{

View File

@@ -641,6 +641,7 @@ bool ModelTool::ImportDataAssimp(const char* path, ImportedModelData& data, Opti
aiProcess_JoinIdenticalVertices |
aiProcess_LimitBoneWeights |
aiProcess_Triangulate |
aiProcess_SortByPType |
aiProcess_GenUVCoords |
aiProcess_FindDegenerates |
aiProcess_FindInvalidData |

View File

@@ -1552,21 +1552,21 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
int32 ModelTool::DetectLodIndex(const String& nodeName)
{
const int32 index = nodeName.FindLast(TEXT("LOD"));
int32 index = nodeName.FindLast(TEXT("LOD"), StringSearchCase::IgnoreCase);
if (index != -1)
{
// Some models use LOD_0 to identify LOD levels
if (nodeName.Length() > index + 4 && nodeName[index + 3] == '_')
index++;
int32 num;
if (!StringUtils::Parse(nodeName.Get() + index + 3, &num))
{
if (num >= 0 && num < MODEL_MAX_LODS)
{
return num;
}
LOG(Warning, "Invalid mesh level of detail index at node \'{0}\'. Maximum supported amount of LODs is {1}.", nodeName, MODEL_MAX_LODS);
}
}
return 0;
}