Merge branch 'FlaxEngine:master' into ContentBrowserImprovement

This commit is contained in:
NoriteSC
2023-07-28 15:56:40 +02:00
committed by GitHub
71 changed files with 913 additions and 327 deletions

View File

@@ -45,8 +45,14 @@ namespace FlaxEditor.Content.GUI
private void ImportActors(DragActors actors, ContentFolder location)
{
// Use only the first actor
Editor.Instance.Prefabs.CreatePrefab(actors.Objects[0].Actor);
foreach (var actorNode in actors.Objects)
{
var actor = actorNode.Actor;
if (actors.Objects.Contains(actorNode.ParentNode as ActorNode))
continue;
Editor.Instance.Prefabs.CreatePrefab(actor, false);
}
}
/// <inheritdoc />

View File

@@ -29,7 +29,7 @@ namespace FlaxEditor.Content
if (asset)
{
var source = Editor.GetShaderSourceCode(asset);
Utilities.Utils.ShowSourceCodeWindow(source, "Shader Source", item.RootWindow.Window);
Utilities.Utils.ShowSourceCodeWindow(source, "Shader Source", item.RootWindow?.Window);
}
return null;
}

View File

@@ -33,7 +33,12 @@ namespace FlaxEditor.CustomEditors.Editors
[CustomEditor(typeof(Asset)), DefaultEditor]
public class AssetRefEditor : CustomEditor
{
private AssetPicker _picker;
/// <summary>
/// The asset picker used to get a reference to an asset.
/// </summary>
public AssetPicker Picker;
private bool _isRefreshing;
private ScriptType _valueType;
/// <inheritdoc />
@@ -44,7 +49,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
if (HasDifferentTypes)
return;
_picker = layout.Custom<AssetPicker>().CustomControl;
Picker = layout.Custom<AssetPicker>().CustomControl;
_valueType = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
var assetType = _valueType;
@@ -66,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
// Generic file picker
assetType = ScriptType.Null;
_picker.FileExtension = assetReference.TypeName;
Picker.FileExtension = assetReference.TypeName;
}
else
{
@@ -75,26 +80,30 @@ namespace FlaxEditor.CustomEditors.Editors
assetType = customType;
else if (!Content.Settings.GameSettings.OptionalPlatformSettings.Contains(assetReference.TypeName))
Debug.LogWarning(string.Format("Unknown asset type '{0}' to use for asset picker filter.", assetReference.TypeName));
else
assetType = ScriptType.Void;
}
}
_picker.AssetType = assetType;
_picker.Height = height;
_picker.SelectedItemChanged += OnSelectedItemChanged;
Picker.AssetType = assetType;
Picker.Height = height;
Picker.SelectedItemChanged += OnSelectedItemChanged;
}
private void OnSelectedItemChanged()
{
if (_isRefreshing)
return;
if (typeof(AssetItem).IsAssignableFrom(_valueType.Type))
SetValue(_picker.SelectedItem);
SetValue(Picker.SelectedItem);
else if (_valueType.Type == typeof(Guid))
SetValue(_picker.SelectedID);
SetValue(Picker.SelectedID);
else if (_valueType.Type == typeof(SceneReference))
SetValue(new SceneReference(_picker.SelectedID));
SetValue(new SceneReference(Picker.SelectedID));
else if (_valueType.Type == typeof(string))
SetValue(_picker.SelectedPath);
SetValue(Picker.SelectedPath);
else
SetValue(_picker.SelectedAsset);
SetValue(Picker.SelectedAsset);
}
/// <inheritdoc />
@@ -104,16 +113,18 @@ namespace FlaxEditor.CustomEditors.Editors
if (!HasDifferentValues)
{
_isRefreshing = true;
if (Values[0] is AssetItem assetItem)
_picker.SelectedItem = assetItem;
Picker.SelectedItem = assetItem;
else if (Values[0] is Guid guid)
_picker.SelectedID = guid;
Picker.SelectedID = guid;
else if (Values[0] is SceneReference sceneAsset)
_picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
Picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
else if (Values[0] is string path)
_picker.SelectedPath = path;
Picker.SelectedPath = path;
else
_picker.SelectedAsset = Values[0] as Asset;
Picker.SelectedAsset = Values[0] as Asset;
_isRefreshing = false;
}
}
}

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.CustomEditors.GUI;
using FlaxEditor.Scripting;
using FlaxEngine;
namespace FlaxEditor.CustomEditors.Editors
@@ -13,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Editors
{
private GroupElement _group;
private bool _updateName;
private int _entryIndex;
private bool _isRefreshing;
private MaterialBase _material;
private ModelInstanceActor _modelInstance;
private AssetRefEditor _materialEditor;
/// <inheritdoc />
public override void Initialize(LayoutElementsContainer layout)
@@ -21,47 +28,122 @@ namespace FlaxEditor.CustomEditors.Editors
var group = layout.Group("Entry");
_group = group;
if (ParentEditor == null)
return;
var entry = (ModelInstanceEntry)Values[0];
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
var materialLabel = new PropertyNameLabel("Material");
materialLabel.TooltipText = "The mesh surface material used for the rendering.";
if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance)
{
_entryIndex = entryIndex;
_modelInstance = modelInstance;
var slots = modelInstance.MaterialSlots;
if (entry.Material == slots[entryIndex].Material)
{
// Ensure that entry with default material set is set back to null
modelInstance.SetMaterial(entryIndex, null);
}
_material = modelInstance.GetMaterial(entryIndex);
var defaultValue = GPUDevice.Instance.DefaultMaterial;
if (slots[entryIndex].Material)
{
// Use default value set on asset (eg. Model Asset)
defaultValue = slots[entryIndex].Material;
}
// Create material picker
var materialValue = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase);
var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue);
materialEditor.Values.SetDefaultValue(defaultValue);
materialEditor.RefreshDefaultValue();
materialEditor.Picker.SelectedItemChanged += OnSelectedMaterialChanged;
_materialEditor = materialEditor;
}
base.Initialize(group);
}
private void OnSelectedMaterialChanged()
{
if (_isRefreshing)
return;
_isRefreshing = true;
var slots = _modelInstance.MaterialSlots;
var material = _materialEditor.Picker.SelectedAsset as MaterialBase;
var defaultMaterial = GPUDevice.Instance.DefaultMaterial;
var value = (ModelInstanceEntry)Values[0];
var prevMaterial = value.Material;
if (!material)
{
// Fallback to default material
_materialEditor.Picker.SelectedAsset = defaultMaterial;
value.Material = defaultMaterial;
}
else if (material == slots[_entryIndex].Material)
{
// Asset default material
value.Material = null;
}
else if (material == defaultMaterial && !slots[_entryIndex].Material)
{
// Default material while asset has no set as well
value.Material = null;
}
else
{
// Custom material
value.Material = material;
}
if (prevMaterial != value.Material)
SetValue(value);
_isRefreshing = false;
}
/// <inheritdoc />
protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
{
// Skip material member as it is overridden
if (item.Info.Name == "Material" && _materialEditor != null)
return;
base.SpawnProperty(itemLayout, itemValues, item);
}
/// <inheritdoc />
public override void Refresh()
{
// Update panel title to match material slot name
if (_updateName &&
_group != null &&
ParentEditor?.ParentEditor != null &&
ParentEditor.ParentEditor.Values.Count > 0)
{
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel)
if (ParentEditor.ParentEditor.Values[0] is ModelInstanceActor modelInstance)
{
var model = staticModel.Model;
if (model && model.IsLoaded)
var slots = modelInstance.MaterialSlots;
if (slots != null && slots.Length > entryIndex)
{
var slots = model.MaterialSlots;
if (slots != null && slots.Length > entryIndex)
{
_group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
_updateName = false;
}
}
}
else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel)
{
var model = animatedModel.SkinnedModel;
if (model && model.IsLoaded)
{
var slots = model.MaterialSlots;
if (slots != null && slots.Length > entryIndex)
{
_group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
_updateName = false;
}
_updateName = false;
_group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
}
}
}
// Refresh currently selected material
_material = _modelInstance.GetMaterial(_entryIndex);
base.Refresh();
}
/// <inheritdoc />
protected override void Deinitialize()
{
_material = null;
_modelInstance = null;
_materialEditor = null;
base.Deinitialize();
}
}
}

View File

@@ -102,5 +102,7 @@ public class Editor : EditorModule
files.Add(Path.Combine(FolderPath, "Cooker/GameCooker.h"));
files.Add(Path.Combine(FolderPath, "Cooker/PlatformTools.h"));
files.Add(Path.Combine(FolderPath, "Cooker/Steps/CookAssetsStep.h"));
files.Add(Path.Combine(FolderPath, "Utilities/ScreenUtilities.h"));
files.Add(Path.Combine(FolderPath, "Utilities/ViewportIconsRenderer.h"));
}
}

View File

@@ -475,22 +475,31 @@ namespace FlaxEditor.GUI
if (_type != ScriptType.Null)
{
// Show asset picker popup
AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item =>
var popup = AssetSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item =>
{
SelectedItem = item;
RootWindow.Focus();
Focus();
});
if (_selected != null)
{
var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path);
popup.ScrollToAndHighlightItemByName(selectedAssetName);
}
}
else
{
// Show content item picker popup
ContentSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item =>
var popup = ContentSearchPopup.Show(this, Button1Rect.BottomLeft, IsValid, item =>
{
SelectedItem = item;
RootWindow.Focus();
Focus();
});
if (_selectedItem != null)
{
popup.ScrollToAndHighlightItemByName(_selectedItem.ShortName);
}
}
}
else if (_selected != null || _selectedItem != null)

View File

@@ -265,6 +265,35 @@ namespace FlaxEditor.GUI
_searchBox.Focus();
}
/// <summary>
/// Scrolls the scroll panel to a specific Item
/// </summary>
/// <param name="item">The item to scroll to.</param>
public void ScrollViewTo(Item item)
{
_scrollPanel.ScrollViewTo(item, true);
}
/// <summary>
/// Scrolls to the item and focuses it by name.
/// </summary>
/// <param name="itemName">The item name.</param>
public void ScrollToAndHighlightItemByName(string itemName)
{
foreach (var child in ItemsPanel.Children)
{
if (child is not ItemsListContextMenu.Item item)
continue;
if (string.Equals(item.Name, itemName, StringComparison.Ordinal))
{
// Highlight and scroll to item
item.Focus();
ScrollViewTo(item);
break;
}
}
}
/// <summary>
/// Sorts the items list (by item name by default).
/// </summary>

View File

@@ -178,6 +178,8 @@ namespace FlaxEditor.Gizmo
if (selection[i] is ActorNode actorNode && actorNode.Actor != null)
CollectActors(actorNode.Actor);
}
if (_actors.Count == 0)
return;
// Render selected objects depth
Renderer.DrawSceneDepth(context, task, customDepth, _actors);

View File

@@ -37,6 +37,11 @@ namespace FlaxEditor.Modules
/// </summary>
public event Action<Prefab, Actor> PrefabApplied;
/// <summary>
/// Locally cached actor for prefab creation.
/// </summary>
private Actor _prefabCreationActor;
internal PrefabsModule(Editor editor)
: base(editor)
{
@@ -78,6 +83,16 @@ namespace FlaxEditor.Modules
/// </remarks>
/// <param name="actor">The root prefab actor.</param>
public void CreatePrefab(Actor actor)
{
CreatePrefab(actor, true);
}
/// <summary>
/// Starts the creating prefab for the given actor by showing the new item creation dialog in <see cref="ContentWindow"/>.
/// </summary>
/// <param name="actor">The root prefab actor.</param>
/// <param name="rename">Allow renaming or not</param>
public void CreatePrefab(Actor actor, bool rename)
{
// Skip in invalid states
if (!Editor.StateMachine.CurrentState.CanEditContent)
@@ -90,7 +105,8 @@ namespace FlaxEditor.Modules
PrefabCreating?.Invoke(actor);
var proxy = Editor.ContentDatabase.GetProxy<Prefab>();
Editor.Windows.ContentWin.NewItem(proxy, actor, OnPrefabCreated, actor.Name);
_prefabCreationActor = actor;
Editor.Windows.ContentWin.NewItem(proxy, actor, OnPrefabCreated, actor.Name, rename);
}
private void OnPrefabCreated(ContentItem contentItem)
@@ -107,25 +123,21 @@ namespace FlaxEditor.Modules
// Record undo for prefab creating (backend links the target instance with the prefab)
if (Editor.Undo.Enabled)
{
var selection = Editor.SceneEditing.Selection.Where(x => x is ActorNode).ToList().BuildNodesParents();
if (selection.Count == 0)
if (!_prefabCreationActor)
return;
if (selection.Count == 1)
var actorsList = new List<Actor>();
Utilities.Utils.GetActorsTree(actorsList, _prefabCreationActor);
var actions = new IUndoAction[actorsList.Count];
for (int i = 0; i < actorsList.Count; i++)
{
var action = BreakPrefabLinkAction.Linked(((ActorNode)selection[0]).Actor);
Undo.AddAction(action);
}
else
{
var actions = new IUndoAction[selection.Count];
for (int i = 0; i < selection.Count; i++)
{
var action = BreakPrefabLinkAction.Linked(((ActorNode)selection[i]).Actor);
actions[i] = action;
}
Undo.AddAction(new MultiUndoAction(actions));
var action = BreakPrefabLinkAction.Linked(actorsList[i]);
actions[i] = action;
}
Undo.AddAction(new MultiUndoAction(actions));
_prefabCreationActor = null;
}
Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout();

View File

@@ -292,7 +292,6 @@ void FoliageTools::Paint(Foliage* foliage, Span<int32> foliageTypesIndices, cons
{
PROFILE_CPU_NAMED("Place Instances");
Matrix matrix;
FoliageInstance instance;
Quaternion tmp;
Matrix world;

View File

@@ -185,8 +185,8 @@ namespace FlaxEditor.Viewport.Previews
{
UseTimeScale = false,
UpdateWhenOffscreen = true,
//_previewModel.BoundsScale = 1000.0f;
UpdateMode = AnimatedModel.AnimationUpdateMode.Manual
BoundsScale = 100.0f,
UpdateMode = AnimatedModel.AnimationUpdateMode.Manual,
};
Task.AddCustomActor(_previewModel);

View File

@@ -80,6 +80,7 @@ namespace FlaxEditor.Windows
public LogGroup Group;
public LogEntryDescription Desc;
public SpriteHandle Icon;
public int LogCount = 1;
public LogEntry(DebugLogWindow window, ref LogEntryDescription desc)
: base(0, 0, 120, DefaultHeight)
@@ -137,7 +138,14 @@ namespace FlaxEditor.Windows
// Title
var textRect = new Rectangle(38, 2, clientRect.Width - 40, clientRect.Height - 10);
Render2D.PushClip(ref clientRect);
Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground);
if (LogCount == 1)
{
Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground);
}
else if (LogCount > 1)
{
Render2D.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground);
}
Render2D.PopClip();
}
@@ -289,6 +297,7 @@ namespace FlaxEditor.Windows
private readonly List<LogEntry> _pendingEntries = new List<LogEntry>(32);
private readonly ToolStripButton _clearOnPlayButton;
private readonly ToolStripButton _collapseLogsButton;
private readonly ToolStripButton _pauseOnErrorButton;
private readonly ToolStripButton[] _groupButtons = new ToolStripButton[3];
@@ -316,6 +325,7 @@ namespace FlaxEditor.Windows
};
toolstrip.AddButton("Clear", Clear).LinkTooltip("Clears all log entries");
_clearOnPlayButton = (ToolStripButton)toolstrip.AddButton("Clear on Play").SetAutoCheck(true).SetChecked(true).LinkTooltip("Clears all log entries on enter playmode");
_collapseLogsButton = (ToolStripButton)toolstrip.AddButton("Collapse").SetAutoCheck(true).SetChecked(true).LinkTooltip("Collapses similar logs.");
_pauseOnErrorButton = (ToolStripButton)toolstrip.AddButton("Pause on Error").SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
toolstrip.AddSeparator();
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides error messages");
@@ -612,6 +622,30 @@ namespace FlaxEditor.Windows
var top = _entriesPanel.Children.Count != 0 ? _entriesPanel.Children[_entriesPanel.Children.Count - 1].Bottom + spacing : margin.Top;
for (int i = 0; i < _pendingEntries.Count; i++)
{
if (_collapseLogsButton.Checked)
{
bool logExists = false;
foreach (var child in _entriesPanel.Children)
{
if (child is LogEntry entry)
{
var pendingEntry = _pendingEntries[i];
if (string.Equals(entry.Desc.Title, pendingEntry.Desc.Title, StringComparison.Ordinal) &&
string.Equals(entry.Desc.LocationFile, pendingEntry.Desc.LocationFile, StringComparison.Ordinal) &&
entry.Desc.Level == pendingEntry.Desc.Level &&
string.Equals(entry.Desc.Description, pendingEntry.Desc.Description, StringComparison.Ordinal) &&
entry.Desc.LocationLine == pendingEntry.Desc.LocationLine)
{
entry.LogCount += 1;
newEntry = entry;
logExists = true;
break;
}
}
}
if (logExists)
continue;
}
newEntry = _pendingEntries[i];
newEntry.Visible = _groupButtons[(int)newEntry.Group].Checked;
anyVisible |= newEntry.Visible;

View File

@@ -1,7 +1,6 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
@@ -94,9 +93,11 @@ namespace FlaxEngine
public AnimatedModel Instance;
}
[HideInEditor]
[CustomMarshaller(typeof(Context), MarshalMode.Default, typeof(ContextMarshaller))]
internal static class ContextMarshaller
{
[HideInEditor]
[StructLayout(LayoutKind.Sequential)]
public struct ContextNative
{
@@ -116,7 +117,7 @@ namespace FlaxEngine
internal static Context ToManaged(ContextNative managed)
{
return new Context()
return new Context
{
Graph = managed.Graph,
GraphExecutor = managed.GraphExecutor,
@@ -129,9 +130,10 @@ namespace FlaxEngine
Instance = AnimatedModelMarshaller.ConvertToManaged(managed.Instance),
};
}
internal static ContextNative ToNative(Context managed)
{
return new ContextNative()
return new ContextNative
{
Graph = managed.Graph,
GraphExecutor = managed.GraphExecutor,
@@ -144,6 +146,7 @@ namespace FlaxEngine
Instance = AnimatedModelMarshaller.ConvertToUnmanaged(managed.Instance),
};
}
internal static void Free(ContextNative unmanaged)
{
}
@@ -243,6 +246,12 @@ namespace FlaxEngine
throw new ArgumentNullException(nameof(source));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (source->NodesCount <= 0 || source->NodesCount > 4096)
throw new ArgumentOutOfRangeException(nameof(source));
if (destination->NodesCount <= 0 || destination->NodesCount > 4096)
throw new ArgumentOutOfRangeException(nameof(destination));
if (source->NodesCount != destination->NodesCount)
throw new ArgumentOutOfRangeException();
destination->NodesCount = source->NodesCount;
destination->Unused = source->Unused;
Utils.MemoryCopy(new IntPtr(destination->Nodes), new IntPtr(source->Nodes), (ulong)(source->NodesCount * sizeof(Transform)));

View File

@@ -5,7 +5,6 @@
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Scripting/ManagedCLR/MDomain.h"
#include "Engine/Scripting/ManagedCLR/MMethod.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
@@ -189,12 +188,8 @@ bool AnimGraph::InitCustomNode(Node* node)
return false;
}
// Create node values managed array
// Initialization can happen on Content Thread so ensure to have runtime attached
MCore::Thread::Attach();
MArray* values = MCore::Array::New( MCore::TypeCache::Object, node->Values.Count());
MObject** valuesPtr = MCore::Array::GetAddress<MObject*>(values);
for (int32 i = 0; i < node->Values.Count(); i++)
valuesPtr[i] = MUtils::BoxVariant(node->Values[i]);
// Allocate managed node object (create GC handle to prevent destruction)
MObject* obj = type->CreateInstance();
@@ -202,7 +197,7 @@ bool AnimGraph::InitCustomNode(Node* node)
// Initialize node
InternalInitData initData;
initData.Values = values;
initData.Values = MUtils::ToArray(node->Values, MCore::TypeCache::Object);
initData.BaseModel = BaseModel.GetManagedInstance();
void* params[1];
params[0] = &initData;
@@ -211,7 +206,6 @@ bool AnimGraph::InitCustomNode(Node* node)
if (exception)
{
MCore::GCHandle::Free(handleGC);
MException ex(exception);
ex.Log(LogType::Warning, TEXT("AnimGraph"));
return false;

View File

@@ -217,6 +217,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
const float animPrevPos = GetAnimSamplePos(length, anim, prevPos, speed);
// Evaluate nested animations
bool hasNested = false;
if (anim->NestedAnims.Count() != 0)
{
for (auto& e : anim->NestedAnims)
@@ -234,11 +235,13 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
const float nestedAnimLength = nestedAnim.Anim->GetLength();
const float nestedAnimDuration = nestedAnim.Anim->GetDuration();
const float nestedAnimSpeed = nestedAnim.Speed * speed;
nestedAnimPos = nestedAnimPos / nestedAnimDuration * nestedAnimSpeed;
nestedAnimPrevPos = nestedAnimPrevPos / nestedAnimDuration * nestedAnimSpeed;
const float frameRateMatchScale = (float)nestedAnim.Anim->Data.FramesPerSecond / (float)anim->Data.FramesPerSecond;
nestedAnimPos = nestedAnimPos / nestedAnimDuration * nestedAnimSpeed * frameRateMatchScale;
nestedAnimPrevPos = nestedAnimPrevPos / nestedAnimDuration * nestedAnimSpeed * frameRateMatchScale;
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode);
hasNested = true;
}
}
}
@@ -291,7 +294,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
dstNode.Scale = srcNode.Scale * weight;
dstNode.Orientation = srcNode.Orientation * weight;
}
else
else if (!hasNested)
{
dstNode = srcNode;
}

View File

@@ -27,7 +27,6 @@ public:
/// <summary>
/// Returns true if material is an material instance.
/// </summary>
/// <returns>True if it's a material instance, otherwise false.</returns>
virtual bool IsMaterialInstance() const = 0;
public:

View File

@@ -37,6 +37,9 @@ public class Content : EngineModule
files.AddRange(Directory.GetFiles(FolderPath, "*.h", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Assets"), "*.h", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Cache"), "*.h", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Factories"), "*.h", SearchOption.TopDirectoryOnly));
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Storage"), "*.h", SearchOption.TopDirectoryOnly));
files.Add(Path.Combine(FolderPath, "Upgraders/BinaryAssetUpgrader.h"));
files.Add(Path.Combine(FolderPath, "Upgraders/IAssetUpgrader.h"));
}
}

View File

@@ -205,9 +205,6 @@ FlaxStorage::~FlaxStorage()
{
// Validate if has been disposed
ASSERT(IsDisposed());
// Validate other fields
// Note: disposed storage has no open files
CHECK(_chunksLock == 0);
CHECK(_refCount == 0);
ASSERT(_chunks.IsEmpty());
@@ -216,7 +213,6 @@ FlaxStorage::~FlaxStorage()
// Ensure to close any outstanding file handles to prevent file locking in case it failed to load
_file.DeleteAll();
#endif
}
FlaxStorage::LockData FlaxStorage::LockSafe()
@@ -550,11 +546,9 @@ bool FlaxStorage::Load()
}
break;
default:
{
LOG(Warning, "Unsupported storage format version: {1}. {0}", ToString(), _version);
return true;
}
}
// Mark as loaded (version number describes 'isLoaded' state)
_version = version;
@@ -573,7 +567,7 @@ bool FlaxStorage::Reload()
// Perform clean reloading
Dispose();
bool failed = Load();
const bool failed = Load();
OnReloaded(this, failed);
@@ -1434,10 +1428,8 @@ void FlaxFile::GetEntries(Array<Entry>& output) const
void FlaxFile::Dispose()
{
// Base
FlaxStorage::Dispose();
// Clean
_asset.ID = Guid::Empty;
}
@@ -1482,7 +1474,7 @@ bool FlaxPackage::HasAsset(const Guid& id) const
bool FlaxPackage::HasAsset(const AssetInfo& info) const
{
ASSERT(_path == info.Path);
Entry* e = _entries.TryGet(info.ID);
const Entry* e = _entries.TryGet(info.ID);
return e && e->TypeName == info.TypeName;
}
@@ -1511,10 +1503,8 @@ void FlaxPackage::GetEntries(Array<Entry>& output) const
void FlaxPackage::Dispose()
{
// Base
FlaxStorage::Dispose();
// Clean
_entries.Clear();
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.IO;
using Flax.Build;
using Flax.Build.NativeCpp;
@@ -23,5 +24,7 @@ public class ContentExporters : EngineModule
/// <inheritdoc />
public override void GetFilesToDeploy(List<string> files)
{
files.Add(Path.Combine(FolderPath, "AssetsExportingManager.h"));
files.Add(Path.Combine(FolderPath, "Types.h"));
}
}

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.IO;
using Flax.Build;
using Flax.Build.NativeCpp;
@@ -31,5 +32,7 @@ public class ContentImporters : EngineModule
/// <inheritdoc />
public override void GetFilesToDeploy(List<string> files)
{
files.Add(Path.Combine(FolderPath, "AssetsImportingManager.h"));
files.Add(Path.Combine(FolderPath, "Types.h"));
}
}

View File

@@ -38,9 +38,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
BoundingBox()
{
}
BoundingBox() = default;
/// <summary>
/// Initializes a new instance of the <see cref="BoundingBox"/> struct.

View File

@@ -34,9 +34,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
BoundingFrustum()
{
}
BoundingFrustum() = default;
/// <summary>
/// Initializes a new instance of the <see cref="BoundingFrustum"/> struct.

View File

@@ -34,9 +34,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
BoundingSphere()
{
}
BoundingSphere() = default;
/// <summary>
/// Initializes a new instance of the <see cref="BoundingSphere"/> struct.

View File

@@ -50,9 +50,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Color()
{
}
Color() = default;
/// <summary>
/// Initializes a new instance of the <see cref="Color"/> struct.

View File

@@ -53,9 +53,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Color32()
{
}
Color32() = default;
/// <summary>
/// Constructs a new Color32 with given r, g, b, a components.

View File

@@ -121,9 +121,7 @@ public:
/// <summary>
/// Default constructor
/// </summary>
Half2()
{
}
Half2() = default;
/// <summary>
/// Init
@@ -185,9 +183,7 @@ public:
Half Z;
public:
Half3()
{
}
Half3() = default;
Half3(Half x, Half y, Half z)
: X(x)
@@ -242,9 +238,7 @@ public:
Half W;
public:
Half4()
{
}
Half4() = default;
Half4(Half x, Half y, Half z, Half w)
: X(x)

View File

@@ -876,38 +876,54 @@ namespace FlaxEngine
/// <summary>
/// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range.
/// </summary>
/// <remarks>Optimized version of <see cref="UnwindRadiansAccurate"/> that is it faster and has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem.</remarks>
/// <param name="angle">Angle in radians to unwind.</param>
/// <returns>Valid angle in radians.</returns>
public static double UnwindRadians(double angle)
{
// TODO: make it faster?
var a = angle - Math.Floor(angle / TwoPi) * TwoPi; // Loop function between 0 and TwoPi
return a > Pi ? a - TwoPi : a; // Change range so it become Pi and -Pi
}
/// <summary>
/// The same as <see cref="UnwindRadians"/> but is more computation intensive with large <see href="angle"/> and has better accuracy with large <see href="angle"/>.
/// <br>cost of this function is <see href="angle"/> % <see cref="Pi"/></br>
/// </summary>
/// <param name="angle">Angle in radians to unwind.</param>
/// <returns>Valid angle in radians.</returns>
public static double UnwindRadiansAccurate(double angle)
{
while (angle > Pi)
{
angle -= TwoPi;
}
while (angle < -Pi)
{
angle += TwoPi;
}
return angle;
}
/// <summary>
/// Utility to ensure angle is between +/- 180 degrees by unwinding
/// Utility to ensure angle is between +/- 180 degrees by unwinding.
/// </summary>
/// <remarks>Optimized version of <see cref="UnwindDegreesAccurate"/> that is it faster and has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem.</remarks>
/// <param name="angle">Angle in degrees to unwind.</param>
/// <returns>Valid angle in degrees.</returns>
public static double UnwindDegrees(double angle)
{
// TODO: make it faster?
while (angle > 180.0f)
{
angle -= 360.0f;
}
while (angle < -180.0f)
{
angle += 360.0f;
}
var a = angle - Math.Floor(angle / 360.0) * 360.0; // Loop function between 0 and 360
return a > 180 ? a - 360.0 : a; // Change range so it become 180 and -180
}
/// <summary>
/// The same as <see cref="UnwindDegrees"/> but is more computation intensive with large <see href="angle"/> and has better accuracy with large <see href="angle"/>.
/// <br>cost of this function is <see href="angle"/> % 180.0f</br>
/// </summary>
/// <param name="angle">Angle in radians to unwind.</param>
/// <returns>Valid angle in radians.</returns>
public static double UnwindDegreesAccurate(double angle)
{
while (angle > 180.0)
angle -= 360.0;
while (angle < -180.0)
angle += 360.0;
return angle;
}
@@ -927,8 +943,9 @@ namespace FlaxEngine
/// Interpolates between two values using a linear function by a given amount.
/// </summary>
/// <remarks>
/// See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and
/// http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
/// See:
/// <br><seealso href="http://www.encyclopediaofmath.org/index.php/Linear_interpolation"/></br>
/// <br><seealso href="http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/"/></br>
/// </remarks>
/// <param name="from">Value to interpolate from.</param>
/// <param name="to">Value to interpolate to.</param>
@@ -944,7 +961,8 @@ namespace FlaxEngine
/// Performs smooth (cubic Hermite) interpolation between 0 and 1.
/// </summary>
/// <remarks>
/// See https://en.wikipedia.org/wiki/Smoothstep
/// See:
/// <br><seealso href="https://en.wikipedia.org/wiki/Smoothstep"/></br>
/// </remarks>
/// <param name="amount">Value between 0 and 1 indicating interpolation amount.</param>
public static double SmoothStep(double amount)
@@ -956,7 +974,8 @@ namespace FlaxEngine
/// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints.
/// </summary>
/// <remarks>
/// See https://en.wikipedia.org/wiki/Smoothstep
/// See:
/// <br><seealso href="https://en.wikipedia.org/wiki/Smoothstep"/></br>
/// </remarks>
/// <param name="amount">Value between 0 and 1 indicating interpolation amount.</param>
public static double SmootherStep(double amount)
@@ -1013,7 +1032,7 @@ namespace FlaxEngine
/// <summary>
/// Gauss function.
/// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function
/// <br><seealso href="http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function"/></br>
/// </summary>
/// <param name="amplitude">Curve amplitude.</param>
/// <param name="x">Position X.</param>

View File

@@ -1176,38 +1176,54 @@ namespace FlaxEngine
/// <summary>
/// Given a heading which may be outside the +/- PI range, 'unwind' it back into that range.
/// </summary>
/// <remarks>Optimized version of <see cref="UnwindRadiansAccurate"/> that is it faster and has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem.</remarks>
/// <param name="angle">Angle in radians to unwind.</param>
/// <returns>Valid angle in radians.</returns>
public static float UnwindRadians(float angle)
{
// TODO: make it faster?
var a = angle - (float)Math.Floor(angle / TwoPi) * TwoPi; // Loop function between 0 and TwoPi
return a > Pi ? a - TwoPi : a; // Change range so it become Pi and -Pi
}
/// <summary>
/// The same as <see cref="UnwindRadians"/> but is more computation intensive with large <see href="angle"/> and has better accuracy with large <see href="angle"/>.
/// <br>cost of this function is <see href="angle"/> % <see cref="Pi"/></br>
/// </summary>
/// <param name="angle">Angle in radians to unwind.</param>
/// <returns>Valid angle in radians.</returns>
public static float UnwindRadiansAccurate(float angle)
{
while (angle > Pi)
{
angle -= TwoPi;
}
while (angle < -Pi)
{
angle += TwoPi;
}
return angle;
}
/// <summary>
/// Utility to ensure angle is between +/- 180 degrees by unwinding
/// Utility to ensure angle is between +/- 180 degrees by unwinding.
/// </summary>
/// <remarks>Optimized version of <see cref="UnwindDegreesAccurate"/> that is it faster and has fixed cost but with large angle values (100 for example) starts to lose accuracy floating point problem.</remarks>
/// <param name="angle">Angle in degrees to unwind.</param>
/// <returns>Valid angle in degrees.</returns>
public static float UnwindDegrees(float angle)
{
// TODO: make it faster?
var a = angle - (float)Math.Floor(angle / 360.0f) * 360.0f; // Loop function between 0 and 360
return a > 180 ? a - 360.0f : a; // Change range so it become 180 and -180
}
/// <summary>
/// The same as <see cref="UnwindDegrees"/> but is more computation intensive with large <see href="angle"/> and has better accuracy with large <see href="angle"/>.
/// <br>cost of this function is <see href="angle"/> % 180.0f</br>
/// </summary>
/// <param name="angle">Angle in radians to unwind.</param>
/// <returns>Valid angle in radians.</returns>
public static float UnwindDegreesAccurate(float angle)
{
while (angle > 180.0f)
{
angle -= 360.0f;
}
while (angle < -180.0f)
{
angle += 360.0f;
}
return angle;
}
@@ -1299,8 +1315,12 @@ namespace FlaxEngine
/// <summary>
/// Interpolates between two values using a linear function by a given amount.
/// </summary>
/// <remarks>See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/</remarks>
/// <param name="from">Value to interpolate from.</param>
/// <remarks>
/// See:
/// <br><seealso href="http://www.encyclopediaofmath.org/index.php/Linear_interpolation"/></br>
/// <br><seealso href="http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/"/></br>
/// </remarks>
/// /// <param name="from">Value to interpolate from.</param>
/// <param name="to">Value to interpolate to.</param>
/// <param name="amount">Interpolation amount.</param>
/// <returns>The result of linear interpolation of values based on the amount.</returns>
@@ -1312,8 +1332,12 @@ namespace FlaxEngine
/// <summary>
/// Interpolates between two values using a linear function by a given amount.
/// </summary>
/// <remarks>See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/</remarks>
/// <param name="from">Value to interpolate from.</param>
/// <remarks>
/// See:
/// <br><seealso href="http://www.encyclopediaofmath.org/index.php/Linear_interpolation"/></br>
/// <br><seealso href="http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/"/></br>
/// </remarks>
/// /// <param name="from">Value to interpolate from.</param>
/// <param name="to">Value to interpolate to.</param>
/// <param name="amount">Interpolation amount.</param>
/// <returns>The result of linear interpolation of values based on the amount.</returns>
@@ -1325,8 +1349,12 @@ namespace FlaxEngine
/// <summary>
/// Interpolates between two values using a linear function by a given amount.
/// </summary>
/// <remarks>See http://www.encyclopediaofmath.org/index.php/Linear_interpolation and http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/</remarks>
/// <param name="from">Value to interpolate from.</param>
/// <remarks>
/// See:
/// <br><seealso href="http://www.encyclopediaofmath.org/index.php/Linear_interpolation"/></br>
/// <br><seealso href="http://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/"/></br>
/// </remarks>
/// /// <param name="from">Value to interpolate from.</param>
/// <param name="to">Value to interpolate to.</param>
/// <param name="amount">Interpolation amount.</param>
/// <returns>The result of linear interpolation of values based on the amount.</returns>
@@ -1338,7 +1366,10 @@ namespace FlaxEngine
/// <summary>
/// Performs smooth (cubic Hermite) interpolation between 0 and 1.
/// </summary>
/// <remarks>See https://en.wikipedia.org/wiki/Smoothstep</remarks>
/// <remarks>
/// See:
/// <br><seealso href="https://en.wikipedia.org/wiki/Smoothstep"/></br>
/// </remarks>
/// <param name="amount">Value between 0 and 1 indicating interpolation amount.</param>
public static float SmoothStep(float amount)
{
@@ -1348,7 +1379,10 @@ namespace FlaxEngine
/// <summary>
/// Performs smooth (cubic Hermite) interpolation between 0 and 1.
/// </summary>
/// <remarks>See https://en.wikipedia.org/wiki/Smoothstep</remarks>
/// <remarks>
/// See:
/// <br><seealso href="https://en.wikipedia.org/wiki/Smoothstep"/></br>
/// </remarks>
/// <param name="amount">Value between 0 and 1 indicating interpolation amount.</param>
public static double SmoothStep(double amount)
{
@@ -1358,7 +1392,10 @@ namespace FlaxEngine
/// <summary>
/// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints.
/// </summary>
/// <remarks>See https://en.wikipedia.org/wiki/Smoothstep</remarks>
/// <remarks>
/// See:
/// <br><seealso href="https://en.wikipedia.org/wiki/Smoothstep"/></br>
/// </remarks>
/// <param name="amount">Value between 0 and 1 indicating interpolation amount.</param>
public static float SmootherStep(float amount)
{
@@ -1368,7 +1405,10 @@ namespace FlaxEngine
/// <summary>
/// Performs a smooth(er) interpolation between 0 and 1 with 1st and 2nd order derivatives of zero at endpoints.
/// </summary>
/// <remarks>See https://en.wikipedia.org/wiki/Smoothstep</remarks>
/// <remarks>
/// See:
/// <br><seealso href="https://en.wikipedia.org/wiki/Smoothstep"/></br>
/// </remarks>
/// <param name="amount">Value between 0 and 1 indicating interpolation amount.</param>
public static double SmootherStep(double amount)
{
@@ -1446,7 +1486,7 @@ namespace FlaxEngine
/// <summary>
/// Gauss function.
/// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function
/// <br><seealso href="http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function"/></br>
/// </summary>
/// <param name="amplitude">Curve amplitude.</param>
/// <param name="x">Position X.</param>
@@ -1463,7 +1503,7 @@ namespace FlaxEngine
/// <summary>
/// Gauss function.
/// http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function
/// <br><seealso href="http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function"/></br>
/// </summary>
/// <param name="amplitude">Curve amplitude.</param>
/// <param name="x">Position X.</param>

View File

@@ -83,9 +83,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Matrix()
{
}
Matrix() = default;
/// <summary>
/// Initializes a new instance of the <see cref="Matrix"/> struct.

View File

@@ -63,9 +63,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Matrix3x3()
{
}
Matrix3x3() = default;
/// <summary>
/// Initializes a new instance of the <see cref="Matrix3x3"/> struct.

View File

@@ -30,9 +30,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Plane()
{
}
Plane() = default;
/// <summary>
/// Init

View File

@@ -67,9 +67,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Quaternion()
{
}
Quaternion() = default;
/// <summary>
/// Init

View File

@@ -35,9 +35,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Ray()
{
}
Ray() = default;
/// <summary>
/// Initializes a new instance of the <see cref="Ray"/> struct.

View File

@@ -31,9 +31,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Rectangle()
{
}
Rectangle() = default;
// Init
// @param x Rectangle location X coordinate

View File

@@ -40,9 +40,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Transform()
{
}
Transform() = default;
/// <summary>
/// Initializes a new instance of the <see cref="Transform"/> struct.

View File

@@ -30,9 +30,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Triangle()
{
}
Triangle() = default;
/// <summary>
/// Initializes a new instance of the <see cref="Triangle"/> struct.

View File

@@ -60,9 +60,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Vector2Base()
{
}
Vector2Base() = default;
FORCE_INLINE Vector2Base(T xy)
: X(xy)

View File

@@ -89,9 +89,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Vector3Base()
{
}
Vector3Base() = default;
FORCE_INLINE Vector3Base(T xyz)
: X(xyz)

View File

@@ -76,9 +76,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Vector4Base()
{
}
Vector4Base() = default;
FORCE_INLINE Vector4Base(T xyzw)
: X(xyzw)

View File

@@ -52,9 +52,7 @@ public:
/// <summary>
/// Empty constructor.
/// </summary>
Viewport()
{
}
Viewport() = default;
// Init
// @param x The x coordinate of the upper-left corner of the viewport in pixels

View File

@@ -115,6 +115,12 @@ inline Span<T> ToSpan(const T* ptr, int32 length)
return Span<T>(ptr, length);
}
template<typename T, typename U = T, typename AllocationType = HeapAllocation>
inline Span<U> ToSpan(const Array<T, AllocationType>& data)
{
return Span<U>((U*)data.Get(), data.Count());
}
template<typename T>
inline bool SpanContains(const Span<T> span, const T& value)
{

View File

@@ -2796,6 +2796,122 @@ String Variant::ToString() const
}
}
void Variant::Inline()
{
VariantType::Types type = VariantType::Null;
byte data[sizeof(Matrix)];
if (Type.Type == VariantType::Structure && AsBlob.Data && AsBlob.Length <= sizeof(Matrix))
{
for (int32 i = 2; i < VariantType::MAX; i++)
{
if (StringUtils::Compare(Type.TypeName, InBuiltTypesTypeNames[i]) == 0)
{
type = (VariantType::Types)i;
break;
}
}
if (type == VariantType::Null)
{
// Aliases
if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector2") == 0)
type = VariantType::Types::Vector2;
else if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector3") == 0)
type = VariantType::Types::Vector3;
else if (StringUtils::Compare(Type.TypeName, "FlaxEngine.Vector4") == 0)
type = VariantType::Types::Vector4;
}
if (type != VariantType::Null)
Platform::MemoryCopy(data, AsBlob.Data, AsBlob.Length);
}
if (type != VariantType::Null)
{
switch (type)
{
case VariantType::Bool:
*this = *(bool*)data;
break;
case VariantType::Int:
*this = *(int32*)data;
break;
case VariantType::Uint:
*this = *(uint32*)data;
break;
case VariantType::Int64:
*this = *(int64*)data;
break;
case VariantType::Uint64:
*this = *(uint64*)data;
break;
case VariantType::Float:
*this = *(float*)data;
break;
case VariantType::Double:
*this = *(double*)data;
break;
case VariantType::Float2:
*this = *(Float2*)data;
break;
case VariantType::Float3:
*this = *(Float3*)data;
break;
case VariantType::Float4:
*this = *(Float4*)data;
break;
case VariantType::Color:
*this = *(Color*)data;
break;
case VariantType::Guid:
*this = *(Guid*)data;
break;
case VariantType::BoundingBox:
*this = Variant(*(BoundingBox*)data);
break;
case VariantType::BoundingSphere:
*this = *(BoundingSphere*)data;
break;
case VariantType::Quaternion:
*this = *(Quaternion*)data;
break;
case VariantType::Transform:
*this = Variant(*(Transform*)data);
break;
case VariantType::Rectangle:
*this = *(Rectangle*)data;
break;
case VariantType::Ray:
*this = Variant(*(Ray*)data);
break;
case VariantType::Matrix:
*this = Variant(*(Matrix*)data);
break;
case VariantType::Int2:
*this = *(Int2*)data;
break;
case VariantType::Int3:
*this = *(Int3*)data;
break;
case VariantType::Int4:
*this = *(Int4*)data;
break;
case VariantType::Int16:
*this = *(int16*)data;
break;
case VariantType::Uint16:
*this = *(uint16*)data;
break;
case VariantType::Double2:
*this = *(Double2*)data;
break;
case VariantType::Double3:
*this = *(Double3*)data;
break;
case VariantType::Double4:
*this = *(Double4*)data;
break;
}
}
}
bool Variant::CanCast(const Variant& v, const VariantType& to)
{
if (v.Type == to)
@@ -3682,6 +3798,7 @@ void Variant::AllocStructure()
const ScriptingType& type = typeHandle.GetType();
AsBlob.Length = type.Size;
AsBlob.Data = Allocator::Allocate(AsBlob.Length);
Platform::MemoryClear(AsBlob.Data, AsBlob.Length);
type.Struct.Ctor(AsBlob.Data);
}
else if (typeName == "System.Int16" || typeName == "System.UInt16")

View File

@@ -359,6 +359,9 @@ public:
void SetAsset(Asset* asset);
String ToString() const;
// Inlines potential value type into in-built format (eg. Vector3 stored as Structure, or String stored as ManagedObject).
void Inline();
FORCE_INLINE Variant Cast(const VariantType& to) const
{
return Cast(*this, to);

View File

@@ -72,15 +72,6 @@ public:
return _major;
}
/// <summary>
/// Gets the high 16 bits of the revision number.
/// </summary>
/// <returns>A 16-bit signed integer.</returns>
FORCE_INLINE int16 MajorRevision() const
{
return static_cast<int16>(_revision >> 16);
}
/// <summary>
/// Gets the value of the minor component of the version number for the current Version object.
/// </summary>
@@ -90,15 +81,6 @@ public:
return _minor;
}
/// <summary>
/// Gets the low 16 bits of the revision number.
/// </summary>
/// <returns>A 16-bit signed integer.</returns>
FORCE_INLINE int16 MinorRevision() const
{
return static_cast<int16>(_revision & 65535);
}
/// <summary>
/// Gets the value of the revision component of the version number for the current Version object.
/// </summary>
@@ -126,61 +108,26 @@ public:
return _major == obj._major && _minor == obj._minor && _build == obj._build && _revision == obj._revision;
}
/// <summary>
/// Determines whether two specified Version objects are equal.
/// </summary>
/// <param name="other">The other Version object.</param>
/// <returns>True if <paramref name="v1" /> equals <paramref name="v2" />; otherwise, false.</returns>
FORCE_INLINE bool operator==(const Version& other) const
{
return Equals(other);
}
/// <summary>
/// Determines whether the first specified Version object is greater than the second specified Version object.
/// </summary>
/// <param name="other">The first Version object.</param>
/// <returns>True if <paramref name="v1" /> is greater than <paramref name="v2" />; otherwise, false.</returns>
FORCE_INLINE bool operator >(const Version& other) const
FORCE_INLINE bool operator>(const Version& other) const
{
return other < *this;
}
/// <summary>
/// Determines whether the first specified Version object is greater than or equal to the second specified Version object.
/// /summary>
/// <param name="other">The other Version object.</param>
/// <returns>True if <paramref name="v1" /> is greater than or equal to <paramref name="v2" />; otherwise, false.</returns>
FORCE_INLINE bool operator >=(const Version& other) const
FORCE_INLINE bool operator>=(const Version& other) const
{
return other <= *this;
}
/// <summary>
/// Determines whether two specified Version objects are not equal.
/// </summary>
/// <param name="other">The other Version object.</param>
/// <returns>True if <paramref name="v1" /> does not equal <paramref name="v2" />; otherwise, false.</returns>
FORCE_INLINE bool operator!=(const Version& other) const
{
return !(*this == other);
}
/// <summary>
/// Determines whether the first specified Version object is less than the second specified Version object.
/// </summary>
/// <param name="other">The first other object.</param>
/// <returns>True if <paramref name="v1" /> is less than <paramref name="v2" />; otherwise, false.</returns>
FORCE_INLINE bool operator<(const Version& other) const
{
return CompareTo(other) < 0;
}
/// <summary>
/// Determines whether the first specified Version object is less than or equal to the second Version object.
/// </summary>
/// <param name="other">The other Version object.</param>
/// <returns>True if <paramref name="v1" /> is less than or equal to <paramref name="v2" />; otherwise, false.</returns>
FORCE_INLINE bool operator<=(const Version& other) const
{
return CompareTo(other) <= 0;

View File

@@ -274,12 +274,12 @@ namespace FlaxEngine.Interop
#if FLAX_EDITOR
[HideInEditor]
#endif
internal static class ManagedString
public static class ManagedString
{
internal static ManagedHandle EmptyStringHandle = ManagedHandle.Alloc(string.Empty);
[System.Diagnostics.DebuggerStepThrough]
internal static unsafe IntPtr ToNative(string str)
public static unsafe IntPtr ToNative(string str)
{
if (str == null)
return IntPtr.Zero;
@@ -290,7 +290,7 @@ namespace FlaxEngine.Interop
}
[System.Diagnostics.DebuggerStepThrough]
internal static unsafe IntPtr ToNativeWeak(string str)
public static unsafe IntPtr ToNativeWeak(string str)
{
if (str == null)
return IntPtr.Zero;
@@ -301,7 +301,7 @@ namespace FlaxEngine.Interop
}
[System.Diagnostics.DebuggerStepThrough]
internal static string ToManaged(IntPtr ptr)
public static string ToManaged(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
@@ -309,7 +309,7 @@ namespace FlaxEngine.Interop
}
[System.Diagnostics.DebuggerStepThrough]
internal static void Free(IntPtr ptr)
public static void Free(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return;

View File

@@ -244,7 +244,25 @@ namespace FlaxEngine.Interop
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
typeAttributes = (uint)type.Attributes,
};
*assemblyHandle = GetAssemblyHandle(type.Assembly);
Assembly assembly = null;
if (type.IsGenericType && !type.Assembly.IsCollectible)
{
// The owning assembly of a generic type with type arguments referencing
// collectible assemblies must be one of the collectible assemblies.
foreach (var genericType in type.GetGenericArguments())
{
if (genericType.Assembly.IsCollectible)
{
assembly = genericType.Assembly;
break;
}
}
}
if (assembly == null)
assembly = type.Assembly;
*assemblyHandle = GetAssemblyHandle(assembly);
}
[UnmanagedCallersOnly]

View File

@@ -444,7 +444,7 @@ namespace FlaxEngine.Interop
}
else
{
toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper<T>.ReferenceTypeField<ReferenceTypePlaceholder>.ToManagedField), bindingFlags);
toManagedFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, arrayElementType).GetMethod(nameof(MarshalHelper<T>.ReferenceTypeField<ReferenceTypePlaceholder>.ToManagedFieldArray), bindingFlags);
toNativeFieldMethod = typeof(MarshalHelper<>.ReferenceTypeField<>).MakeGenericType(type, fieldType).GetMethod(nameof(MarshalHelper<T>.ReferenceTypeField<ReferenceTypePlaceholder>.ToNativeField), bindingFlags);
}
}
@@ -663,6 +663,17 @@ namespace FlaxEngine.Interop
MarshalHelperReferenceType<TField>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(fieldPtr.ToPointer()), false);
}
internal static void ToManagedFieldArray(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset)
{
fieldOffset = Unsafe.SizeOf<IntPtr>();
IntPtr fieldStartPtr = fieldPtr;
fieldPtr = EnsureAlignment(fieldPtr, IntPtr.Size);
fieldOffset += (fieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref GetFieldReference<TField[]>(field, ref fieldOwner);
MarshalHelperReferenceType<TField>.ToManagedArray(ref fieldValueRef, Unsafe.Read<IntPtr>(fieldPtr.ToPointer()), false);
}
internal static void ToNativeField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset)
{
fieldOffset = Unsafe.SizeOf<IntPtr>();
@@ -691,14 +702,15 @@ namespace FlaxEngine.Interop
internal static void ToManaged(ref T managedValue, IntPtr nativePtr, bool byRef)
{
Type type = typeof(T);
if (type.IsByRef || byRef)
byRef |= type.IsByRef;
if (byRef)
{
if (type.IsByRef)
type = type.GetElementType();
Assert.IsTrue(type.IsValueType);
}
if (type == typeof(IntPtr))
if (type == typeof(IntPtr) && byRef)
managedValue = (T)(object)nativePtr;
else if (type == typeof(ManagedHandle))
managedValue = (T)(object)ManagedHandle.FromIntPtr(nativePtr);
@@ -778,10 +790,12 @@ namespace FlaxEngine.Interop
if (type == typeof(string))
managedValue = Unsafe.As<T>(ManagedString.ToManaged(nativePtr));
else if (nativePtr == IntPtr.Zero)
managedValue = null;
else if (type.IsClass)
managedValue = nativePtr != IntPtr.Zero ? Unsafe.As<T>(ManagedHandle.FromIntPtr(nativePtr).Target) : null;
managedValue = Unsafe.As<T>(ManagedHandle.FromIntPtr(nativePtr).Target);
else if (type.IsInterface) // Dictionary
managedValue = nativePtr != IntPtr.Zero ? Unsafe.As<T>(ManagedHandle.FromIntPtr(nativePtr).Target) : null;
managedValue = Unsafe.As<T>(ManagedHandle.FromIntPtr(nativePtr).Target);
else
throw new NotImplementedException();
}

View File

@@ -238,7 +238,7 @@ public:
/// <summary>
/// Gets the default material.
/// </summary>
MaterialBase* GetDefaultMaterial() const;
API_PROPERTY() MaterialBase* GetDefaultMaterial() const;
/// <summary>
/// Gets the default material (Deformable domain).

View File

@@ -898,6 +898,31 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
DrawModes |= DrawPass::GlobalSurfaceAtlas;
}
const Span<MaterialSlot> AnimatedModel::GetMaterialSlots() const
{
const auto model = SkinnedModel.Get();
if (model && !model->WaitForLoaded())
return ToSpan(model->MaterialSlots);
return Span<MaterialSlot>();
}
MaterialBase* AnimatedModel::GetMaterial(int32 entryIndex)
{
if (SkinnedModel)
SkinnedModel->WaitForLoaded();
else
return nullptr;
CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
MaterialBase* material = Entries[entryIndex].Material.Get();
if (!material)
{
material = SkinnedModel->MaterialSlots[entryIndex].Material.Get();
if (!material)
material = GPUDevice::Instance->GetDefaultMaterial();
}
return material;
}
bool AnimatedModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
{
auto model = SkinnedModel.Get();

View File

@@ -373,6 +373,8 @@ public:
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
const Span<MaterialSlot> GetMaterialSlots() const override;
MaterialBase* GetMaterial(int32 entryIndex) override;
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
void OnDeleteObject() override;

View File

@@ -39,10 +39,9 @@ void ModelInstanceActor::SetMaterial(int32 entryIndex, MaterialBase* material)
MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 entryIndex)
{
WaitForModelLoad();
CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
auto material = Entries[entryIndex].Material.Get();
MaterialBase* material = GetMaterial(entryIndex);
CHECK_RETURN(material && !material->WaitForLoaded(), nullptr);
const auto result = material->CreateVirtualInstance();
MaterialInstance* result = material->CreateVirtualInstance();
Entries[entryIndex].Material = result;
if (_sceneRenderingKey != -1)
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);

View File

@@ -35,6 +35,17 @@ public:
/// </summary>
API_PROPERTY() void SetEntries(const Array<ModelInstanceEntry>& value);
/// <summary>
/// Gets the material slots array set on the asset (eg. model or skinned model asset).
/// </summary>
API_PROPERTY(Sealed) virtual const Span<class MaterialSlot> GetMaterialSlots() const = 0;
/// <summary>
/// Gets the material used to draw the meshes which are assigned to that slot (set in Entries or model's default).
/// </summary>
/// <param name="entryIndex">The material slot entry index.</param>
API_FUNCTION(Sealed) virtual MaterialBase* GetMaterial(int32 entryIndex) = 0;
/// <summary>
/// Sets the material to the entry slot. Can be used to override the material of the meshes using this slot.
/// </summary>

View File

@@ -341,6 +341,31 @@ void SplineModel::OnParentChanged()
OnSplineUpdated();
}
const Span<MaterialSlot> SplineModel::GetMaterialSlots() const
{
const auto model = Model.Get();
if (model && !model->WaitForLoaded())
return ToSpan(model->MaterialSlots);
return Span<MaterialSlot>();
}
MaterialBase* SplineModel::GetMaterial(int32 entryIndex)
{
if (Model)
Model->WaitForLoaded();
else
return nullptr;
CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
MaterialBase* material = Entries[entryIndex].Material.Get();
if (!material)
{
material = Model->MaterialSlots[entryIndex].Material.Get();
if (!material)
material = GPUDevice::Instance->GetDefaultDeformableMaterial();
}
return material;
}
bool SplineModel::HasContentLoaded() const
{
return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded();

View File

@@ -115,6 +115,8 @@ public:
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
void OnParentChanged() override;
const Span<MaterialSlot> GetMaterialSlots() const override;
MaterialBase* GetMaterial(int32 entryIndex) override;
protected:
// [ModelInstanceActor]

View File

@@ -535,6 +535,31 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
}
}
const Span<MaterialSlot> StaticModel::GetMaterialSlots() const
{
const auto model = Model.Get();
if (model && !model->WaitForLoaded())
return ToSpan(model->MaterialSlots);
return Span<MaterialSlot>();
}
MaterialBase* StaticModel::GetMaterial(int32 entryIndex)
{
if (Model)
Model->WaitForLoaded();
else
return nullptr;
CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
MaterialBase* material = Entries[entryIndex].Material.Get();
if (!material)
{
material = Model->MaterialSlots[entryIndex].Material.Get();
if (!material)
material = GPUDevice::Instance->GetDefaultMaterial();
}
return material;
}
bool StaticModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
{
auto model = Model.Get();

View File

@@ -121,7 +121,7 @@ public:
/// <param name="meshIndex">The zero-based mesh index.</param>
/// <param name="lodIndex">The LOD index.</param>
/// <returns>Material or null if not assigned.</returns>
API_FUNCTION() MaterialBase* GetMaterial(int32 meshIndex, int32 lodIndex = 0) const;
API_FUNCTION() MaterialBase* GetMaterial(int32 meshIndex, int32 lodIndex) const;
/// <summary>
/// Gets the color of the painter vertex (this model instance).
@@ -166,6 +166,8 @@ public:
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
const Span<MaterialSlot> GetMaterialSlots() const override;
MaterialBase* GetMaterial(int32 entryIndex) override;
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;

View File

@@ -189,7 +189,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va
const Matrix viewProjection = context.ViewTask ? context.ViewTask->View.PrevViewProjection : Matrix::Identity;
const Float3 position = (Float3)TryGetValue(node->GetBox(0), Value::Zero);
Float4 projPos;
Float3::Transform(position, viewProjection);
Float3::Transform(position, viewProjection, projPos);
projPos /= projPos.W;
value = Float2(projPos.X * 0.5f + 0.5f, projPos.Y * 0.5f + 0.5f);
break;

View File

@@ -56,36 +56,94 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
{
if (CommandLine::Options.Headless)
return DialogResult::None;
CFStringRef textRef = AppleUtils::ToString(text);
CFStringRef captionRef = AppleUtils::ToString(caption);
CFOptionFlags flags = 0;
NSAlert* alert = [[NSAlert alloc] init];
ASSERT(alert);
switch (buttons)
{
case MessageBoxButtons::AbortRetryIgnore:
[alert addButtonWithTitle:@"Abort"];
[alert addButtonWithTitle:@"Retry"];
[alert addButtonWithTitle:@"Ignore"];
break;
case MessageBoxButtons::OK:
[alert addButtonWithTitle:@"OK"];
break;
case MessageBoxButtons::OKCancel:
[alert addButtonWithTitle:@"OK"];
[alert addButtonWithTitle:@"Cancel"];
break;
case MessageBoxButtons::RetryCancel:
[alert addButtonWithTitle:@"Retry"];
[alert addButtonWithTitle:@"Cancel"];
break;
case MessageBoxButtons::YesNo:
[alert addButtonWithTitle:@"Yes"];
[alert addButtonWithTitle:@"No"];
break;
case MessageBoxButtons::YesNoCancel:
flags |= kCFUserNotificationCancelResponse;
[alert addButtonWithTitle:@"Yes"];
[alert addButtonWithTitle:@"No"];
[alert addButtonWithTitle:@"Cancel"];
break;
}
switch (icon)
{
case MessageBoxIcon::Information:
flags |= kCFUserNotificationNoteAlertLevel;
[alert setAlertStyle:NSAlertStyleCritical];
break;
case MessageBoxIcon::Error:
case MessageBoxIcon::Stop:
flags |= kCFUserNotificationStopAlertLevel;
[alert setAlertStyle:NSAlertStyleInformational];
break;
case MessageBoxIcon::Warning:
flags |= kCFUserNotificationCautionAlertLevel;
[alert setAlertStyle:NSAlertStyleWarning];
break;
}
SInt32 result = CFUserNotificationDisplayNotice(0, flags, nullptr, nullptr, nullptr, captionRef, textRef, nullptr);
CFRelease(captionRef);
CFRelease(textRef);
return DialogResult::OK;
[alert setMessageText:(NSString*)AppleUtils::ToString(caption)];
[alert setInformativeText:(NSString*)AppleUtils::ToString(text)];
NSInteger button = [alert runModal];
DialogResult result = DialogResult::OK;
switch (buttons)
{
case MessageBoxButtons::AbortRetryIgnore:
if (button == NSAlertFirstButtonReturn)
result = DialogResult::Abort;
else if (button == NSAlertSecondButtonReturn)
result = DialogResult::Retry;
else
result = DialogResult::Ignore;
break;
case MessageBoxButtons::OK:
result = DialogResult::OK;
break;
case MessageBoxButtons::OKCancel:
if (button == NSAlertFirstButtonReturn)
result = DialogResult::OK;
else
result = DialogResult::Cancel;
break;
case MessageBoxButtons::RetryCancel:
if (button == NSAlertFirstButtonReturn)
result = DialogResult::Retry;
else
result = DialogResult::Cancel;
break;
case MessageBoxButtons::YesNo:
if (button == NSAlertFirstButtonReturn)
result = DialogResult::Yes;
else
result = DialogResult::No;
break;
case MessageBoxButtons::YesNoCancel:
if (button == NSAlertFirstButtonReturn)
result = DialogResult::Yes;
else if (button == NSAlertSecondButtonReturn)
result = DialogResult::No;
else
result = DialogResult::Cancel;
break;
}
return result;
}
Float2 AppleUtils::PosToCoca(const Float2& pos)
@@ -301,15 +359,16 @@ Float2 MacPlatform::GetMousePosition()
CGEventRef event = CGEventCreate(nullptr);
CGPoint cursor = CGEventGetLocation(event);
CFRelease(event);
return Float2((float)cursor.x, (float)cursor.y);
return Float2((float)cursor.x, (float)cursor.y) * MacPlatform::ScreenScale;
}
void MacPlatform::SetMousePosition(const Float2& pos)
{
CGPoint cursor;
cursor.x = (CGFloat)pos.X;
cursor.y = (CGFloat)pos.Y;
cursor.x = (CGFloat)(pos.X / MacPlatform::ScreenScale);
cursor.y = (CGFloat)(pos.Y / MacPlatform::ScreenScale);
CGWarpMouseCursorPosition(cursor);
CGAssociateMouseAndMouseCursorPosition(true);
}
Float2 MacPlatform::GetDesktopSize()

View File

@@ -72,11 +72,11 @@ KeyboardKeys GetKey(NSEvent* event)
case 0x30: return KeyboardKeys::Tab;
case 0x31: return KeyboardKeys::Spacebar;
case 0x32: return KeyboardKeys::BackQuote;
case 0x33: return KeyboardKeys::Delete;
case 0x33: return KeyboardKeys::Backspace;
//case 0x34:
case 0x35: return KeyboardKeys::Escape;
//case 0x36:
//case 0x37: Command
case 0x36: return KeyboardKeys::Control; // Command (right)
case 0x37: return KeyboardKeys::Control; // Command (left)
case 0x38: return KeyboardKeys::Shift;
case 0x39: return KeyboardKeys::Capital;
case 0x3A: return KeyboardKeys::Alt;
@@ -378,7 +378,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
{
KeyboardKeys key = GetKey(event);
if (key != KeyboardKeys::None)
Input::Keyboard->OnKeyDown(key);
Input::Keyboard->OnKeyDown(key, Window);
// Send a text input event
switch (key)
@@ -400,7 +400,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
if (length >= 16)
length = 15;
[text getCharacters:buffer range:NSMakeRange(0, length)];
Input::Keyboard->OnCharInput((Char)buffer[0]);
Input::Keyboard->OnCharInput((Char)buffer[0], Window);
}
}
@@ -408,7 +408,32 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
{
KeyboardKeys key = GetKey(event);
if (key != KeyboardKeys::None)
Input::Keyboard->OnKeyUp(key);
Input::Keyboard->OnKeyUp(key, Window);
}
- (void)flagsChanged:(NSEvent*)event
{
int32 modMask;
int32 keyCode = [event keyCode];
if (keyCode == 0x36 || keyCode == 0x37)
modMask = NSEventModifierFlagCommand;
else if (keyCode == 0x38 || keyCode == 0x3c)
modMask = NSEventModifierFlagShift;
else if (keyCode == 0x3a || keyCode == 0x3d)
modMask = NSEventModifierFlagOption;
else if (keyCode == 0x3b || keyCode == 0x3e)
modMask = NSEventModifierFlagControl;
else
return;
KeyboardKeys key = GetKey(event);
if (key != KeyboardKeys::None)
{
int32 modifierFlags = [event modifierFlags];
if ((modifierFlags & modMask) == modMask)
Input::Keyboard->OnKeyDown(key, Window);
else
Input::Keyboard->OnKeyUp(key, Window);
}
}
- (void)scrollWheel:(NSEvent*)event
@@ -643,7 +668,6 @@ MacWindow::MacWindow(const CreateWindowSettings& settings)
// TODO: impl StartPosition for MacWindow
// TODO: impl Fullscreen for MacWindow
// TODO: impl ShowInTaskbar for MacWindow
// TODO: impl AllowInput for MacWindow
// TODO: impl IsTopmost for MacWindow
}

View File

@@ -17,10 +17,13 @@ namespace FlaxEngine
[Unmanaged]
public static void DrawSceneDepth(GPUContext context, SceneRenderTask task, GPUTexture output, List<Actor> customActors)
{
if (customActors.Count == 0)
return;
var temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME
var tempCount = temp.Length;
Actor[] temp = null;
int tempCount = 0;
if (customActors != null && customActors.Count != 0)
{
temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME
tempCount = temp.Length;
}
Internal_DrawSceneDepth(FlaxEngine.Object.GetUnmanagedPtr(context), FlaxEngine.Object.GetUnmanagedPtr(task), FlaxEngine.Object.GetUnmanagedPtr(output), temp, ref tempCount);
}
@@ -32,7 +35,14 @@ namespace FlaxEngine
[Unmanaged]
public static void DrawActors(ref RenderContext renderContext, List<Actor> customActors)
{
DrawActors(ref renderContext, Utils.ExtractArrayFromList(customActors));
Actor[] temp = null;
int tempCount = 0;
if (customActors != null && customActors.Count != 0)
{
temp = CollectionsMarshal.AsSpan(customActors).ToArray(); // FIXME
tempCount = temp.Length;
}
Internal_DrawActors(ref renderContext, temp, ref tempCount);
}
}
}

View File

@@ -108,7 +108,7 @@ void ManagedSerialization::Deserialize(const StringAnsiView& data, MObject* obje
// Prepare arguments
void* args[3];
args[0] = object;
args[1] = (void*)str;
args[1] = (void*)&str;
args[2] = (void*)&len;
// Call serialization tool

View File

@@ -3,7 +3,6 @@
#include "MUtils.h"
#include "MClass.h"
#include "MCore.h"
#include "MDomain.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Core/Types/Version.h"
@@ -449,7 +448,7 @@ Variant MUtils::UnboxVariant(MObject* value)
{
auto& a = array[i];
a.SetType(elementType);
Platform::MemoryCopy(&a.AsData,(byte*)ptr + elementSize * i, elementSize);
Platform::MemoryCopy(&a.AsData, (byte*)ptr + elementSize * i, elementSize);
}
break;
case VariantType::Transform:
@@ -1004,11 +1003,11 @@ MClass* MUtils::GetClass(const Variant& value)
case VariantType::Enum:
return Scripting::FindClass(StringAnsiView(value.Type.TypeName));
case VariantType::ManagedObject:
{
MObject* obj = (MObject*)value;
if (obj)
return MCore::Object::GetClass(obj);
}
{
MObject* obj = (MObject*)value;
if (obj)
return MCore::Object::GetClass(obj);
}
default: ;
}
return GetClass(value.Type);
@@ -1224,10 +1223,10 @@ MObject* MUtils::ToManaged(const Version& value)
auto versionToManaged = scriptingClass->GetMethod("VersionToManaged", 4);
CHECK_RETURN(versionToManaged, nullptr);
int major = value.Major();
int minor = value.Minor();
int build = value.Build();
int revision = value.Revision();
int32 major = value.Major();
int32 minor = value.Minor();
int32 build = value.Build();
int32 revision = value.Revision();
void* params[4];
params[0] = &major;
@@ -1244,26 +1243,29 @@ MObject* MUtils::ToManaged(const Version& value)
Version MUtils::ToNative(MObject* value)
{
Version result;
if (value)
#if USE_NETCORE
{
auto ver = Version();
auto scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, ver);
auto versionToNative = scriptingClass->GetMethod("VersionToNative", 2);
CHECK_RETURN(versionToNative, ver);
CHECK_RETURN(scriptingClass, result);
auto versionToNative = scriptingClass->GetMethod("VersionToNative", 5);
CHECK_RETURN(versionToNative, result);
void* params[2];
void* params[5];
params[0] = value;
params[1] = &ver;
params[1] = (byte*)&result;
params[2] = (byte*)&result + sizeof(int32);
params[3] = (byte*)&result + sizeof(int32) * 2;
params[4] = (byte*)&result + sizeof(int32) * 3;
versionToNative->Invoke(nullptr, params, nullptr);
return ver;
return result;
}
#else
return *(Version*)MCore::Object::Unbox(value);
#endif
return Version();
return result;
}
#endif

View File

@@ -476,12 +476,28 @@ bool Scripting::Load()
// Load FlaxEngine
const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
if (((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->Load(flaxEnginePath))
auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine();
if (flaxEngineModule->Assembly->Load(flaxEnginePath))
{
LOG(Error, "Failed to load FlaxEngine C# assembly.");
return true;
}
// Insert type aliases for vector types that don't exist in C++ but are just typedef (properly redirect them to actual types)
// TODO: add support for automatic typedef aliases setup for scripting module to properly lookup type from the alias typename
#if USE_LARGE_WORLDS
flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double2"];
flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double3"];
flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Double4"];
#else
flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float2"];
flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float3"];
flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float4"];
#endif
flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector2")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"];
flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector3")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"];
flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector4")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"];
#if USE_EDITOR
// Skip loading game modules in Editor on startup - Editor loads them later during splash screen (eg. after first compilation)
static bool SkipFirstLoad = true;

View File

@@ -229,22 +229,24 @@ namespace FlaxEngine
return ManagedHandle.Alloc(new CultureInfo(lcid));
}
internal static void VersionToNative(ManagedHandle versionHandle, IntPtr nativePtr)
[StructLayout(LayoutKind.Sequential)]
internal struct VersionNative
{
public int Major;
public int Minor;
public int Build;
public int Revision;
}
internal static void VersionToNative(ManagedHandle versionHandle, ref int major, ref int minor, ref int build, ref int revision)
{
Version version = Unsafe.As<Version>(versionHandle.Target);
if (version != null)
{
Marshal.WriteInt32(nativePtr, 0, version.Major);
Marshal.WriteInt32(nativePtr, 4, version.Minor);
Marshal.WriteInt32(nativePtr, 8, version.Build);
Marshal.WriteInt32(nativePtr, 12, version.Revision);
}
else
{
Marshal.WriteInt32(nativePtr, 0, 0);
Marshal.WriteInt32(nativePtr, 4, 0);
Marshal.WriteInt32(nativePtr, 8, -1);
Marshal.WriteInt32(nativePtr, 12, -1);
major = version.Major;
minor = version.Minor;
build = version.Build;
revision = version.Revision;
}
}

View File

@@ -0,0 +1,48 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#if FLAX_TESTS
using System;
using NUnit.Framework;
namespace FlaxEngine.Tests
{
/// <summary>
/// Tests for <see cref="Mathf"/> and <see cref="Mathd"/>.
/// </summary>
[TestFixture]
public class TestMath
{
/// <summary>
/// Test unwinding angles.
/// </summary>
[Test]
public void TestUnwind()
{
Assert.AreEqual(0.0f, Mathf.UnwindDegreesAccurate(0.0f));
Assert.AreEqual(45.0f, Mathf.UnwindDegreesAccurate(45.0f));
Assert.AreEqual(90.0f, Mathf.UnwindDegreesAccurate(90.0f));
Assert.AreEqual(180.0f, Mathf.UnwindDegreesAccurate(180.0f));
Assert.AreEqual(0.0f, Mathf.UnwindDegreesAccurate(360.0f));
Assert.AreEqual(0.0f, Mathf.UnwindDegrees(0.0f));
Assert.AreEqual(45.0f, Mathf.UnwindDegrees(45.0f));
Assert.AreEqual(90.0f, Mathf.UnwindDegrees(90.0f));
Assert.AreEqual(180.0f, Mathf.UnwindDegrees(180.0f));
Assert.AreEqual(0.0f, Mathf.UnwindDegrees(360.0f));
var fError = 0.001f;
var dError = 0.00001;
for (float f = -400.0f; f <= 400.0f; f += 0.1f)
{
var f1 = Mathf.UnwindDegreesAccurate(f);
var f2 = Mathf.UnwindDegrees(f);
if (Mathf.Abs(f1 - f2) >= fError)
throw new Exception($"Failed on angle={f}, {f1} != {f2}");
var d = (double)f;
var d1 = Mathd.UnwindDegreesAccurate(d);
var d2 = Mathd.UnwindDegrees(d);
if (Mathd.Abs(d1 - d2) >= dError)
throw new Exception($"Failed on angle={d}, {d1} != {d2}");
}
}
}
}
#endif

View File

@@ -675,6 +675,10 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
}
}
}
// For in-built structures try to convert it into internal format for better comparability with the scripting
value.Inline();
break;
}
// Unpack Structure

View File

@@ -246,6 +246,7 @@ public:
void ProcessGroupCollections(Box* box, Node* node, Value& value);
protected:
void InlineVariantStruct(Variant& v);
virtual Value eatBox(Node* caller, Box* box) = 0;
virtual Graph* GetCurrentGraph() const = 0;

View File

@@ -1203,6 +1203,8 @@ namespace Flax.Build.Bindings
contents.Append("ref ");
else if (parameterInfo.IsThis)
contents.Append("this ");
else if (parameterInfo.IsParams)
contents.Append("params ");
contents.Append(managedType);
contents.Append(' ');
contents.Append(parameterInfo.Name);
@@ -1263,6 +1265,8 @@ namespace Flax.Build.Bindings
contents.Append("ref ");
else if (parameterInfo.IsThis)
contents.Append("this ");
else if (parameterInfo.IsParams)
contents.Append("params ");
contents.Append(managedType);
contents.Append(' ');
contents.Append(parameterInfo.Name);
@@ -1988,6 +1992,8 @@ namespace Flax.Build.Bindings
contents.Append("ref ");
else if (parameterInfo.IsThis)
contents.Append("this ");
else if (parameterInfo.IsParams)
contents.Append("params ");
contents.Append(managedType);
contents.Append(' ');
contents.Append(parameterInfo.Name);
@@ -2053,7 +2059,7 @@ namespace Flax.Build.Bindings
if (code.StartsWith("using"))
CSharpUsedNamespaces.Add(code.Substring(6));
else if (code.Length > 0)
contents.Append(injectCodeInfo.Code).AppendLine(";");
contents.Append(code).AppendLine(";");
}
}
else

View File

@@ -19,7 +19,7 @@ namespace Flax.Build.Bindings
partial class BindingsGenerator
{
private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
private const int CacheVersion = 18;
private const int CacheVersion = 19;
internal static void Write(BinaryWriter writer, string e)
{

View File

@@ -337,6 +337,9 @@ namespace Flax.Build.Bindings
case "this":
currentParam.IsThis = true;
break;
case "params":
currentParam.IsParams = true;
break;
case "attributes":
currentParam.Attributes = tag.Value;
break;
@@ -847,6 +850,9 @@ namespace Flax.Build.Bindings
case "hidden":
desc.IsHidden = true;
break;
case "sealed":
desc.IsVirtual = false;
break;
case "tag":
ParseTag(ref desc.Tags, tag);
break;

View File

@@ -19,6 +19,7 @@ namespace Flax.Build.Bindings
public bool IsRef;
public bool IsOut;
public bool IsThis;
public bool IsParams;
public bool HasDefaultValue => !string.IsNullOrEmpty(DefaultValue);
@@ -37,6 +38,7 @@ namespace Flax.Build.Bindings
writer.Write(IsRef);
writer.Write(IsOut);
writer.Write(IsThis);
writer.Write(IsParams);
}
public void Read(BinaryReader reader)
@@ -49,6 +51,7 @@ namespace Flax.Build.Bindings
IsRef = reader.ReadBoolean();
IsOut = reader.ReadBoolean();
IsThis = reader.ReadBoolean();
IsParams = reader.ReadBoolean();
}
public override string ToString()