Merge branch 'master' of https://github.com/FlaxEngine/FlaxEngine into feature/1151-play-mode-actions
This commit is contained in:
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
@@ -3,7 +3,7 @@
|
|||||||
"Version": {
|
"Version": {
|
||||||
"Major": 1,
|
"Major": 1,
|
||||||
"Minor": 6,
|
"Minor": 6,
|
||||||
"Build": 6342
|
"Build": 6344
|
||||||
},
|
},
|
||||||
"Company": "Flax",
|
"Company": "Flax",
|
||||||
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace FlaxEditor.Content
|
|||||||
if (asset)
|
if (asset)
|
||||||
{
|
{
|
||||||
var source = Editor.GetShaderSourceCode(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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
using FlaxEditor.CustomEditors;
|
using FlaxEditor.CustomEditors;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine;
|
|
||||||
using DockState = FlaxEditor.GUI.Docking.DockState;
|
using DockState = FlaxEditor.GUI.Docking.DockState;
|
||||||
|
|
||||||
namespace FlaxEditor
|
namespace FlaxEditor
|
||||||
@@ -86,8 +85,12 @@ namespace FlaxEditor
|
|||||||
if (!FlaxEngine.Scripting.IsTypeFromGameScripts(type))
|
if (!FlaxEngine.Scripting.IsTypeFromGameScripts(type))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!Window.IsHidden)
|
||||||
|
{
|
||||||
Editor.Instance.Windows.AddToRestore(this);
|
Editor.Instance.Windows.AddToRestore(this);
|
||||||
|
}
|
||||||
Window.Close();
|
Window.Close();
|
||||||
|
Window.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
Presenter.Undo?.AddAction(new MultiUndoAction(actions));
|
Presenter.Undo?.AddAction(new MultiUndoAction(actions));
|
||||||
|
|
||||||
// Build ragdoll
|
// Build ragdoll
|
||||||
SceneGraph.Actors.AnimatedModelNode.BuildRagdoll(animatedModel, options, ragdoll);
|
AnimatedModelNode.BuildRagdoll(animatedModel, options, ragdoll);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRebuildBone(Button button)
|
private void OnRebuildBone(Button button)
|
||||||
@@ -191,7 +191,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build ragdoll
|
// Build ragdoll
|
||||||
SceneGraph.Actors.AnimatedModelNode.BuildRagdoll(animatedModel, new AnimatedModelNode.RebuildOptions(), ragdoll, name);
|
AnimatedModelNode.BuildRagdoll(animatedModel, new AnimatedModelNode.RebuildOptions(), ragdoll, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRemoveBone(Button button)
|
private void OnRemoveBone(Button button)
|
||||||
|
|||||||
@@ -33,7 +33,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
[CustomEditor(typeof(Asset)), DefaultEditor]
|
[CustomEditor(typeof(Asset)), DefaultEditor]
|
||||||
public class AssetRefEditor : CustomEditor
|
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;
|
private ScriptType _valueType;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -44,7 +49,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
if (HasDifferentTypes)
|
if (HasDifferentTypes)
|
||||||
return;
|
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]);
|
_valueType = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
|
||||||
var assetType = _valueType;
|
var assetType = _valueType;
|
||||||
@@ -66,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
// Generic file picker
|
// Generic file picker
|
||||||
assetType = ScriptType.Null;
|
assetType = ScriptType.Null;
|
||||||
_picker.FileExtension = assetReference.TypeName;
|
Picker.FileExtension = assetReference.TypeName;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -78,23 +83,25 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_picker.AssetType = assetType;
|
Picker.AssetType = assetType;
|
||||||
_picker.Height = height;
|
Picker.Height = height;
|
||||||
_picker.SelectedItemChanged += OnSelectedItemChanged;
|
Picker.SelectedItemChanged += OnSelectedItemChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelectedItemChanged()
|
private void OnSelectedItemChanged()
|
||||||
{
|
{
|
||||||
|
if (_isRefreshing)
|
||||||
|
return;
|
||||||
if (typeof(AssetItem).IsAssignableFrom(_valueType.Type))
|
if (typeof(AssetItem).IsAssignableFrom(_valueType.Type))
|
||||||
SetValue(_picker.SelectedItem);
|
SetValue(Picker.SelectedItem);
|
||||||
else if (_valueType.Type == typeof(Guid))
|
else if (_valueType.Type == typeof(Guid))
|
||||||
SetValue(_picker.SelectedID);
|
SetValue(Picker.SelectedID);
|
||||||
else if (_valueType.Type == typeof(SceneReference))
|
else if (_valueType.Type == typeof(SceneReference))
|
||||||
SetValue(new SceneReference(_picker.SelectedID));
|
SetValue(new SceneReference(Picker.SelectedID));
|
||||||
else if (_valueType.Type == typeof(string))
|
else if (_valueType.Type == typeof(string))
|
||||||
SetValue(_picker.SelectedPath);
|
SetValue(Picker.SelectedPath);
|
||||||
else
|
else
|
||||||
SetValue(_picker.SelectedAsset);
|
SetValue(Picker.SelectedAsset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -104,16 +111,18 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
if (!HasDifferentValues)
|
if (!HasDifferentValues)
|
||||||
{
|
{
|
||||||
|
_isRefreshing = true;
|
||||||
if (Values[0] is AssetItem assetItem)
|
if (Values[0] is AssetItem assetItem)
|
||||||
_picker.SelectedItem = assetItem;
|
Picker.SelectedItem = assetItem;
|
||||||
else if (Values[0] is Guid guid)
|
else if (Values[0] is Guid guid)
|
||||||
_picker.SelectedID = guid;
|
Picker.SelectedID = guid;
|
||||||
else if (Values[0] is SceneReference sceneAsset)
|
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)
|
else if (Values[0] is string path)
|
||||||
_picker.SelectedPath = path;
|
Picker.SelectedPath = path;
|
||||||
else
|
else
|
||||||
_picker.SelectedAsset = Values[0] as Asset;
|
Picker.SelectedAsset = Values[0] as Asset;
|
||||||
|
_isRefreshing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
element.CustomControl.Value = (Color)Values[0];
|
var value = Values[0];
|
||||||
|
if (value is Color asColor)
|
||||||
|
element.CustomControl.Value = asColor;
|
||||||
|
else if (value is Color32 asColor32)
|
||||||
|
element.CustomControl.Value = asColor32;
|
||||||
|
else if (value is Float4 asFloat4)
|
||||||
|
element.CustomControl.Value = asFloat4;
|
||||||
|
else if (value is Double4 asDouble4)
|
||||||
|
element.CustomControl.Value = (Float4)asDouble4;
|
||||||
|
else if (value is Vector4 asVector4)
|
||||||
|
element.CustomControl.Value = asVector4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using FlaxEditor.CustomEditors.Elements;
|
using FlaxEditor.CustomEditors.Elements;
|
||||||
|
using FlaxEditor.CustomEditors.GUI;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.CustomEditors.Editors
|
namespace FlaxEditor.CustomEditors.Editors
|
||||||
@@ -13,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
{
|
{
|
||||||
private GroupElement _group;
|
private GroupElement _group;
|
||||||
private bool _updateName;
|
private bool _updateName;
|
||||||
|
private int _entryIndex;
|
||||||
|
private bool _isRefreshing;
|
||||||
|
private MaterialBase _material;
|
||||||
|
private ModelInstanceActor _modelInstance;
|
||||||
|
private AssetRefEditor _materialEditor;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
@@ -21,47 +28,122 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
var group = layout.Group("Entry");
|
var group = layout.Group("Entry");
|
||||||
_group = group;
|
_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);
|
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 />
|
/// <inheritdoc />
|
||||||
public override void Refresh()
|
public override void Refresh()
|
||||||
{
|
{
|
||||||
|
// Update panel title to match material slot name
|
||||||
if (_updateName &&
|
if (_updateName &&
|
||||||
_group != null &&
|
_group != null &&
|
||||||
ParentEditor?.ParentEditor != null &&
|
ParentEditor?.ParentEditor != null &&
|
||||||
ParentEditor.ParentEditor.Values.Count > 0)
|
ParentEditor.ParentEditor.Values.Count > 0)
|
||||||
{
|
{
|
||||||
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
|
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;
|
var slots = modelInstance.MaterialSlots;
|
||||||
if (model && model.IsLoaded)
|
|
||||||
{
|
|
||||||
var slots = model.MaterialSlots;
|
|
||||||
if (slots != null && slots.Length > entryIndex)
|
if (slots != null && slots.Length > entryIndex)
|
||||||
{
|
{
|
||||||
_group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
|
|
||||||
_updateName = false;
|
_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;
|
_group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
|
||||||
_updateName = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh currently selected material
|
||||||
|
_material = _modelInstance.GetMaterial(_entryIndex);
|
||||||
|
|
||||||
base.Refresh();
|
base.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Deinitialize()
|
||||||
|
{
|
||||||
|
_material = null;
|
||||||
|
_modelInstance = null;
|
||||||
|
_materialEditor = null;
|
||||||
|
|
||||||
|
base.Deinitialize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -390,7 +390,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
if (addTagDropPanel.IsClosed)
|
if (addTagDropPanel.IsClosed)
|
||||||
{
|
{
|
||||||
Debug.Log("Hit");
|
|
||||||
nameTextBox.BorderColor = Color.Transparent;
|
nameTextBox.BorderColor = Color.Transparent;
|
||||||
nameTextBox.BorderSelectedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
|
nameTextBox.BorderSelectedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ public class Editor : EditorModule
|
|||||||
{
|
{
|
||||||
base.Setup(options);
|
base.Setup(options);
|
||||||
|
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.Private.Xml");
|
options.ScriptingAPI.SystemReferences.Add("System.Xml");
|
||||||
|
options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
|
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
|
||||||
|
|
||||||
@@ -101,5 +102,7 @@ public class Editor : EditorModule
|
|||||||
files.Add(Path.Combine(FolderPath, "Cooker/GameCooker.h"));
|
files.Add(Path.Combine(FolderPath, "Cooker/GameCooker.h"));
|
||||||
files.Add(Path.Combine(FolderPath, "Cooker/PlatformTools.h"));
|
files.Add(Path.Combine(FolderPath, "Cooker/PlatformTools.h"));
|
||||||
files.Add(Path.Combine(FolderPath, "Cooker/Steps/CookAssetsStep.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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,17 @@ namespace FlaxEditor.GUI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ScriptType Type => _type;
|
public ScriptType Type => _type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TypeItemView"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public TypeItemView()
|
||||||
|
{
|
||||||
|
_type = ScriptType.Null;
|
||||||
|
Name = "<null>";
|
||||||
|
TooltipText = "Unset value.";
|
||||||
|
Tag = _type;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TypeItemView"/> class.
|
/// Initializes a new instance of the <see cref="TypeItemView"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -83,6 +94,7 @@ namespace FlaxEditor.GUI
|
|||||||
|
|
||||||
// TODO: use async thread to search types without UI stall
|
// TODO: use async thread to search types without UI stall
|
||||||
var allTypes = Editor.Instance.CodeEditing.All.Get();
|
var allTypes = Editor.Instance.CodeEditing.All.Get();
|
||||||
|
AddItem(new TypeItemView());
|
||||||
for (int i = 0; i < allTypes.Count; i++)
|
for (int i = 0; i < allTypes.Count; i++)
|
||||||
{
|
{
|
||||||
var type = allTypes[i];
|
var type = allTypes[i];
|
||||||
|
|||||||
@@ -178,6 +178,8 @@ namespace FlaxEditor.Gizmo
|
|||||||
if (selection[i] is ActorNode actorNode && actorNode.Actor != null)
|
if (selection[i] is ActorNode actorNode && actorNode.Actor != null)
|
||||||
CollectActors(actorNode.Actor);
|
CollectActors(actorNode.Actor);
|
||||||
}
|
}
|
||||||
|
if (_actors.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
// Render selected objects depth
|
// Render selected objects depth
|
||||||
Renderer.DrawSceneDepth(context, task, customDepth, _actors);
|
Renderer.DrawSceneDepth(context, task, customDepth, _actors);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace FlaxEditor
|
|||||||
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
|
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
@@ -44,7 +44,7 @@ namespace FlaxEditor
|
|||||||
/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
|
/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
|
||||||
/// <param name="serializer">The calling serializer.</param>
|
/// <param name="serializer">The calling serializer.</param>
|
||||||
/// <returns>The object value.</returns>
|
/// <returns>The object value.</returns>
|
||||||
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (reader.TokenType == JsonToken.Null)
|
if (reader.TokenType == JsonToken.Null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public:
|
|||||||
{
|
{
|
||||||
Version = ::Version(1, 0);
|
Version = ::Version(1, 0);
|
||||||
DefaultSceneSpawn = Ray(Vector3::Zero, Vector3::Forward);
|
DefaultSceneSpawn = Ray(Vector3::Zero, Vector3::Forward);
|
||||||
|
DefaultScene = Guid::Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -613,6 +613,28 @@ bool ScriptsBuilderService::Init()
|
|||||||
const String targetOutput = Globals::ProjectFolder / TEXT("Binaries") / target / platform / architecture / configuration;
|
const String targetOutput = Globals::ProjectFolder / TEXT("Binaries") / target / platform / architecture / configuration;
|
||||||
Array<String> files;
|
Array<String> files;
|
||||||
FileSystem::DirectoryGetFiles(files, targetOutput, TEXT("*.HotReload.*"), DirectorySearchOption::TopDirectoryOnly);
|
FileSystem::DirectoryGetFiles(files, targetOutput, TEXT("*.HotReload.*"), DirectorySearchOption::TopDirectoryOnly);
|
||||||
|
|
||||||
|
for (const auto& reference : Editor::Project->References)
|
||||||
|
{
|
||||||
|
if (reference.Project->Name == TEXT("Flax"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String referenceTarget;
|
||||||
|
if (reference.Project->EditorTarget.HasChars())
|
||||||
|
{
|
||||||
|
referenceTarget = reference.Project->EditorTarget.Get();
|
||||||
|
}
|
||||||
|
else if (reference.Project->GameTarget.HasChars())
|
||||||
|
{
|
||||||
|
referenceTarget = reference.Project->GameTarget.Get();
|
||||||
|
}
|
||||||
|
if (referenceTarget.IsEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const String referenceTargetOutput = reference.Project->ProjectFolderPath / TEXT("Binaries") / referenceTarget / platform / architecture / configuration;
|
||||||
|
FileSystem::DirectoryGetFiles(files, referenceTargetOutput, TEXT("*.HotReload.*"), DirectorySearchOption::TopDirectoryOnly);
|
||||||
|
}
|
||||||
|
|
||||||
if (files.HasItems())
|
if (files.HasItems())
|
||||||
LOG(Info, "Removing {0} files from previous Editor run hot-reloads", files.Count());
|
LOG(Info, "Removing {0} files from previous Editor run hot-reloads", files.Count());
|
||||||
for (auto& file : files)
|
for (auto& file : files)
|
||||||
|
|||||||
@@ -363,7 +363,6 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
Profiler.BeginEvent("VisjectCM.RemoveGroup");
|
Profiler.BeginEvent("VisjectCM.RemoveGroup");
|
||||||
if (group.Archetypes.Count == 0)
|
if (group.Archetypes.Count == 0)
|
||||||
{
|
{
|
||||||
Debug.Log("Remove");
|
|
||||||
_groups.RemoveAt(i);
|
_groups.RemoveAt(i);
|
||||||
group.Dispose();
|
group.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -292,7 +292,6 @@ void FoliageTools::Paint(Foliage* foliage, Span<int32> foliageTypesIndices, cons
|
|||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Place Instances");
|
PROFILE_CPU_NAMED("Place Instances");
|
||||||
|
|
||||||
Matrix matrix;
|
|
||||||
FoliageInstance instance;
|
FoliageInstance instance;
|
||||||
Quaternion tmp;
|
Quaternion tmp;
|
||||||
Matrix world;
|
Matrix world;
|
||||||
|
|||||||
@@ -1538,6 +1538,7 @@ namespace FlaxEditor.Viewport
|
|||||||
new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur"),
|
new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur"),
|
||||||
new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows"),
|
new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows"),
|
||||||
new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug"),
|
new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug"),
|
||||||
|
new ViewFlagOptions(ViewFlags.LightsDebug, "Lights Debug"),
|
||||||
new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw"),
|
new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEditor.GUI.Input;
|
|
||||||
using FlaxEngine.GUI;
|
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Previews
|
namespace FlaxEditor.Viewport.Previews
|
||||||
@@ -15,11 +13,10 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
/// <seealso cref="AssetPreview" />
|
/// <seealso cref="AssetPreview" />
|
||||||
public class AnimatedModelPreview : AssetPreview
|
public class AnimatedModelPreview : AssetPreview
|
||||||
{
|
{
|
||||||
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
|
|
||||||
private bool _showNodes, _showBounds, _showFloor, _showCurrentLOD, _showNodesNames;
|
|
||||||
private AnimatedModel _previewModel;
|
private AnimatedModel _previewModel;
|
||||||
|
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
|
||||||
|
private bool _showNodes, _showBounds, _showFloor, _showNodesNames;
|
||||||
private StaticModel _floorModel;
|
private StaticModel _floorModel;
|
||||||
private ContextMenuButton _showCurrentLODButton;
|
|
||||||
private bool _playAnimation, _playAnimationOnce;
|
private bool _playAnimation, _playAnimationOnce;
|
||||||
private float _playSpeed = 1.0f;
|
private float _playSpeed = 1.0f;
|
||||||
|
|
||||||
@@ -188,8 +185,8 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
{
|
{
|
||||||
UseTimeScale = false,
|
UseTimeScale = false,
|
||||||
UpdateWhenOffscreen = true,
|
UpdateWhenOffscreen = true,
|
||||||
//_previewModel.BoundsScale = 1000.0f;
|
BoundsScale = 100.0f,
|
||||||
UpdateMode = AnimatedModel.AnimationUpdateMode.Manual
|
UpdateMode = AnimatedModel.AnimationUpdateMode.Manual,
|
||||||
};
|
};
|
||||||
Task.AddCustomActor(_previewModel);
|
Task.AddCustomActor(_previewModel);
|
||||||
|
|
||||||
@@ -207,26 +204,6 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
// Show Floor
|
// Show Floor
|
||||||
_showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor);
|
_showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor);
|
||||||
_showFloorButton.IndexInParent = 1;
|
_showFloorButton.IndexInParent = 1;
|
||||||
|
|
||||||
// Show Current LOD
|
|
||||||
_showCurrentLODButton = ViewWidgetShowMenu.AddButton("Current LOD", button =>
|
|
||||||
{
|
|
||||||
_showCurrentLOD = !_showCurrentLOD;
|
|
||||||
_showCurrentLODButton.Icon = _showCurrentLOD ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
|
||||||
});
|
|
||||||
_showCurrentLODButton.IndexInParent = 2;
|
|
||||||
|
|
||||||
// Preview LOD
|
|
||||||
{
|
|
||||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
|
||||||
previewLOD.CloseMenuOnClick = false;
|
|
||||||
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
|
|
||||||
{
|
|
||||||
Parent = previewLOD
|
|
||||||
};
|
|
||||||
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
|
|
||||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable shadows
|
// Enable shadows
|
||||||
@@ -339,44 +316,6 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
_previewModel.ResetAnimation();
|
_previewModel.ResetAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ComputeLODIndex(SkinnedModel model)
|
|
||||||
{
|
|
||||||
if (PreviewActor.ForcedLOD != -1)
|
|
||||||
return PreviewActor.ForcedLOD;
|
|
||||||
|
|
||||||
// Based on RenderTools::ComputeModelLOD
|
|
||||||
CreateProjectionMatrix(out var projectionMatrix);
|
|
||||||
float screenMultiple = 0.5f * Mathf.Max(projectionMatrix.M11, projectionMatrix.M22);
|
|
||||||
var sphere = PreviewActor.Sphere;
|
|
||||||
var viewOrigin = ViewPosition;
|
|
||||||
var distSqr = Vector3.DistanceSquared(ref sphere.Center, ref viewOrigin);
|
|
||||||
var screenRadiusSquared = Mathf.Square(screenMultiple * sphere.Radius) / Mathf.Max(1.0f, distSqr);
|
|
||||||
|
|
||||||
// Check if model is being culled
|
|
||||||
if (Mathf.Square(model.MinScreenSize * 0.5f) > screenRadiusSquared)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Skip if no need to calculate LOD
|
|
||||||
if (model.LoadedLODs == 0)
|
|
||||||
return -1;
|
|
||||||
var lods = model.LODs;
|
|
||||||
if (lods.Length == 0)
|
|
||||||
return -1;
|
|
||||||
if (lods.Length == 1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Iterate backwards and return the first matching LOD
|
|
||||||
for (int lodIndex = lods.Length - 1; lodIndex >= 0; lodIndex--)
|
|
||||||
{
|
|
||||||
if (Mathf.Square(lods[lodIndex].ScreenSize * 0.5f) >= screenRadiusSquared)
|
|
||||||
{
|
|
||||||
return lodIndex + PreviewActor.LODBias;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext)
|
protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext)
|
||||||
{
|
{
|
||||||
@@ -440,45 +379,6 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Draw()
|
|
||||||
{
|
|
||||||
base.Draw();
|
|
||||||
|
|
||||||
var skinnedModel = _previewModel.SkinnedModel;
|
|
||||||
if (skinnedModel == null || !skinnedModel.IsLoaded)
|
|
||||||
return;
|
|
||||||
var lods = skinnedModel.LODs;
|
|
||||||
if (lods.Length == 0)
|
|
||||||
{
|
|
||||||
// Force show skeleton for models without geometry
|
|
||||||
ShowNodes = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_showCurrentLOD)
|
|
||||||
{
|
|
||||||
var lodIndex = ComputeLODIndex(skinnedModel);
|
|
||||||
string text = string.Format("Current LOD: {0}", lodIndex);
|
|
||||||
if (lodIndex != -1)
|
|
||||||
{
|
|
||||||
lodIndex = Mathf.Clamp(lodIndex + PreviewActor.LODBias, 0, lods.Length - 1);
|
|
||||||
var lod = lods[lodIndex];
|
|
||||||
int triangleCount = 0, vertexCount = 0;
|
|
||||||
for (int meshIndex = 0; meshIndex < lod.Meshes.Length; meshIndex++)
|
|
||||||
{
|
|
||||||
var mesh = lod.Meshes[meshIndex];
|
|
||||||
triangleCount += mesh.TriangleCount;
|
|
||||||
vertexCount += mesh.VertexCount;
|
|
||||||
}
|
|
||||||
text += string.Format("\nTriangles: {0:N0}\nVertices: {1:N0}", triangleCount, vertexCount);
|
|
||||||
}
|
|
||||||
var font = Style.Current.FontMedium;
|
|
||||||
var pos = new Float2(10, 50);
|
|
||||||
Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black);
|
|
||||||
Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Update(float deltaTime)
|
public override void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
@@ -498,14 +398,21 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the camera to focus on a object.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetCamera()
|
||||||
|
{
|
||||||
|
ViewportCamera.SetArcBallView(_previewModel.Box);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnKeyDown(KeyboardKeys key)
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
{
|
{
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case KeyboardKeys.F:
|
case KeyboardKeys.F:
|
||||||
// Pay respect..
|
ResetCamera();
|
||||||
ViewportCamera.SetArcBallView(_previewModel.Box);
|
|
||||||
return true;
|
return true;
|
||||||
case KeyboardKeys.Spacebar:
|
case KeyboardKeys.Spacebar:
|
||||||
PlayAnimation = !PlayAnimation;
|
PlayAnimation = !PlayAnimation;
|
||||||
@@ -525,7 +432,6 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
_showNodesButton = null;
|
_showNodesButton = null;
|
||||||
_showBoundsButton = null;
|
_showBoundsButton = null;
|
||||||
_showFloorButton = null;
|
_showFloorButton = null;
|
||||||
_showCurrentLODButton = null;
|
|
||||||
_showNodesNamesButton = null;
|
_showNodesNamesButton = null;
|
||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using System;
|
|||||||
using FlaxEditor.Surface;
|
using FlaxEditor.Surface;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEditor.Viewport.Widgets;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Previews
|
namespace FlaxEditor.Viewport.Previews
|
||||||
@@ -46,6 +48,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
private int _selectedModelIndex;
|
private int _selectedModelIndex;
|
||||||
private Image _guiMaterialControl;
|
private Image _guiMaterialControl;
|
||||||
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
|
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
|
||||||
|
private ContextMenu _modelWidgetButtonMenu;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the material asset to preview. It can be <see cref="FlaxEngine.Material"/> or <see cref="FlaxEngine.MaterialInstance"/>.
|
/// Gets or sets the material asset to preview. It can be <see cref="FlaxEngine.Material"/> or <see cref="FlaxEngine.MaterialInstance"/>.
|
||||||
@@ -95,20 +98,33 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
Task.AddCustomActor(_previewModel);
|
Task.AddCustomActor(_previewModel);
|
||||||
|
|
||||||
// Create context menu for primitive switching
|
// Create context menu for primitive switching
|
||||||
if (useWidgets && ViewWidgetButtonMenu != null)
|
if (useWidgets)
|
||||||
{
|
{
|
||||||
ViewWidgetButtonMenu.AddSeparator();
|
// Model mode widget
|
||||||
var modelSelect = ViewWidgetButtonMenu.AddChildMenu("Model").ContextMenu;
|
var modelMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
_modelWidgetButtonMenu = new ContextMenu();
|
||||||
|
_modelWidgetButtonMenu.VisibleChanged += control =>
|
||||||
|
{
|
||||||
|
if (!control.Visible)
|
||||||
|
return;
|
||||||
|
_modelWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||||
|
|
||||||
// Fill out all models
|
// Fill out all models
|
||||||
for (int i = 0; i < Models.Length; i++)
|
for (int i = 0; i < Models.Length; i++)
|
||||||
{
|
{
|
||||||
var button = modelSelect.AddButton(Models[i]);
|
var index = i;
|
||||||
button.Tag = i;
|
var button = _modelWidgetButtonMenu.AddButton(Models[index]);
|
||||||
|
button.ButtonClicked += _ => SelectedModelIndex = index;
|
||||||
|
button.Checked = SelectedModelIndex == index;
|
||||||
|
button.Tag = index;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
// Link the action
|
new ViewportWidgetButton("Model", SpriteHandle.Invalid, _modelWidgetButtonMenu)
|
||||||
modelSelect.ButtonClicked += (button) => SelectedModelIndex = (int)button.Tag;
|
{
|
||||||
|
TooltipText = "Change material model",
|
||||||
|
Parent = modelMode,
|
||||||
|
};
|
||||||
|
modelMode.Parent = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using FlaxEditor.GUI.Input;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
@@ -56,25 +55,14 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
// Link actors for rendering
|
// Link actors for rendering
|
||||||
Task.AddCustomActor(StaticModel);
|
Task.AddCustomActor(StaticModel);
|
||||||
Task.AddCustomActor(AnimatedModel);
|
Task.AddCustomActor(AnimatedModel);
|
||||||
|
}
|
||||||
|
|
||||||
if (useWidgets)
|
/// <summary>
|
||||||
|
/// Resets the camera to focus on a object.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetCamera()
|
||||||
{
|
{
|
||||||
// Preview LOD
|
ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
|
||||||
{
|
|
||||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
|
||||||
previewLOD.CloseMenuOnClick = false;
|
|
||||||
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
|
|
||||||
{
|
|
||||||
Parent = previewLOD
|
|
||||||
};
|
|
||||||
previewLODValue.ValueChanged += () =>
|
|
||||||
{
|
|
||||||
StaticModel.ForcedLOD = previewLODValue.Value;
|
|
||||||
AnimatedModel.ForcedLOD = previewLODValue.Value;
|
|
||||||
};
|
|
||||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = StaticModel.ForcedLOD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBegin(RenderTask task, GPUContext context)
|
private void OnBegin(RenderTask task, GPUContext context)
|
||||||
@@ -103,8 +91,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case KeyboardKeys.F:
|
case KeyboardKeys.F:
|
||||||
// Pay respect..
|
ResetCamera();
|
||||||
ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return base.OnKeyDown(key);
|
return base.OnKeyDown(key);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.GUI.Input;
|
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
|
using FlaxEditor.Viewport.Widgets;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport.Previews
|
namespace FlaxEditor.Viewport.Previews
|
||||||
@@ -16,10 +16,27 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
public class ModelPreview : AssetPreview
|
public class ModelPreview : AssetPreview
|
||||||
{
|
{
|
||||||
private ContextMenuButton _showBoundsButton, _showCurrentLODButton, _showNormalsButton, _showTangentsButton, _showBitangentsButton, _showFloorButton;
|
private ContextMenuButton _showBoundsButton, _showCurrentLODButton, _showNormalsButton, _showTangentsButton, _showBitangentsButton, _showFloorButton;
|
||||||
|
private ContextMenu _previewLODsWidgetButtonMenu;
|
||||||
private StaticModel _previewModel, _floorModel;
|
private StaticModel _previewModel, _floorModel;
|
||||||
private bool _showBounds, _showCurrentLOD, _showNormals, _showTangents, _showBitangents, _showFloor;
|
private bool _showBounds, _showCurrentLOD, _showNormals, _showTangents, _showBitangents, _showFloor;
|
||||||
private MeshDataCache _meshDatas;
|
private MeshDataCache _meshDatas;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that shows LOD statistics
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowCurrentLOD
|
||||||
|
{
|
||||||
|
get => _showCurrentLOD;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showCurrentLOD == value)
|
||||||
|
return;
|
||||||
|
_showCurrentLOD = value;
|
||||||
|
if (_showCurrentLODButton != null)
|
||||||
|
_showCurrentLODButton.Checked = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the model asset to preview.
|
/// Gets or sets the model asset to preview.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -198,18 +215,37 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
});
|
});
|
||||||
_showCurrentLODButton.IndexInParent = 2;
|
_showCurrentLODButton.IndexInParent = 2;
|
||||||
|
|
||||||
// Preview LOD
|
// Preview LODs mode widget
|
||||||
|
var PreviewLODsMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
_previewLODsWidgetButtonMenu = new ContextMenu();
|
||||||
|
_previewLODsWidgetButtonMenu.VisibleChanged += control =>
|
||||||
{
|
{
|
||||||
var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD");
|
if (!control.Visible)
|
||||||
previewLOD.CloseMenuOnClick = false;
|
return;
|
||||||
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
|
var model = _previewModel.Model;
|
||||||
|
if (model && !model.WaitForLoaded())
|
||||||
{
|
{
|
||||||
Parent = previewLOD
|
_previewLODsWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||||
};
|
var lods = model.LODs.Length;
|
||||||
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
|
for (int i = -1; i < lods; i++)
|
||||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;
|
{
|
||||||
|
var index = i;
|
||||||
|
var button = _previewLODsWidgetButtonMenu.AddButton("LOD " + (index == -1 ? "Auto" : index));
|
||||||
|
button.ButtonClicked += _ => _previewModel.ForcedLOD = index;
|
||||||
|
button.Checked = _previewModel.ForcedLOD == index;
|
||||||
|
button.Tag = index;
|
||||||
|
if (lods <= 1)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
new ViewportWidgetButton("Preview LOD", SpriteHandle.Invalid, _previewLODsWidgetButtonMenu)
|
||||||
|
{
|
||||||
|
TooltipText = "Preview LOD properties",
|
||||||
|
Parent = PreviewLODsMode,
|
||||||
|
};
|
||||||
|
PreviewLODsMode.Parent = this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBegin(RenderTask task, GPUContext context)
|
private void OnBegin(RenderTask task, GPUContext context)
|
||||||
@@ -347,7 +383,10 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
{
|
{
|
||||||
var asset = Model;
|
var asset = Model;
|
||||||
var lodIndex = ComputeLODIndex(asset, out var screenSize);
|
var lodIndex = ComputeLODIndex(asset, out var screenSize);
|
||||||
string text = string.Format("Current LOD: {0}\nScreen Size: {1:F2}", lodIndex, screenSize);
|
var auto = _previewModel.ForcedLOD == -1;
|
||||||
|
string text = auto ? "LOD Automatic" : "";
|
||||||
|
text += auto ? string.Format("\nScreen Size: {0:F2}", screenSize) : "";
|
||||||
|
text += string.Format("\nCurrent LOD: {0}", lodIndex);
|
||||||
if (lodIndex != -1)
|
if (lodIndex != -1)
|
||||||
{
|
{
|
||||||
var lods = asset.LODs;
|
var lods = asset.LODs;
|
||||||
@@ -369,14 +408,21 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the camera to focus on a object.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetCamera()
|
||||||
|
{
|
||||||
|
ViewportCamera.SetArcBallView(_previewModel.Box);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnKeyDown(KeyboardKeys key)
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
{
|
{
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case KeyboardKeys.F:
|
case KeyboardKeys.F:
|
||||||
// Pay respect..
|
ResetCamera();
|
||||||
ViewportCamera.SetArcBallView(_previewModel.Box);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return base.OnKeyDown(key);
|
return base.OnKeyDown(key);
|
||||||
@@ -389,6 +435,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
Object.Destroy(ref _previewModel);
|
Object.Destroy(ref _previewModel);
|
||||||
_showBoundsButton = null;
|
_showBoundsButton = null;
|
||||||
_showCurrentLODButton = null;
|
_showCurrentLODButton = null;
|
||||||
|
_previewLODsWidgetButtonMenu = null;
|
||||||
_showNormalsButton = null;
|
_showNormalsButton = null;
|
||||||
_showTangentsButton = null;
|
_showTangentsButton = null;
|
||||||
_showBitangentsButton = null;
|
_showBitangentsButton = null;
|
||||||
|
|||||||
177
Source/Editor/Viewport/Previews/SkinnedModelPreview.cs
Normal file
177
Source/Editor/Viewport/Previews/SkinnedModelPreview.cs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEditor.Viewport.Widgets;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Viewport.Previews
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Animation asset preview editor viewport.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="AnimatedModelPreview" />
|
||||||
|
public class SkinnedModelPreview : AnimatedModelPreview
|
||||||
|
{
|
||||||
|
private bool _showCurrentLOD;
|
||||||
|
private ContextMenuButton _showCurrentLODButton;
|
||||||
|
private ContextMenu _previewLODsWidgetButtonMenu;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that shows LOD statistics
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowCurrentLOD
|
||||||
|
{
|
||||||
|
get => _showCurrentLOD;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showCurrentLOD == value)
|
||||||
|
return;
|
||||||
|
_showCurrentLOD = value;
|
||||||
|
if (_showCurrentLODButton != null)
|
||||||
|
_showCurrentLODButton.Checked = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="SkinnedModelPreview"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="useWidgets">if set to <c>true</c> use widgets.</param>
|
||||||
|
public SkinnedModelPreview(bool useWidgets)
|
||||||
|
: base(useWidgets)
|
||||||
|
{
|
||||||
|
if (useWidgets)
|
||||||
|
{
|
||||||
|
// Show Current LOD
|
||||||
|
_showCurrentLODButton = ViewWidgetShowMenu.AddButton("Current LOD", button =>
|
||||||
|
{
|
||||||
|
_showCurrentLOD = !_showCurrentLOD;
|
||||||
|
_showCurrentLODButton.Icon = _showCurrentLOD ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
|
});
|
||||||
|
_showCurrentLODButton.IndexInParent = 2;
|
||||||
|
|
||||||
|
// PreviewLODS mode widget
|
||||||
|
var PreviewLODSMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||||
|
_previewLODsWidgetButtonMenu = new ContextMenu();
|
||||||
|
_previewLODsWidgetButtonMenu.VisibleChanged += control =>
|
||||||
|
{
|
||||||
|
if (!control.Visible)
|
||||||
|
return;
|
||||||
|
var skinned = PreviewActor.SkinnedModel;
|
||||||
|
if (skinned && !skinned.WaitForLoaded())
|
||||||
|
{
|
||||||
|
_previewLODsWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||||
|
var lods = skinned.LODs.Length;
|
||||||
|
for (int i = -1; i < lods; i++)
|
||||||
|
{
|
||||||
|
var index = i;
|
||||||
|
var button = _previewLODsWidgetButtonMenu.AddButton("LOD " + (index == -1 ? "Auto" : index));
|
||||||
|
button.ButtonClicked += (button) => PreviewActor.ForcedLOD = index;
|
||||||
|
button.Checked = PreviewActor.ForcedLOD == index;
|
||||||
|
button.Tag = index;
|
||||||
|
if (lods <= 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
new ViewportWidgetButton("Preview LOD", SpriteHandle.Invalid, _previewLODsWidgetButtonMenu)
|
||||||
|
{
|
||||||
|
TooltipText = "Preview LOD properties",
|
||||||
|
Parent = PreviewLODSMode,
|
||||||
|
};
|
||||||
|
PreviewLODSMode.Parent = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ComputeLODIndex(SkinnedModel model, out float screenSize)
|
||||||
|
{
|
||||||
|
screenSize = 1.0f;
|
||||||
|
if (PreviewActor.ForcedLOD != -1)
|
||||||
|
return PreviewActor.ForcedLOD;
|
||||||
|
|
||||||
|
// Based on RenderTools::ComputeModelLOD
|
||||||
|
CreateProjectionMatrix(out var projectionMatrix);
|
||||||
|
float screenMultiple = 0.5f * Mathf.Max(projectionMatrix.M11, projectionMatrix.M22);
|
||||||
|
var sphere = PreviewActor.Sphere;
|
||||||
|
var viewOrigin = ViewPosition;
|
||||||
|
var distSqr = Vector3.DistanceSquared(ref sphere.Center, ref viewOrigin);
|
||||||
|
var screenRadiusSquared = Mathf.Square(screenMultiple * sphere.Radius) / Mathf.Max(1.0f, distSqr);
|
||||||
|
screenSize = Mathf.Sqrt((float)screenRadiusSquared) * 2.0f;
|
||||||
|
|
||||||
|
// Check if model is being culled
|
||||||
|
if (Mathf.Square(model.MinScreenSize * 0.5f) > screenRadiusSquared)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Skip if no need to calculate LOD
|
||||||
|
if (model.LoadedLODs == 0)
|
||||||
|
return -1;
|
||||||
|
var lods = model.LODs;
|
||||||
|
if (lods.Length == 0)
|
||||||
|
return -1;
|
||||||
|
if (lods.Length == 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Iterate backwards and return the first matching LOD
|
||||||
|
for (int lodIndex = lods.Length - 1; lodIndex >= 0; lodIndex--)
|
||||||
|
{
|
||||||
|
if (Mathf.Square(lods[lodIndex].ScreenSize * 0.5f) >= screenRadiusSquared)
|
||||||
|
{
|
||||||
|
return lodIndex + PreviewActor.LODBias;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
base.Draw();
|
||||||
|
|
||||||
|
var skinnedModel = PreviewActor.SkinnedModel;
|
||||||
|
if (skinnedModel == null || !skinnedModel.IsLoaded)
|
||||||
|
return;
|
||||||
|
var lods = skinnedModel.LODs;
|
||||||
|
if (lods.Length == 0)
|
||||||
|
{
|
||||||
|
// Force show skeleton for models without geometry
|
||||||
|
ShowNodes = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_showCurrentLOD)
|
||||||
|
{
|
||||||
|
var lodIndex = ComputeLODIndex(skinnedModel, out var screenSize);
|
||||||
|
var auto = PreviewActor.ForcedLOD == -1;
|
||||||
|
string text = auto ? "LOD Automatic" : "";
|
||||||
|
text += auto ? string.Format("\nScreen Size: {0:F2}", screenSize) : "";
|
||||||
|
text += string.Format("\nCurrent LOD: {0}", lodIndex);
|
||||||
|
if (lodIndex != -1)
|
||||||
|
{
|
||||||
|
lodIndex = Mathf.Clamp(lodIndex + PreviewActor.LODBias, 0, lods.Length - 1);
|
||||||
|
var lod = lods[lodIndex];
|
||||||
|
int triangleCount = 0, vertexCount = 0;
|
||||||
|
for (int meshIndex = 0; meshIndex < lod.Meshes.Length; meshIndex++)
|
||||||
|
{
|
||||||
|
var mesh = lod.Meshes[meshIndex];
|
||||||
|
triangleCount += mesh.TriangleCount;
|
||||||
|
vertexCount += mesh.VertexCount;
|
||||||
|
}
|
||||||
|
text += string.Format("\nTriangles: {0:N0}\nVertices: {1:N0}", triangleCount, vertexCount);
|
||||||
|
}
|
||||||
|
var font = Style.Current.FontMedium;
|
||||||
|
var pos = new Float2(10, 50);
|
||||||
|
Render2D.DrawText(font, text, new Rectangle(pos + Float2.One, Size), Color.Black);
|
||||||
|
Render2D.DrawText(font, text, new Rectangle(pos, Size), Color.White);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
_showCurrentLODButton = null;
|
||||||
|
_previewLODsWidgetButtonMenu = null;
|
||||||
|
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -182,6 +182,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
{
|
{
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
|
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole collision");
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/physics/colliders/collision-data.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/physics/colliders/collision-data.html")).LinkTooltip("See documentation to learn more");
|
||||||
|
|
||||||
// Split Panel
|
// Split Panel
|
||||||
|
|||||||
@@ -779,6 +779,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
private MeshDataCache _meshData;
|
private MeshDataCache _meshData;
|
||||||
private ModelImportSettings _importSettings = new ModelImportSettings();
|
private ModelImportSettings _importSettings = new ModelImportSettings();
|
||||||
private float _backfacesThreshold = 0.6f;
|
private float _backfacesThreshold = 0.6f;
|
||||||
|
private ToolStripButton _showCurrentLODButton;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ModelWindow(Editor editor, AssetItem item)
|
public ModelWindow(Editor editor, AssetItem item)
|
||||||
@@ -786,6 +787,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
{
|
{
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
|
_showCurrentLODButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Info64, () => _preview.ShowCurrentLOD = !_preview.ShowCurrentLOD).LinkTooltip("Show LOD statistics");
|
||||||
|
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole model");
|
||||||
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/models/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/models/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
|
|
||||||
// Model preview
|
// Model preview
|
||||||
@@ -869,6 +873,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showCurrentLODButton.Checked = _preview.ShowCurrentLOD;
|
||||||
|
|
||||||
base.Update(deltaTime);
|
base.Update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,6 +952,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
|
|
||||||
Object.Destroy(ref _highlightActor);
|
Object.Destroy(ref _highlightActor);
|
||||||
|
_showCurrentLODButton = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
||||||
public sealed class SkinnedModelWindow : ModelBaseWindow<SkinnedModel, SkinnedModelWindow>
|
public sealed class SkinnedModelWindow : ModelBaseWindow<SkinnedModel, SkinnedModelWindow>
|
||||||
{
|
{
|
||||||
private sealed class Preview : AnimatedModelPreview
|
private sealed class Preview : SkinnedModelPreview
|
||||||
{
|
{
|
||||||
private readonly SkinnedModelWindow _window;
|
private readonly SkinnedModelWindow _window;
|
||||||
|
|
||||||
@@ -1105,6 +1105,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
private Preview _preview;
|
private Preview _preview;
|
||||||
private AnimatedModel _highlightActor;
|
private AnimatedModel _highlightActor;
|
||||||
private ToolStripButton _showNodesButton;
|
private ToolStripButton _showNodesButton;
|
||||||
|
private ToolStripButton _showCurrentLODButton;
|
||||||
|
|
||||||
private MeshData[][] _meshDatas;
|
private MeshData[][] _meshDatas;
|
||||||
private bool _meshDatasInProgress;
|
private bool _meshDatasInProgress;
|
||||||
@@ -1116,7 +1117,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
{
|
{
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
|
_showCurrentLODButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Info64, () => _preview.ShowCurrentLOD = !_preview.ShowCurrentLOD).LinkTooltip("Show LOD statistics");
|
||||||
_showNodesButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Bone64, () => _preview.ShowNodes = !_preview.ShowNodes).LinkTooltip("Show animated model nodes debug view");
|
_showNodesButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Bone64, () => _preview.ShowNodes = !_preview.ShowNodes).LinkTooltip("Show animated model nodes debug view");
|
||||||
|
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole model");
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/skinned-model/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/skinned-model/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
|
|
||||||
@@ -1265,6 +1268,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showCurrentLODButton.Checked = _preview.ShowCurrentLOD;
|
||||||
_showNodesButton.Checked = _preview.ShowNodes;
|
_showNodesButton.Checked = _preview.ShowNodes;
|
||||||
|
|
||||||
base.Update(deltaTime);
|
base.Update(deltaTime);
|
||||||
@@ -1349,6 +1353,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
Object.Destroy(ref _highlightActor);
|
Object.Destroy(ref _highlightActor);
|
||||||
_preview = null;
|
_preview = null;
|
||||||
_showNodesButton = null;
|
_showNodesButton = null;
|
||||||
|
_showCurrentLODButton = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ namespace FlaxEditor.Windows
|
|||||||
public LogGroup Group;
|
public LogGroup Group;
|
||||||
public LogEntryDescription Desc;
|
public LogEntryDescription Desc;
|
||||||
public SpriteHandle Icon;
|
public SpriteHandle Icon;
|
||||||
|
public int LogCount = 1;
|
||||||
|
|
||||||
public LogEntry(DebugLogWindow window, ref LogEntryDescription desc)
|
public LogEntry(DebugLogWindow window, ref LogEntryDescription desc)
|
||||||
: base(0, 0, 120, DefaultHeight)
|
: base(0, 0, 120, DefaultHeight)
|
||||||
@@ -137,7 +138,14 @@ namespace FlaxEditor.Windows
|
|||||||
// Title
|
// Title
|
||||||
var textRect = new Rectangle(38, 2, clientRect.Width - 40, clientRect.Height - 10);
|
var textRect = new Rectangle(38, 2, clientRect.Width - 40, clientRect.Height - 10);
|
||||||
Render2D.PushClip(ref clientRect);
|
Render2D.PushClip(ref clientRect);
|
||||||
|
if (LogCount == 1)
|
||||||
|
{
|
||||||
Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground);
|
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();
|
Render2D.PopClip();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +297,7 @@ namespace FlaxEditor.Windows
|
|||||||
private readonly List<LogEntry> _pendingEntries = new List<LogEntry>(32);
|
private readonly List<LogEntry> _pendingEntries = new List<LogEntry>(32);
|
||||||
|
|
||||||
private readonly ToolStripButton _clearOnPlayButton;
|
private readonly ToolStripButton _clearOnPlayButton;
|
||||||
|
private readonly ToolStripButton _collapseLogsButton;
|
||||||
private readonly ToolStripButton _pauseOnErrorButton;
|
private readonly ToolStripButton _pauseOnErrorButton;
|
||||||
private readonly ToolStripButton[] _groupButtons = new ToolStripButton[3];
|
private readonly ToolStripButton[] _groupButtons = new ToolStripButton[3];
|
||||||
|
|
||||||
@@ -316,6 +325,7 @@ namespace FlaxEditor.Windows
|
|||||||
};
|
};
|
||||||
toolstrip.AddButton("Clear", Clear).LinkTooltip("Clears all log entries");
|
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");
|
_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");
|
_pauseOnErrorButton = (ToolStripButton)toolstrip.AddButton("Pause on Error").SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
|
||||||
toolstrip.AddSeparator();
|
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");
|
_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;
|
var top = _entriesPanel.Children.Count != 0 ? _entriesPanel.Children[_entriesPanel.Children.Count - 1].Bottom + spacing : margin.Top;
|
||||||
for (int i = 0; i < _pendingEntries.Count; i++)
|
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 = _pendingEntries[i];
|
||||||
newEntry.Visible = _groupButtons[(int)newEntry.Group].Checked;
|
newEntry.Visible = _groupButtons[(int)newEntry.Group].Checked;
|
||||||
anyVisible |= newEntry.Visible;
|
anyVisible |= newEntry.Visible;
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
|||||||
const float animPrevPos = GetAnimSamplePos(length, anim, prevPos, speed);
|
const float animPrevPos = GetAnimSamplePos(length, anim, prevPos, speed);
|
||||||
|
|
||||||
// Evaluate nested animations
|
// Evaluate nested animations
|
||||||
|
bool hasNested = false;
|
||||||
if (anim->NestedAnims.Count() != 0)
|
if (anim->NestedAnims.Count() != 0)
|
||||||
{
|
{
|
||||||
for (auto& e : anim->NestedAnims)
|
for (auto& e : anim->NestedAnims)
|
||||||
@@ -239,6 +240,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
|||||||
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
|
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
|
||||||
|
|
||||||
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode);
|
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode);
|
||||||
|
hasNested = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,7 +293,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
|||||||
dstNode.Scale = srcNode.Scale * weight;
|
dstNode.Scale = srcNode.Scale * weight;
|
||||||
dstNode.Orientation = srcNode.Orientation * weight;
|
dstNode.Orientation = srcNode.Orientation * weight;
|
||||||
}
|
}
|
||||||
else
|
else if (!hasNested)
|
||||||
{
|
{
|
||||||
dstNode = srcNode;
|
dstNode = srcNode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if material is an material instance.
|
/// Returns true if material is an material instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>True if it's a material instance, otherwise false.</returns>
|
|
||||||
virtual bool IsMaterialInstance() const = 0;
|
virtual bool IsMaterialInstance() const = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -618,7 +618,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
|||||||
// Return
|
// Return
|
||||||
case 5:
|
case 5:
|
||||||
{
|
{
|
||||||
auto& scope = ThreadStacks.Get().Stack->Scope;
|
auto scope = ThreadStacks.Get().Stack->Scope;
|
||||||
scope->FunctionReturn = tryGetValue(node->GetBox(1), Value::Zero);
|
scope->FunctionReturn = tryGetValue(node->GetBox(1), Value::Zero);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -634,7 +634,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Evaluate method parameter value from the current scope
|
// Evaluate method parameter value from the current scope
|
||||||
auto& scope = ThreadStacks.Get().Stack->Scope;
|
auto scope = ThreadStacks.Get().Stack->Scope;
|
||||||
int32 index = boxBase->ID - 1;
|
int32 index = boxBase->ID - 1;
|
||||||
if (index < scope->Parameters.Length())
|
if (index < scope->Parameters.Length())
|
||||||
value = scope->Parameters.Get()[index];
|
value = scope->Parameters.Get()[index];
|
||||||
@@ -1138,7 +1138,7 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int32 arrayIndex = 0;
|
int32 arrayIndex = 0;
|
||||||
for (; iteratorIndex < scope->ReturnedValues.Count(); arrayIndex++)
|
for (; arrayIndex < scope->ReturnedValues.Count(); arrayIndex++)
|
||||||
{
|
{
|
||||||
const auto& e = scope->ReturnedValues[arrayIndex];
|
const auto& e = scope->ReturnedValues[arrayIndex];
|
||||||
if (e.NodeId == node->ID && e.BoxId == 1)
|
if (e.NodeId == node->ID && e.BoxId == 1)
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ public class Content : EngineModule
|
|||||||
files.AddRange(Directory.GetFiles(FolderPath, "*.h", SearchOption.TopDirectoryOnly));
|
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, "Assets"), "*.h", SearchOption.TopDirectoryOnly));
|
||||||
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Cache"), "*.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.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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using Flax.Build;
|
using Flax.Build;
|
||||||
using Flax.Build.NativeCpp;
|
using Flax.Build.NativeCpp;
|
||||||
|
|
||||||
@@ -23,5 +24,7 @@ public class ContentExporters : EngineModule
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void GetFilesToDeploy(List<string> files)
|
public override void GetFilesToDeploy(List<string> files)
|
||||||
{
|
{
|
||||||
|
files.Add(Path.Combine(FolderPath, "AssetsExportingManager.h"));
|
||||||
|
files.Add(Path.Combine(FolderPath, "Types.h"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using Flax.Build;
|
using Flax.Build;
|
||||||
using Flax.Build.NativeCpp;
|
using Flax.Build.NativeCpp;
|
||||||
|
|
||||||
@@ -31,5 +32,7 @@ public class ContentImporters : EngineModule
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void GetFilesToDeploy(List<string> files)
|
public override void GetFilesToDeploy(List<string> files)
|
||||||
{
|
{
|
||||||
|
files.Add(Path.Combine(FolderPath, "AssetsImportingManager.h"));
|
||||||
|
files.Add(Path.Combine(FolderPath, "Types.h"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,8 +357,8 @@ public:
|
|||||||
{
|
{
|
||||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||||
{
|
{
|
||||||
if (i->Value)
|
if (i->Item)
|
||||||
::Delete(i->Value);
|
::Delete(i->Item);
|
||||||
}
|
}
|
||||||
Clear();
|
Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,9 +38,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BoundingBox()
|
BoundingBox() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BoundingBox"/> struct.
|
/// Initializes a new instance of the <see cref="BoundingBox"/> struct.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_STRUCT(InBuild) struct FLAXENGINE_API BoundingFrustum
|
API_STRUCT(InBuild) struct FLAXENGINE_API BoundingFrustum
|
||||||
{
|
{
|
||||||
|
friend CollisionsHelper;
|
||||||
private:
|
private:
|
||||||
Matrix _matrix;
|
Matrix _matrix;
|
||||||
|
|
||||||
@@ -33,9 +34,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BoundingFrustum()
|
BoundingFrustum() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BoundingFrustum"/> struct.
|
/// Initializes a new instance of the <see cref="BoundingFrustum"/> struct.
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BoundingSphere()
|
BoundingSphere() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BoundingSphere"/> struct.
|
/// Initializes a new instance of the <see cref="BoundingSphere"/> struct.
|
||||||
|
|||||||
@@ -939,7 +939,6 @@ bool CollisionsHelper::RayIntersectsSphere(const Ray& ray, const BoundingSphere&
|
|||||||
normal = Vector3::Up;
|
normal = Vector3::Up;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vector3 point = ray.Position + ray.Direction * distance;
|
const Vector3 point = ray.Position + ray.Direction * distance;
|
||||||
normal = Vector3::Normalize(point - sphere.Center);
|
normal = Vector3::Normalize(point - sphere.Center);
|
||||||
return true;
|
return true;
|
||||||
@@ -953,22 +952,17 @@ bool CollisionsHelper::RayIntersectsSphere(const Ray& ray, const BoundingSphere&
|
|||||||
point = Vector3::Zero;
|
point = Vector3::Zero;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
point = ray.Position + ray.Direction * distance;
|
point = ray.Position + ray.Direction * distance;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaneIntersectionType CollisionsHelper::PlaneIntersectsPoint(const Plane& plane, const Vector3& point)
|
PlaneIntersectionType CollisionsHelper::PlaneIntersectsPoint(const Plane& plane, const Vector3& point)
|
||||||
{
|
{
|
||||||
Real distance = Vector3::Dot(plane.Normal, point);
|
const Real distance = Vector3::Dot(plane.Normal, point) + plane.D;
|
||||||
distance += plane.D;
|
|
||||||
|
|
||||||
if (distance > Plane::DistanceEpsilon)
|
if (distance > Plane::DistanceEpsilon)
|
||||||
return PlaneIntersectionType::Front;
|
return PlaneIntersectionType::Front;
|
||||||
|
|
||||||
if (distance < Plane::DistanceEpsilon)
|
if (distance < Plane::DistanceEpsilon)
|
||||||
return PlaneIntersectionType::Back;
|
return PlaneIntersectionType::Back;
|
||||||
|
|
||||||
return PlaneIntersectionType::Intersecting;
|
return PlaneIntersectionType::Intersecting;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1169,7 +1163,6 @@ ContainmentType CollisionsHelper::SphereContainsPoint(const BoundingSphere& sphe
|
|||||||
{
|
{
|
||||||
if (Vector3::DistanceSquared(point, sphere.Center) <= sphere.Radius * sphere.Radius)
|
if (Vector3::DistanceSquared(point, sphere.Center) <= sphere.Radius * sphere.Radius)
|
||||||
return ContainmentType::Contains;
|
return ContainmentType::Contains;
|
||||||
|
|
||||||
return ContainmentType::Disjoint;
|
return ContainmentType::Disjoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1254,13 +1247,10 @@ ContainmentType CollisionsHelper::SphereContainsBox(const BoundingSphere& sphere
|
|||||||
ContainmentType CollisionsHelper::SphereContainsSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
|
ContainmentType CollisionsHelper::SphereContainsSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
|
||||||
{
|
{
|
||||||
const Real distance = Vector3::Distance(sphere1.Center, sphere2.Center);
|
const Real distance = Vector3::Distance(sphere1.Center, sphere2.Center);
|
||||||
|
|
||||||
if (sphere1.Radius + sphere2.Radius < distance)
|
if (sphere1.Radius + sphere2.Radius < distance)
|
||||||
return ContainmentType::Disjoint;
|
return ContainmentType::Disjoint;
|
||||||
|
|
||||||
if (sphere1.Radius - sphere2.Radius < distance)
|
if (sphere1.Radius - sphere2.Radius < distance)
|
||||||
return ContainmentType::Intersects;
|
return ContainmentType::Intersects;
|
||||||
|
|
||||||
return ContainmentType::Contains;
|
return ContainmentType::Contains;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1274,7 +1264,8 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
|||||||
auto result = ContainmentType::Contains;
|
auto result = ContainmentType::Contains;
|
||||||
for (int32 i = 0; i < 6; i++)
|
for (int32 i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
Plane plane = frustum.GetPlane(i);
|
Plane plane = frustum._planes[i];
|
||||||
|
|
||||||
Vector3 p = box.Minimum;
|
Vector3 p = box.Minimum;
|
||||||
if (plane.Normal.X >= 0)
|
if (plane.Normal.X >= 0)
|
||||||
p.X = box.Maximum.X;
|
p.X = box.Maximum.X;
|
||||||
@@ -1282,7 +1273,7 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
|||||||
p.Y = box.Maximum.Y;
|
p.Y = box.Maximum.Y;
|
||||||
if (plane.Normal.Z >= 0)
|
if (plane.Normal.Z >= 0)
|
||||||
p.Z = box.Maximum.Z;
|
p.Z = box.Maximum.Z;
|
||||||
if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back)
|
if (Vector3::Dot(plane.Normal, p) + plane.D < Plane::DistanceEpsilon)
|
||||||
return ContainmentType::Disjoint;
|
return ContainmentType::Disjoint;
|
||||||
|
|
||||||
p = box.Maximum;
|
p = box.Maximum;
|
||||||
@@ -1292,7 +1283,7 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
|||||||
p.Y = box.Minimum.Y;
|
p.Y = box.Minimum.Y;
|
||||||
if (plane.Normal.Z >= 0)
|
if (plane.Normal.Z >= 0)
|
||||||
p.Z = box.Minimum.Z;
|
p.Z = box.Minimum.Z;
|
||||||
if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back)
|
if (Vector3::Dot(plane.Normal, p) + plane.D < Plane::DistanceEpsilon)
|
||||||
result = ContainmentType::Intersects;
|
result = ContainmentType::Intersects;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -50,9 +50,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Color()
|
Color() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Color"/> struct.
|
/// Initializes a new instance of the <see cref="Color"/> struct.
|
||||||
|
|||||||
@@ -53,9 +53,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Color32()
|
Color32() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new Color32 with given r, g, b, a components.
|
/// Constructs a new Color32 with given r, g, b, a components.
|
||||||
|
|||||||
@@ -121,9 +121,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default constructor
|
/// Default constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Half2()
|
Half2() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Init
|
/// Init
|
||||||
@@ -185,9 +183,7 @@ public:
|
|||||||
Half Z;
|
Half Z;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Half3()
|
Half3() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Half3(Half x, Half y, Half z)
|
Half3(Half x, Half y, Half z)
|
||||||
: X(x)
|
: X(x)
|
||||||
@@ -242,9 +238,7 @@ public:
|
|||||||
Half W;
|
Half W;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Half4()
|
Half4() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Half4(Half x, Half y, Half z, Half w)
|
Half4(Half x, Half y, Half z, Half w)
|
||||||
: X(x)
|
: X(x)
|
||||||
|
|||||||
@@ -83,9 +83,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Matrix()
|
Matrix() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Matrix"/> struct.
|
/// Initializes a new instance of the <see cref="Matrix"/> struct.
|
||||||
|
|||||||
@@ -63,9 +63,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Matrix3x3()
|
Matrix3x3() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Matrix3x3"/> struct.
|
/// Initializes a new instance of the <see cref="Matrix3x3"/> struct.
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Plane()
|
Plane() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Init
|
/// Init
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Quaternion()
|
Quaternion() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Init
|
/// Init
|
||||||
|
|||||||
@@ -35,9 +35,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Ray()
|
Ray() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Ray"/> struct.
|
/// Initializes a new instance of the <see cref="Ray"/> struct.
|
||||||
|
|||||||
@@ -31,9 +31,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Rectangle()
|
Rectangle() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
// @param x Rectangle location X coordinate
|
// @param x Rectangle location X coordinate
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Transform()
|
Transform() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Transform"/> struct.
|
/// Initializes a new instance of the <see cref="Transform"/> struct.
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Triangle()
|
Triangle() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Triangle"/> struct.
|
/// Initializes a new instance of the <see cref="Triangle"/> struct.
|
||||||
|
|||||||
@@ -60,9 +60,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Vector2Base()
|
Vector2Base() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCE_INLINE Vector2Base(T xy)
|
FORCE_INLINE Vector2Base(T xy)
|
||||||
: X(xy)
|
: X(xy)
|
||||||
|
|||||||
@@ -89,9 +89,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Vector3Base()
|
Vector3Base() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCE_INLINE Vector3Base(T xyz)
|
FORCE_INLINE Vector3Base(T xyz)
|
||||||
: X(xyz)
|
: X(xyz)
|
||||||
|
|||||||
@@ -76,9 +76,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Vector4Base()
|
Vector4Base() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FORCE_INLINE Vector4Base(T xyzw)
|
FORCE_INLINE Vector4Base(T xyzw)
|
||||||
: X(xyzw)
|
: X(xyzw)
|
||||||
|
|||||||
@@ -52,9 +52,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty constructor.
|
/// Empty constructor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Viewport()
|
Viewport() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
// @param x The x coordinate of the upper-left corner of the viewport in pixels
|
// @param x The x coordinate of the upper-left corner of the viewport in pixels
|
||||||
|
|||||||
@@ -36,6 +36,32 @@ namespace AllocatorExt
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reallocates block of the memory.
|
||||||
|
/// </summary>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">A pointer to the memory block to reallocate.</param>
|
||||||
|
/// <param name="newSize">The size of the new allocation (in bytes).</param>
|
||||||
|
/// <param name="alignment">The memory alignment (in bytes). Must be an integer power of 2.</param>
|
||||||
|
/// <returns>The pointer to the allocated chunk of the memory. The pointer is a multiple of alignment.</returns>
|
||||||
|
inline void* ReallocAligned(void* ptr, uint64 newSize, uint64 alignment)
|
||||||
|
{
|
||||||
|
if (newSize == 0)
|
||||||
|
{
|
||||||
|
Allocator::Free(ptr);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!ptr)
|
||||||
|
return Allocator::Allocate(newSize, alignment);
|
||||||
|
void* result = Allocator::Allocate(newSize, alignment);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
Platform::MemoryCopy(result, ptr, newSize);
|
||||||
|
Allocator::Free(ptr);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reallocates block of the memory.
|
/// Reallocates block of the memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -114,3 +114,20 @@ inline Span<T> ToSpan(const T* ptr, int32 length)
|
|||||||
{
|
{
|
||||||
return Span<T>(ptr, 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)
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < span.Length(); i++)
|
||||||
|
{
|
||||||
|
if (span.Get()[i] == value)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ void String::Set(const char* chars, int32 length)
|
|||||||
_length = length;
|
_length = length;
|
||||||
}
|
}
|
||||||
if (chars)
|
if (chars)
|
||||||
StringUtils::ConvertANSI2UTF16(chars, _data, length);
|
StringUtils::ConvertANSI2UTF16(chars, _data, length, _length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::SetUTF8(const char* chars, int32 length)
|
void String::SetUTF8(const char* chars, int32 length)
|
||||||
@@ -112,7 +112,8 @@ void String::Append(const char* chars, int32 count)
|
|||||||
_data = (Char*)Platform::Allocate((_length + 1) * sizeof(Char), 16);
|
_data = (Char*)Platform::Allocate((_length + 1) * sizeof(Char), 16);
|
||||||
|
|
||||||
Platform::MemoryCopy(_data, oldData, oldLength * sizeof(Char));
|
Platform::MemoryCopy(_data, oldData, oldLength * sizeof(Char));
|
||||||
StringUtils::ConvertANSI2UTF16(chars, _data + oldLength, count * sizeof(Char));
|
StringUtils::ConvertANSI2UTF16(chars, _data + oldLength, count, _length);
|
||||||
|
_length += oldLength;
|
||||||
_data[_length] = 0;
|
_data[_length] = 0;
|
||||||
|
|
||||||
Platform::Free(oldData);
|
Platform::Free(oldData);
|
||||||
|
|||||||
@@ -125,7 +125,8 @@ public:
|
|||||||
const int32 length = str && *str ? StringUtils::Length(str) : 0;
|
const int32 length = str && *str ? StringUtils::Length(str) : 0;
|
||||||
const int32 prevCnt = _data.Count();
|
const int32 prevCnt = _data.Count();
|
||||||
_data.AddDefault(length);
|
_data.AddDefault(length);
|
||||||
StringUtils::ConvertANSI2UTF16(str, _data.Get() + prevCnt, length);
|
int32 tmp;
|
||||||
|
StringUtils::ConvertANSI2UTF16(str, _data.Get() + prevCnt, length, tmp);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -660,7 +660,8 @@ Variant::Variant(const StringAnsiView& v)
|
|||||||
const int32 length = v.Length() * sizeof(Char) + 2;
|
const int32 length = v.Length() * sizeof(Char) + 2;
|
||||||
AsBlob.Data = Allocator::Allocate(length);
|
AsBlob.Data = Allocator::Allocate(length);
|
||||||
AsBlob.Length = length;
|
AsBlob.Length = length;
|
||||||
StringUtils::ConvertANSI2UTF16(v.Get(), (Char*)AsBlob.Data, v.Length());
|
int32 tmp;
|
||||||
|
StringUtils::ConvertANSI2UTF16(v.Get(), (Char*)AsBlob.Data, v.Length(), tmp);
|
||||||
((Char*)AsBlob.Data)[v.Length()] = 0;
|
((Char*)AsBlob.Data)[v.Length()] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2578,7 +2579,8 @@ void Variant::SetString(const StringAnsiView& str)
|
|||||||
AsBlob.Data = Allocator::Allocate(length);
|
AsBlob.Data = Allocator::Allocate(length);
|
||||||
AsBlob.Length = length;
|
AsBlob.Length = length;
|
||||||
}
|
}
|
||||||
StringUtils::ConvertANSI2UTF16(str.Get(), (Char*)AsBlob.Data, str.Length());
|
int32 tmp;
|
||||||
|
StringUtils::ConvertANSI2UTF16(str.Get(), (Char*)AsBlob.Data, str.Length(), tmp);
|
||||||
((Char*)AsBlob.Data)[str.Length()] = 0;
|
((Char*)AsBlob.Data)[str.Length()] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2794,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)
|
bool Variant::CanCast(const Variant& v, const VariantType& to)
|
||||||
{
|
{
|
||||||
if (v.Type == to)
|
if (v.Type == to)
|
||||||
@@ -3680,6 +3798,7 @@ void Variant::AllocStructure()
|
|||||||
const ScriptingType& type = typeHandle.GetType();
|
const ScriptingType& type = typeHandle.GetType();
|
||||||
AsBlob.Length = type.Size;
|
AsBlob.Length = type.Size;
|
||||||
AsBlob.Data = Allocator::Allocate(AsBlob.Length);
|
AsBlob.Data = Allocator::Allocate(AsBlob.Length);
|
||||||
|
Platform::MemoryClear(AsBlob.Data, AsBlob.Length);
|
||||||
type.Struct.Ctor(AsBlob.Data);
|
type.Struct.Ctor(AsBlob.Data);
|
||||||
}
|
}
|
||||||
else if (typeName == "System.Int16" || typeName == "System.UInt16")
|
else if (typeName == "System.Int16" || typeName == "System.UInt16")
|
||||||
|
|||||||
@@ -359,6 +359,9 @@ public:
|
|||||||
void SetAsset(Asset* asset);
|
void SetAsset(Asset* asset);
|
||||||
String ToString() const;
|
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
|
FORCE_INLINE Variant Cast(const VariantType& to) const
|
||||||
{
|
{
|
||||||
return Cast(*this, to);
|
return Cast(*this, to);
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ namespace FlaxEngine.Interop
|
|||||||
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
|
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
|
||||||
public static ManagedArray WrapPooledArray(Array arr, Type arrayType)
|
public static ManagedArray WrapPooledArray(Array arr, Type arrayType)
|
||||||
{
|
{
|
||||||
ManagedArray managedArray = ManagedArrayPool.Get(arr.Length * Marshal.SizeOf(arr.GetType().GetElementType()));
|
ManagedArray managedArray = ManagedArrayPool.Get(arr.Length * NativeInterop.GetTypeSize(arr.GetType().GetElementType()));
|
||||||
managedArray.WrapArray(arr, arrayType);
|
managedArray.WrapArray(arr, arrayType);
|
||||||
return managedArray;
|
return managedArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType)
|
internal static ManagedArray AllocateNewArray(int length, Type arrayType, Type elementType)
|
||||||
=> new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, Marshal.SizeOf(elementType)), length, arrayType, elementType);
|
=> new ManagedArray((IntPtr)NativeInterop.NativeAlloc(length, NativeInterop.GetTypeSize(elementType)), length, arrayType, elementType);
|
||||||
|
|
||||||
internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type arrayType, Type elementType)
|
internal static ManagedArray AllocateNewArray(IntPtr ptr, int length, Type arrayType, Type elementType)
|
||||||
=> new ManagedArray(ptr, length, arrayType, elementType);
|
=> new ManagedArray(ptr, length, arrayType, elementType);
|
||||||
@@ -86,7 +86,7 @@ namespace FlaxEngine.Interop
|
|||||||
_length = arr.Length;
|
_length = arr.Length;
|
||||||
_arrayType = arrayType;
|
_arrayType = arrayType;
|
||||||
_elementType = arr.GetType().GetElementType();
|
_elementType = arr.GetType().GetElementType();
|
||||||
_elementSize = Marshal.SizeOf(_elementType);
|
_elementSize = NativeInterop.GetTypeSize(_elementType);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Allocate<T>(int length) where T : unmanaged
|
internal void Allocate<T>(int length) where T : unmanaged
|
||||||
@@ -117,7 +117,7 @@ namespace FlaxEngine.Interop
|
|||||||
_length = length;
|
_length = length;
|
||||||
_arrayType = arrayType;
|
_arrayType = arrayType;
|
||||||
_elementType = elementType;
|
_elementType = elementType;
|
||||||
_elementSize = Marshal.SizeOf(elementType);
|
_elementSize = NativeInterop.GetTypeSize(_elementType);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ManagedArray()
|
~ManagedArray()
|
||||||
|
|||||||
@@ -350,14 +350,14 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
public static class NativeToManaged
|
public static class NativeToManaged
|
||||||
{
|
{
|
||||||
public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
|
public static T[] AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
if (unmanaged is null)
|
if (unmanaged is null)
|
||||||
return null;
|
return null;
|
||||||
return new T[numElements];
|
return new T[numElements];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Span<T> GetManagedValuesDestination(T[]? managed) => managed;
|
public static Span<T> GetManagedValuesDestination(T[] managed) => managed;
|
||||||
|
|
||||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
@@ -390,7 +390,7 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
public static class ManagedToNative
|
public static class ManagedToNative
|
||||||
{
|
{
|
||||||
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
|
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||||
{
|
{
|
||||||
if (managed is null)
|
if (managed is null)
|
||||||
{
|
{
|
||||||
@@ -399,10 +399,10 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
numElements = managed.Length;
|
numElements = managed.Length;
|
||||||
ManagedArray managedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
ManagedArray managedArray = ManagedArray.AllocatePooledArray<TUnmanagedElement>(managed.Length);
|
||||||
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak);
|
return (TUnmanagedElement*)ManagedHandle.ToIntPtr(managedArray, GCHandleType.Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[]? managed) => managed;
|
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
|
||||||
|
|
||||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
@@ -431,7 +431,7 @@ namespace FlaxEngine.Interop
|
|||||||
ManagedArray unmanagedArray;
|
ManagedArray unmanagedArray;
|
||||||
ManagedHandle handle;
|
ManagedHandle handle;
|
||||||
|
|
||||||
public void FromManaged(T[]? managed)
|
public void FromManaged(T[] managed)
|
||||||
{
|
{
|
||||||
if (managed == null)
|
if (managed == null)
|
||||||
return;
|
return;
|
||||||
@@ -476,7 +476,7 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
|
public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||||
{
|
{
|
||||||
if (managed is null)
|
if (managed is null)
|
||||||
{
|
{
|
||||||
@@ -489,7 +489,7 @@ namespace FlaxEngine.Interop
|
|||||||
return (TUnmanagedElement*)handle;
|
return (TUnmanagedElement*)handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[]? managed) => managed;
|
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => managed;
|
||||||
|
|
||||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
@@ -499,9 +499,9 @@ namespace FlaxEngine.Interop
|
|||||||
return unmanagedArray.ToSpan<TUnmanagedElement>();
|
return unmanagedArray.ToSpan<TUnmanagedElement>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements];
|
public static T[] AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) => unmanaged is null ? null : new T[numElements];
|
||||||
|
|
||||||
public static Span<T> GetManagedValuesDestination(T[]? managed) => managed;
|
public static Span<T> GetManagedValuesDestination(T[] managed) => managed;
|
||||||
|
|
||||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TUnmanagedElement* unmanaged, int numElements)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -244,7 +244,25 @@ namespace FlaxEngine.Interop
|
|||||||
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
|
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
|
||||||
typeAttributes = (uint)type.Attributes,
|
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]
|
[UnmanagedCallersOnly]
|
||||||
@@ -770,6 +788,13 @@ namespace FlaxEngine.Interop
|
|||||||
field.field.SetValue(fieldOwner, value);
|
field.field.SetValue(fieldOwner, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly]
|
||||||
|
internal static int FieldGetOffset(ManagedHandle fieldHandle)
|
||||||
|
{
|
||||||
|
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
||||||
|
return (int)Marshal.OffsetOf(field.field.DeclaringType, field.field.Name);
|
||||||
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
internal static void FieldGetValue(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr)
|
internal static void FieldGetValue(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr)
|
||||||
{
|
{
|
||||||
@@ -778,6 +803,15 @@ namespace FlaxEngine.Interop
|
|||||||
field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset);
|
field.toNativeMarshaller(field.field, fieldOwner, valuePtr, out int fieldOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly]
|
||||||
|
internal static IntPtr FieldGetValueBoxed(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle)
|
||||||
|
{
|
||||||
|
object fieldOwner = fieldOwnerHandle.Target;
|
||||||
|
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
|
||||||
|
object fieldValue = field.field.GetValue(fieldOwner);
|
||||||
|
return Invoker.MarshalReturnValueGeneric(field.field.FieldType, fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly]
|
[UnmanagedCallersOnly]
|
||||||
internal static void WriteArrayReference(ManagedHandle arrayHandle, IntPtr valueHandle, int index)
|
internal static void WriteArrayReference(ManagedHandle arrayHandle, IntPtr valueHandle, int index)
|
||||||
{
|
{
|
||||||
@@ -885,6 +919,7 @@ namespace FlaxEngine.Interop
|
|||||||
handle.Free();
|
handle.Free();
|
||||||
fieldHandleCacheCollectible.Clear();
|
fieldHandleCacheCollectible.Clear();
|
||||||
#endif
|
#endif
|
||||||
|
_typeSizeCache.Clear();
|
||||||
|
|
||||||
foreach (var pair in classAttributesCacheCollectible)
|
foreach (var pair in classAttributesCacheCollectible)
|
||||||
pair.Value.Free();
|
pair.Value.Free();
|
||||||
@@ -923,7 +958,7 @@ namespace FlaxEngine.Interop
|
|||||||
if (nativeType.IsClass)
|
if (nativeType.IsClass)
|
||||||
size = sizeof(IntPtr);
|
size = sizeof(IntPtr);
|
||||||
else
|
else
|
||||||
size = Marshal.SizeOf(nativeType);
|
size = GetTypeSize(nativeType);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ namespace FlaxEngine.Interop
|
|||||||
#endif
|
#endif
|
||||||
private static Dictionary<object, ManagedHandle> classAttributesCacheCollectible = new();
|
private static Dictionary<object, ManagedHandle> classAttributesCacheCollectible = new();
|
||||||
private static Dictionary<Assembly, ManagedHandle> assemblyHandles = new();
|
private static Dictionary<Assembly, ManagedHandle> assemblyHandles = new();
|
||||||
|
private static Dictionary<Type, int> _typeSizeCache = new();
|
||||||
|
|
||||||
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
||||||
internal static Dictionary<string, string> nativeLibraryPaths = new();
|
internal static Dictionary<string, string> nativeLibraryPaths = new();
|
||||||
@@ -96,14 +97,17 @@ namespace FlaxEngine.Interop
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if FLAX_EDITOR
|
#if FLAX_EDITOR
|
||||||
private static Assembly? OnScriptingAssemblyLoadContextResolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
|
private static Assembly OnScriptingAssemblyLoadContextResolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
|
||||||
{
|
{
|
||||||
// FIXME: There should be a better way to resolve the path to EditorTargetPath where the dependencies are stored
|
// FIXME: There should be a better way to resolve the path to EditorTargetPath where the dependencies are stored
|
||||||
string editorTargetPath = Path.GetDirectoryName(nativeLibraryPaths.Keys.First(x => x != "FlaxEngine"));
|
foreach (string nativeLibraryPath in nativeLibraryPaths.Values)
|
||||||
|
{
|
||||||
|
string editorTargetPath = Path.GetDirectoryName(nativeLibraryPath);
|
||||||
|
|
||||||
var assemblyPath = Path.Combine(editorTargetPath, assemblyName.Name + ".dll");
|
var assemblyPath = Path.Combine(editorTargetPath, assemblyName.Name + ".dll");
|
||||||
if (File.Exists(assemblyPath))
|
if (File.Exists(assemblyPath))
|
||||||
return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -581,7 +585,7 @@ namespace FlaxEngine.Interop
|
|||||||
else if (fieldType.IsClass || fieldType.IsPointer)
|
else if (fieldType.IsClass || fieldType.IsPointer)
|
||||||
fieldAlignment = IntPtr.Size;
|
fieldAlignment = IntPtr.Size;
|
||||||
else
|
else
|
||||||
fieldAlignment = Marshal.SizeOf(fieldType);
|
fieldAlignment = GetTypeSize(fieldType);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset)
|
internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset)
|
||||||
@@ -1085,6 +1089,29 @@ namespace FlaxEngine.Interop
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static int GetTypeSize(Type type)
|
||||||
|
{
|
||||||
|
if (!_typeSizeCache.TryGetValue(type, out var size))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var marshalType = type;
|
||||||
|
if (type.IsEnum)
|
||||||
|
marshalType = type.GetEnumUnderlyingType();
|
||||||
|
size = Marshal.SizeOf(marshalType);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Workaround the issue where structure defined within generic type instance (eg. MyType<int>.MyStruct) fails to get size
|
||||||
|
// https://github.com/dotnet/runtime/issues/46426
|
||||||
|
var obj = Activator.CreateInstance(type);
|
||||||
|
size = Marshal.SizeOf(obj);
|
||||||
|
}
|
||||||
|
_typeSizeCache.Add(type, size);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
private static class DelegateHelpers
|
private static class DelegateHelpers
|
||||||
{
|
{
|
||||||
#if USE_AOT
|
#if USE_AOT
|
||||||
|
|||||||
@@ -1031,6 +1031,11 @@ API_ENUM(Attributes="Flags") enum class ViewFlags : uint64
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Sky = 1 << 26,
|
Sky = 1 << 26,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows/hides light debug shapes.
|
||||||
|
/// </summary>
|
||||||
|
LightsDebug = 1 << 27,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default flags for Game.
|
/// Default flags for Game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the default material.
|
/// Gets the default material.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
MaterialBase* GetDefaultMaterial() const;
|
API_PROPERTY() MaterialBase* GetDefaultMaterial() const;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the default material (Deformable domain).
|
/// Gets the default material (Deformable domain).
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ bool GPUShader::Create(MemoryReadStream& stream)
|
|||||||
|
|
||||||
// Create CB
|
// Create CB
|
||||||
#if GPU_ENABLE_RESOURCE_NAMING
|
#if GPU_ENABLE_RESOURCE_NAMING
|
||||||
String name = ToString() + TEXT(".CB") + i;
|
String name = String::Format(TEXT("{}.CB{}"), ToString(), i);
|
||||||
#else
|
#else
|
||||||
String name;
|
String name;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1337,6 +1337,20 @@ Actor* Actor::FindActor(const MClass* type, const StringView& name) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Actor* Actor::FindActor(const MClass* type, const Tag& tag) const
|
||||||
|
{
|
||||||
|
CHECK_RETURN(type, nullptr);
|
||||||
|
if (GetClass()->IsSubClassOf(type) && HasTag(tag))
|
||||||
|
return const_cast<Actor*>(this);
|
||||||
|
for (auto child : Children)
|
||||||
|
{
|
||||||
|
const auto actor = child->FindActor(type, tag);
|
||||||
|
if (actor)
|
||||||
|
return actor;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Script* Actor::FindScript(const MClass* type) const
|
Script* Actor::FindScript(const MClass* type) const
|
||||||
{
|
{
|
||||||
CHECK_RETURN(type, nullptr);
|
CHECK_RETURN(type, nullptr);
|
||||||
|
|||||||
@@ -270,6 +270,17 @@ namespace FlaxEngine
|
|||||||
return FindActor(typeof(T), name) as T;
|
return FindActor(typeof(T), name) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find actor of the given type and tag in this actor hierarchy (checks this actor and all children hierarchy).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">A tag on the object.</param>
|
||||||
|
/// <typeparam name="T">Type of the object.</typeparam>
|
||||||
|
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||||
|
public T FindActor<T>(Tag tag) where T : Actor
|
||||||
|
{
|
||||||
|
return FindActor(typeof(T), tag) as T;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Searches for all actors of a specific type in this actor children list.
|
/// Searches for all actors of a specific type in this actor children list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -739,6 +739,14 @@ public:
|
|||||||
/// <returns>Actor instance if found, null otherwise.</returns>
|
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||||
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name) const;
|
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name) const;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find the actor of the given type and tag in this actor hierarchy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||||
|
/// <param name="tag">The tag of the actor to search for.</param>
|
||||||
|
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||||
|
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag) const;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
|
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -760,6 +768,17 @@ public:
|
|||||||
return (T*)FindActor(T::GetStaticClass(), name);
|
return (T*)FindActor(T::GetStaticClass(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find the actor of the given type and tag in this actor hierarchy (checks this actor and all children hierarchy).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">The tag of the actor to search for.</param>
|
||||||
|
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE T* FindActor(const Tag& tag) const
|
||||||
|
{
|
||||||
|
return (T*)FindActor(T::GetStaticClass(), tag);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find the script of the given type in this actor hierarchy (checks this actor and all children hierarchy).
|
/// Tries to find the script of the given type in this actor hierarchy (checks this actor and all children hierarchy).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ void AnimatedModel::SetCurrentPose(const Array<Matrix>& nodesTransformation, boo
|
|||||||
Matrix invWorld;
|
Matrix invWorld;
|
||||||
Matrix::Invert(world, invWorld);
|
Matrix::Invert(world, invWorld);
|
||||||
for (auto& m : GraphInstance.NodesPose)
|
for (auto& m : GraphInstance.NodesPose)
|
||||||
m = invWorld * m;
|
m = m * invWorld;
|
||||||
}
|
}
|
||||||
OnAnimationUpdated();
|
OnAnimationUpdated();
|
||||||
}
|
}
|
||||||
@@ -603,7 +603,13 @@ void AnimatedModel::OnAnimationUpdated_Sync()
|
|||||||
// Update synchronous stuff
|
// Update synchronous stuff
|
||||||
UpdateSockets();
|
UpdateSockets();
|
||||||
ApplyRootMotion(GraphInstance.RootMotion);
|
ApplyRootMotion(GraphInstance.RootMotion);
|
||||||
|
if (!_isDuringUpdateEvent)
|
||||||
|
{
|
||||||
|
// Prevent stack-overflow when gameplay modifies the pose within the event
|
||||||
|
_isDuringUpdateEvent = true;
|
||||||
AnimationUpdated();
|
AnimationUpdated();
|
||||||
|
_isDuringUpdateEvent = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedModel::OnAnimationUpdated()
|
void AnimatedModel::OnAnimationUpdated()
|
||||||
@@ -892,6 +898,31 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
|
|||||||
DrawModes |= DrawPass::GlobalSurfaceAtlas;
|
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)
|
bool AnimatedModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
|
||||||
{
|
{
|
||||||
auto model = SkinnedModel.Get();
|
auto model = SkinnedModel.Get();
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ private:
|
|||||||
AnimationUpdateMode _actualMode;
|
AnimationUpdateMode _actualMode;
|
||||||
uint32 _counter;
|
uint32 _counter;
|
||||||
Real _lastMinDstSqr;
|
Real _lastMinDstSqr;
|
||||||
|
bool _isDuringUpdateEvent = false;
|
||||||
uint64 _lastUpdateFrame;
|
uint64 _lastUpdateFrame;
|
||||||
BlendShapesInstance _blendShapes;
|
BlendShapesInstance _blendShapes;
|
||||||
ScriptingObjectReference<AnimatedModel> _masterPose;
|
ScriptingObjectReference<AnimatedModel> _masterPose;
|
||||||
@@ -372,6 +373,8 @@ public:
|
|||||||
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
|
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
|
||||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) 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(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
|
||||||
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
|
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
|
||||||
void OnDeleteObject() override;
|
void OnDeleteObject() override;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ void Light::OnEnable()
|
|||||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
GetSceneRendering()->AddViewportIcon(this);
|
GetSceneRendering()->AddViewportIcon(this);
|
||||||
|
GetSceneRendering()->AddLightsDebug<Light, &Light::DrawLightsDebug>(this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
@@ -36,6 +37,7 @@ void Light::OnDisable()
|
|||||||
{
|
{
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
GetSceneRendering()->RemoveViewportIcon(this);
|
GetSceneRendering()->RemoveViewportIcon(this);
|
||||||
|
GetSceneRendering()->RemoveLightsDebug<Light, &Light::DrawLightsDebug>(this);
|
||||||
#endif
|
#endif
|
||||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||||
|
|
||||||
@@ -43,6 +45,12 @@ void Light::OnDisable()
|
|||||||
Actor::OnDisable();
|
Actor::OnDisable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
void Light::DrawLightsDebug(RenderView& view)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Light::Serialize(SerializeStream& stream, const void* otherObj)
|
void Light::Serialize(SerializeStream& stream, const void* otherObj)
|
||||||
{
|
{
|
||||||
// Base
|
// Base
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ public:
|
|||||||
const Vector3 size(50);
|
const Vector3 size(50);
|
||||||
return BoundingBox(_transform.Translation - size, _transform.Translation + size);
|
return BoundingBox(_transform.Translation - size, _transform.Translation + size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void DrawLightsDebug(RenderView& view);
|
||||||
#endif
|
#endif
|
||||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||||
|
|||||||
@@ -39,10 +39,9 @@ void ModelInstanceActor::SetMaterial(int32 entryIndex, MaterialBase* material)
|
|||||||
MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 entryIndex)
|
MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 entryIndex)
|
||||||
{
|
{
|
||||||
WaitForModelLoad();
|
WaitForModelLoad();
|
||||||
CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
|
MaterialBase* material = GetMaterial(entryIndex);
|
||||||
auto material = Entries[entryIndex].Material.Get();
|
|
||||||
CHECK_RETURN(material && !material->WaitForLoaded(), nullptr);
|
CHECK_RETURN(material && !material->WaitForLoaded(), nullptr);
|
||||||
const auto result = material->CreateVirtualInstance();
|
MaterialInstance* result = material->CreateVirtualInstance();
|
||||||
Entries[entryIndex].Material = result;
|
Entries[entryIndex].Material = result;
|
||||||
if (_sceneRenderingKey != -1)
|
if (_sceneRenderingKey != -1)
|
||||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||||
|
|||||||
@@ -35,6 +35,17 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_PROPERTY() void SetEntries(const Array<ModelInstanceEntry>& value);
|
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>
|
/// <summary>
|
||||||
/// Sets the material to the entry slot. Can be used to override the material of the meshes using this slot.
|
/// Sets the material to the entry slot. Can be used to override the material of the meshes using this slot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -143,6 +143,16 @@ void PointLight::OnDebugDrawSelected()
|
|||||||
LightWithShadow::OnDebugDrawSelected();
|
LightWithShadow::OnDebugDrawSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PointLight::DrawLightsDebug(RenderView& view)
|
||||||
|
{
|
||||||
|
const BoundingSphere sphere(_sphere.Center - view.Origin, _sphere.Radius);
|
||||||
|
if (!view.CullingFrustum.Intersects(sphere) || !EnumHasAnyFlags(view.Flags, ViewFlags::PointLights))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Draw influence range
|
||||||
|
DEBUG_DRAW_WIRE_SPHERE(_sphere, Color::Yellow, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void PointLight::OnLayerChanged()
|
void PointLight::OnLayerChanged()
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ public:
|
|||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
void OnDebugDraw() override;
|
void OnDebugDraw() override;
|
||||||
void OnDebugDrawSelected() override;
|
void OnDebugDrawSelected() override;
|
||||||
|
void DrawLightsDebug(RenderView& view) override;
|
||||||
#endif
|
#endif
|
||||||
void OnLayerChanged() override;
|
void OnLayerChanged() override;
|
||||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
|
|||||||
@@ -341,6 +341,31 @@ void SplineModel::OnParentChanged()
|
|||||||
OnSplineUpdated();
|
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
|
bool SplineModel::HasContentLoaded() const
|
||||||
{
|
{
|
||||||
return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded();
|
return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded();
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ public:
|
|||||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||||
void OnParentChanged() override;
|
void OnParentChanged() override;
|
||||||
|
const Span<MaterialSlot> GetMaterialSlots() const override;
|
||||||
|
MaterialBase* GetMaterial(int32 entryIndex) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// [ModelInstanceActor]
|
// [ModelInstanceActor]
|
||||||
|
|||||||
@@ -203,6 +203,11 @@ void SpotLight::OnDebugDrawSelected()
|
|||||||
DEBUG_DRAW_LINE(position, position + forward * radius + right * discRadius, color, 0, true);
|
DEBUG_DRAW_LINE(position, position + forward * radius + right * discRadius, color, 0, true);
|
||||||
DEBUG_DRAW_LINE(position, position + forward * radius - right * discRadius, color, 0, true);
|
DEBUG_DRAW_LINE(position, position + forward * radius - right * discRadius, color, 0, true);
|
||||||
|
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius + up * falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius - up * falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius + right * falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius - right * falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
|
||||||
DEBUG_DRAW_CIRCLE(position + forward * radius, forward, discRadius, color, 0, true);
|
DEBUG_DRAW_CIRCLE(position + forward * radius, forward, discRadius, color, 0, true);
|
||||||
DEBUG_DRAW_CIRCLE(position + forward * radius, forward, falloffDiscRadius, color * 0.6f, 0, true);
|
DEBUG_DRAW_CIRCLE(position + forward * radius, forward, falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
|
||||||
@@ -210,6 +215,34 @@ void SpotLight::OnDebugDrawSelected()
|
|||||||
LightWithShadow::OnDebugDrawSelected();
|
LightWithShadow::OnDebugDrawSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpotLight::DrawLightsDebug(RenderView& view)
|
||||||
|
{
|
||||||
|
const BoundingSphere sphere(_sphere.Center - view.Origin, _sphere.Radius);
|
||||||
|
if (!view.CullingFrustum.Intersects(sphere) || !EnumHasAnyFlags(view.Flags, ViewFlags::SpotLights))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto color = Color::Yellow;
|
||||||
|
Vector3 right = _transform.GetRight();
|
||||||
|
Vector3 up = _transform.GetUp();
|
||||||
|
Vector3 forward = GetDirection();
|
||||||
|
float radius = GetScaledRadius();
|
||||||
|
float discRadius = radius * Math::Tan(_outerConeAngle * DegreesToRadians);
|
||||||
|
float falloffDiscRadius = radius * Math::Tan(_innerConeAngle * DegreesToRadians);
|
||||||
|
Vector3 position = GetPosition();
|
||||||
|
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius + up * discRadius, color, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius - up * discRadius, color, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius + right * discRadius, color, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius - right * discRadius, color, 0, true);
|
||||||
|
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius + up * falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius - up * falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius + right * falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
DEBUG_DRAW_LINE(position, position + forward * radius - right * falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
|
||||||
|
DEBUG_DRAW_CIRCLE(position + forward * radius, forward, discRadius, color, 0, true);
|
||||||
|
DEBUG_DRAW_CIRCLE(position + forward * radius, forward, falloffDiscRadius, color * 0.6f, 0, true);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void SpotLight::Serialize(SerializeStream& stream, const void* otherObj)
|
void SpotLight::Serialize(SerializeStream& stream, const void* otherObj)
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ public:
|
|||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
void OnDebugDraw() override;
|
void OnDebugDraw() override;
|
||||||
void OnDebugDrawSelected() override;
|
void OnDebugDrawSelected() override;
|
||||||
|
void DrawLightsDebug(RenderView& view) override;
|
||||||
#endif
|
#endif
|
||||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||||
|
|||||||
@@ -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)
|
bool StaticModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
|
||||||
{
|
{
|
||||||
auto model = Model.Get();
|
auto model = Model.Get();
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ public:
|
|||||||
/// <param name="meshIndex">The zero-based mesh index.</param>
|
/// <param name="meshIndex">The zero-based mesh index.</param>
|
||||||
/// <param name="lodIndex">The LOD index.</param>
|
/// <param name="lodIndex">The LOD index.</param>
|
||||||
/// <returns>Material or null if not assigned.</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Gets the color of the painter vertex (this model instance).
|
/// 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;
|
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
|
||||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) 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(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
|
||||||
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
|
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
|
||||||
|
|
||||||
|
|||||||
@@ -726,116 +726,6 @@ int32 Level::GetLayerIndex(const StringView& layer)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Actor* FindActorRecursive(Actor* node, const Tag& tag)
|
|
||||||
{
|
|
||||||
if (node->HasTag(tag))
|
|
||||||
return node;
|
|
||||||
Actor* result = nullptr;
|
|
||||||
for (Actor* child : node->Children)
|
|
||||||
{
|
|
||||||
result = FindActorRecursive(child, tag);
|
|
||||||
if (result)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FindActorsRecursive(Actor* node, const Tag& tag, const bool activeOnly, Array<Actor*>& result)
|
|
||||||
{
|
|
||||||
if (activeOnly && !node->GetIsActive())
|
|
||||||
return;
|
|
||||||
if (node->HasTag(tag))
|
|
||||||
result.Add(node);
|
|
||||||
for (Actor* child : node->Children)
|
|
||||||
FindActorsRecursive(child, tag, activeOnly, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FindActorsRecursiveByParentTags(Actor* node, const Array<Tag>& tags, const bool activeOnly, Array<Actor*>& result)
|
|
||||||
{
|
|
||||||
if (activeOnly && !node->GetIsActive())
|
|
||||||
return;
|
|
||||||
for (Tag tag : tags)
|
|
||||||
{
|
|
||||||
if (node->HasTag(tag))
|
|
||||||
{
|
|
||||||
result.Add(node);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Actor* child : node->Children)
|
|
||||||
FindActorsRecursiveByParentTags(child, tags, activeOnly, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Actor* Level::FindActor(const Tag& tag, Actor* root)
|
|
||||||
{
|
|
||||||
PROFILE_CPU();
|
|
||||||
if (root)
|
|
||||||
return FindActorRecursive(root, tag);
|
|
||||||
Actor* result = nullptr;
|
|
||||||
for (Scene* scene : Scenes)
|
|
||||||
{
|
|
||||||
result = FindActorRecursive(scene, tag);
|
|
||||||
if (result)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FindActorRecursive(Actor* node, const Tag& tag, Array<Actor*>& result)
|
|
||||||
{
|
|
||||||
if (node->HasTag(tag))
|
|
||||||
result.Add(node);
|
|
||||||
for (Actor* child : node->Children)
|
|
||||||
FindActorRecursive(child, tag, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
Array<Actor*> Level::FindActors(const Tag& tag, const bool activeOnly, Actor* root)
|
|
||||||
{
|
|
||||||
PROFILE_CPU();
|
|
||||||
Array<Actor*> result;
|
|
||||||
if (root)
|
|
||||||
{
|
|
||||||
FindActorsRecursive(root, tag, activeOnly, result);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ScopeLock lock(ScenesLock);
|
|
||||||
for (Scene* scene : Scenes)
|
|
||||||
FindActorsRecursive(scene, tag, activeOnly, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array<Actor*> Level::FindActorsByParentTag(const Tag& parentTag, const bool activeOnly, Actor* root)
|
|
||||||
{
|
|
||||||
PROFILE_CPU();
|
|
||||||
Array<Actor*> result;
|
|
||||||
const Array<Tag> subTags = Tags::GetSubTags(parentTag);
|
|
||||||
|
|
||||||
if (subTags.Count() == 0)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (subTags.Count() == 1)
|
|
||||||
{
|
|
||||||
result = FindActors(subTags[0], activeOnly, root);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root)
|
|
||||||
{
|
|
||||||
FindActorsRecursiveByParentTags(root, subTags, activeOnly, result);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ScopeLock lock(ScenesLock);
|
|
||||||
for (Scene* scene : Scenes)
|
|
||||||
FindActorsRecursiveByParentTags(scene, subTags, activeOnly, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
|
void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
|
||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
@@ -1505,6 +1395,143 @@ Actor* Level::FindActor(const MClass* type, const StringView& name)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Actor* FindActorRecursive(Actor* node, const Tag& tag)
|
||||||
|
{
|
||||||
|
if (node->HasTag(tag))
|
||||||
|
return node;
|
||||||
|
Actor* result = nullptr;
|
||||||
|
for (Actor* child : node->Children)
|
||||||
|
{
|
||||||
|
result = FindActorRecursive(child, tag);
|
||||||
|
if (result)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor* FindActorRecursiveByType(Actor* node, const MClass* type, const Tag& tag)
|
||||||
|
{
|
||||||
|
CHECK_RETURN(type, nullptr);
|
||||||
|
if (node->HasTag(tag) && node->GetClass()->IsSubClassOf(type))
|
||||||
|
return node;
|
||||||
|
Actor* result = nullptr;
|
||||||
|
for (Actor* child : node->Children)
|
||||||
|
{
|
||||||
|
result = FindActorRecursiveByType(child, type, tag);
|
||||||
|
if (result)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindActorsRecursive(Actor* node, const Tag& tag, const bool activeOnly, Array<Actor*>& result)
|
||||||
|
{
|
||||||
|
if (activeOnly && !node->GetIsActive())
|
||||||
|
return;
|
||||||
|
if (node->HasTag(tag))
|
||||||
|
result.Add(node);
|
||||||
|
for (Actor* child : node->Children)
|
||||||
|
FindActorsRecursive(child, tag, activeOnly, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindActorsRecursiveByParentTags(Actor* node, const Array<Tag>& tags, const bool activeOnly, Array<Actor*>& result)
|
||||||
|
{
|
||||||
|
if (activeOnly && !node->GetIsActive())
|
||||||
|
return;
|
||||||
|
for (Tag tag : tags)
|
||||||
|
{
|
||||||
|
if (node->HasTag(tag))
|
||||||
|
{
|
||||||
|
result.Add(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Actor* child : node->Children)
|
||||||
|
FindActorsRecursiveByParentTags(child, tags, activeOnly, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor* Level::FindActor(const Tag& tag, Actor* root)
|
||||||
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
|
if (root)
|
||||||
|
return FindActorRecursive(root, tag);
|
||||||
|
Actor* result = nullptr;
|
||||||
|
for (Scene* scene : Scenes)
|
||||||
|
{
|
||||||
|
result = FindActorRecursive(scene, tag);
|
||||||
|
if (result)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor* Level::FindActor(const MClass* type, const Tag& tag, Actor* root)
|
||||||
|
{
|
||||||
|
CHECK_RETURN(type, nullptr);
|
||||||
|
if (root)
|
||||||
|
return FindActorRecursiveByType(root, type, tag);
|
||||||
|
Actor* result = nullptr;
|
||||||
|
ScopeLock lock(ScenesLock);
|
||||||
|
for (int32 i = 0; result == nullptr && i < Scenes.Count(); i++)
|
||||||
|
result = Scenes[i]->FindActor(type, tag);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FindActorRecursive(Actor* node, const Tag& tag, Array<Actor*>& result)
|
||||||
|
{
|
||||||
|
if (node->HasTag(tag))
|
||||||
|
result.Add(node);
|
||||||
|
for (Actor* child : node->Children)
|
||||||
|
FindActorRecursive(child, tag, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<Actor*> Level::FindActors(const Tag& tag, const bool activeOnly, Actor* root)
|
||||||
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
|
Array<Actor*> result;
|
||||||
|
if (root)
|
||||||
|
{
|
||||||
|
FindActorsRecursive(root, tag, activeOnly, result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ScopeLock lock(ScenesLock);
|
||||||
|
for (Scene* scene : Scenes)
|
||||||
|
FindActorsRecursive(scene, tag, activeOnly, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<Actor*> Level::FindActorsByParentTag(const Tag& parentTag, const bool activeOnly, Actor* root)
|
||||||
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
|
Array<Actor*> result;
|
||||||
|
const Array<Tag> subTags = Tags::GetSubTags(parentTag);
|
||||||
|
|
||||||
|
if (subTags.Count() == 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (subTags.Count() == 1)
|
||||||
|
{
|
||||||
|
result = FindActors(subTags[0], activeOnly, root);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root)
|
||||||
|
{
|
||||||
|
FindActorsRecursiveByParentTags(root, subTags, activeOnly, result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ScopeLock lock(ScenesLock);
|
||||||
|
for (Scene* scene : Scenes)
|
||||||
|
FindActorsRecursiveByParentTags(scene, subTags, activeOnly, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Script* Level::FindScript(const MClass* type)
|
Script* Level::FindScript(const MClass* type)
|
||||||
{
|
{
|
||||||
CHECK_RETURN(type, nullptr);
|
CHECK_RETURN(type, nullptr);
|
||||||
|
|||||||
@@ -78,6 +78,18 @@ namespace FlaxEngine
|
|||||||
return FindActor(typeof(T), name) as T;
|
return FindActor(typeof(T), name) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find actor of the given type and tag in a root actor or all loaded scenes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">A tag on the object.</param>
|
||||||
|
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||||
|
/// <typeparam name="T">Type of the object.</typeparam>
|
||||||
|
/// <returns>Found actor or null.</returns>
|
||||||
|
public static T FindActor<T>(Tag tag, Actor root = null) where T : Actor
|
||||||
|
{
|
||||||
|
return FindActor(typeof(T), tag, root) as T;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find actor with the given ID in all loaded scenes. It's very fast O(1) lookup.
|
/// Tries to find actor with the given ID in all loaded scenes. It's very fast O(1) lookup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -371,6 +371,41 @@ public:
|
|||||||
/// <returns>Actor instance if found, null otherwise.</returns>
|
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||||
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name);
|
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find the actor with the given tag (returns the first one found).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">The tag of the actor to search for.</param>
|
||||||
|
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||||
|
/// <returns>Found actor or null.</returns>
|
||||||
|
API_FUNCTION() static Actor* FindActor(const Tag& tag, Actor* root = nullptr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find the actor of the given type and tag in all the loaded scenes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Type of the actor to search for. Includes any actors derived from the type.</param>
|
||||||
|
/// <param name="tag">The tag of the actor to search for.</param>
|
||||||
|
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||||
|
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||||
|
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const Tag& tag, Actor* root = nullptr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find the actors with the given tag (returns all found).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">The tag of the actor to search for.</param>
|
||||||
|
/// <param name="activeOnly">Find only active actors.</param>
|
||||||
|
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||||
|
/// <returns>Found actors or empty if none.</returns>
|
||||||
|
API_FUNCTION() static Array<Actor*> FindActors(const Tag& tag, const bool activeOnly = false, Actor* root = nullptr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search actors using a parent parentTag.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parentTag">The tag to search actors with subtags belonging to this tag</param>
|
||||||
|
/// <param name="activeOnly">Find only active actors.</param>
|
||||||
|
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||||
|
/// <returns>Returns all actors that have subtags belonging to the given parent parentTag</returns>
|
||||||
|
API_FUNCTION() static Array<Actor*> FindActorsByParentTag(const Tag& parentTag, const bool activeOnly = false, Actor* root = nullptr);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find the actor of the given type in all the loaded scenes.
|
/// Tries to find the actor of the given type in all the loaded scenes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -392,6 +427,18 @@ public:
|
|||||||
return (T*)FindActor(T::GetStaticClass(), name);
|
return (T*)FindActor(T::GetStaticClass(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to find the actor of the given type and tag in a root actor or all the loaded scenes.
|
||||||
|
/// <param name="tag">The tag of the actor to search for.</param>
|
||||||
|
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||||
|
template<typename T>
|
||||||
|
FORCE_INLINE static T* FindActor(const Tag& tag, Actor* root = nullptr)
|
||||||
|
{
|
||||||
|
return (T*)FindActor(T::GetStaticClass(), tag, root);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find the script of the given type in all the loaded scenes.
|
/// Tries to find the script of the given type in all the loaded scenes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -481,33 +528,6 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_FUNCTION() static int32 GetLayerIndex(const StringView& layer);
|
API_FUNCTION() static int32 GetLayerIndex(const StringView& layer);
|
||||||
|
|
||||||
public:
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to find the actor with the given tag (returns the first one found).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">The tag of the actor to search for.</param>
|
|
||||||
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
|
||||||
/// <returns>Found actor or null.</returns>
|
|
||||||
API_FUNCTION() static Actor* FindActor(const Tag& tag, Actor* root = nullptr);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to find the actors with the given tag (returns all found).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tag">The tag of the actor to search for.</param>
|
|
||||||
/// <param name="activeOnly">Find only active actors.</param>
|
|
||||||
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
|
||||||
/// <returns>Found actors or empty if none.</returns>
|
|
||||||
API_FUNCTION() static Array<Actor*> FindActors(const Tag& tag, const bool activeOnly = false, Actor* root = nullptr);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Search actors using a parent parentTag.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parentTag">The tag to search actors with subtags belonging to this tag</param>
|
|
||||||
/// <param name="activeOnly">Find only active actors.</param>
|
|
||||||
/// <param name="root">The custom root actor to start searching from (hierarchical), otherwise null to search all loaded scenes.</param>
|
|
||||||
/// <returns>Returns all actors that have subtags belonging to the given parent parentTag</returns>
|
|
||||||
API_FUNCTION() static Array<Actor*> FindActorsByParentTag(const Tag& parentTag, const bool activeOnly = false, Actor* root = nullptr);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Actor API
|
// Actor API
|
||||||
enum class ActorEventType
|
enum class ActorEventType
|
||||||
|
|||||||
@@ -96,6 +96,16 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
|
|||||||
physicsDebugData[i](view);
|
physicsDebugData[i](view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw light shapes
|
||||||
|
if (EnumHasAnyFlags(view.Flags, ViewFlags::LightsDebug))
|
||||||
|
{
|
||||||
|
const LightsDebugCallback* lightsDebugData = LightsDebug.Get();
|
||||||
|
for (int32 i = 0; i < LightsDebug.Count(); i++)
|
||||||
|
{
|
||||||
|
lightsDebugData[i](view);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -157,30 +167,34 @@ void SceneRendering::UpdateActor(Actor* a, int32& key)
|
|||||||
const int32 category = a->_drawCategory;
|
const int32 category = a->_drawCategory;
|
||||||
ScopeLock lock(Locker);
|
ScopeLock lock(Locker);
|
||||||
auto& list = Actors[category];
|
auto& list = Actors[category];
|
||||||
if (list.IsEmpty())
|
if (list.Count() <= key) // Ignore invalid key softly
|
||||||
return;
|
return;
|
||||||
auto& e = list[key];
|
auto& e = list[key];
|
||||||
ASSERT_LOW_LAYER(a == e.Actor);
|
if (e.Actor == a)
|
||||||
|
{
|
||||||
for (auto* listener : _listeners)
|
for (auto* listener : _listeners)
|
||||||
listener->OnSceneRenderingUpdateActor(a, e.Bounds);
|
listener->OnSceneRenderingUpdateActor(a, e.Bounds);
|
||||||
e.LayerMask = a->GetLayerMask();
|
e.LayerMask = a->GetLayerMask();
|
||||||
e.Bounds = a->GetSphere();
|
e.Bounds = a->GetSphere();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SceneRendering::RemoveActor(Actor* a, int32& key)
|
void SceneRendering::RemoveActor(Actor* a, int32& key)
|
||||||
{
|
{
|
||||||
const int32 category = a->_drawCategory;
|
const int32 category = a->_drawCategory;
|
||||||
ScopeLock lock(Locker);
|
ScopeLock lock(Locker);
|
||||||
auto& list = Actors[category];
|
auto& list = Actors[category];
|
||||||
if (list.HasItems())
|
if (list.Count() > key) // Ignore invalid key softly (eg. list after batch clear during scene unload)
|
||||||
|
{
|
||||||
|
auto& e = list.Get()[key];
|
||||||
|
if (e.Actor == a)
|
||||||
{
|
{
|
||||||
auto& e = list[key];
|
|
||||||
ASSERT_LOW_LAYER(a == e.Actor);
|
|
||||||
for (auto* listener : _listeners)
|
for (auto* listener : _listeners)
|
||||||
listener->OnSceneRenderingRemoveActor(a);
|
listener->OnSceneRenderingRemoveActor(a);
|
||||||
e.Actor = nullptr;
|
e.Actor = nullptr;
|
||||||
e.LayerMask = 0;
|
e.LayerMask = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
key = -1;
|
key = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class FLAXENGINE_API SceneRendering
|
|||||||
{
|
{
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
typedef Function<void(RenderView&)> PhysicsDebugCallback;
|
typedef Function<void(RenderView&)> PhysicsDebugCallback;
|
||||||
|
typedef Function<void(RenderView&)> LightsDebugCallback;
|
||||||
friend class ViewportIconsRendererService;
|
friend class ViewportIconsRendererService;
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
@@ -95,6 +96,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
Array<PhysicsDebugCallback> PhysicsDebug;
|
Array<PhysicsDebugCallback> PhysicsDebug;
|
||||||
|
Array<LightsDebugCallback> LightsDebug;
|
||||||
Array<Actor*> ViewportIcons;
|
Array<Actor*> ViewportIcons;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -153,6 +155,22 @@ public:
|
|||||||
PhysicsDebug.Remove(f);
|
PhysicsDebug.Remove(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T, void(T::*Method)(RenderView&)>
|
||||||
|
FORCE_INLINE void AddLightsDebug(T* obj)
|
||||||
|
{
|
||||||
|
LightsDebugCallback f;
|
||||||
|
f.Bind<T, Method>(obj);
|
||||||
|
LightsDebug.Add(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, void(T::*Method)(RenderView&)>
|
||||||
|
void RemoveLightsDebug(T* obj)
|
||||||
|
{
|
||||||
|
LightsDebugCallback f;
|
||||||
|
f.Bind<T, Method>(obj);
|
||||||
|
LightsDebug.Remove(f);
|
||||||
|
}
|
||||||
|
|
||||||
FORCE_INLINE void AddViewportIcon(Actor* obj)
|
FORCE_INLINE void AddViewportIcon(Actor* obj)
|
||||||
{
|
{
|
||||||
ViewportIcons.Add(obj);
|
ViewportIcons.Add(obj);
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ namespace FlaxEngine
|
|||||||
if (_keyframes == null || _keyframes.Length != count)
|
if (_keyframes == null || _keyframes.Length != count)
|
||||||
_keyframes = new BezierCurve<Transform>.Keyframe[count];
|
_keyframes = new BezierCurve<Transform>.Keyframe[count];
|
||||||
#if !BUILD_RELEASE
|
#if !BUILD_RELEASE
|
||||||
if (Marshal.SizeOf(typeof(BezierCurve<Transform>.Keyframe)) != Transform.SizeInBytes * 3 + sizeof(float))
|
if (System.Runtime.CompilerServices.Unsafe.SizeOf<BezierCurve<Transform>.Keyframe>() != Transform.SizeInBytes * 3 + sizeof(float))
|
||||||
throw new Exception("Invalid size of BezierCurve keyframe " + Marshal.SizeOf(typeof(BezierCurve<Transform>.Keyframe)) + " bytes.");
|
throw new Exception("Invalid size of BezierCurve keyframe " + System.Runtime.CompilerServices.Unsafe.SizeOf<BezierCurve<Transform>.Keyframe>() + " bytes.");
|
||||||
#endif
|
#endif
|
||||||
Internal_GetKeyframes(__unmanagedPtr, _keyframes);
|
Internal_GetKeyframes(__unmanagedPtr, _keyframes);
|
||||||
return _keyframes;
|
return _keyframes;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ void NetworkReplicationHierarchyUpdateResult::Init()
|
|||||||
{
|
{
|
||||||
_clientsHaveLocation = false;
|
_clientsHaveLocation = false;
|
||||||
_clients.Resize(NetworkManager::Clients.Count());
|
_clients.Resize(NetworkManager::Clients.Count());
|
||||||
_clientsMask = NetworkClientsMask();
|
_clientsMask = NetworkManager::Mode == NetworkManagerMode::Client ? NetworkClientsMask::All : NetworkClientsMask();
|
||||||
for (int32 i = 0; i < _clients.Count(); i++)
|
for (int32 i = 0; i < _clients.Count(); i++)
|
||||||
_clientsMask.SetBit(i);
|
_clientsMask.SetBit(i);
|
||||||
_entries.Clear();
|
_entries.Clear();
|
||||||
@@ -63,6 +63,17 @@ bool NetworkReplicationNode::RemoveObject(ScriptingObject* obj)
|
|||||||
return !Objects.Remove(obj);
|
return !Objects.Remove(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NetworkReplicationNode::GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result)
|
||||||
|
{
|
||||||
|
const int32 index = Objects.Find(obj);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
result = Objects[index];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool NetworkReplicationNode::DirtyObject(ScriptingObject* obj)
|
bool NetworkReplicationNode::DirtyObject(ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
const int32 index = Objects.Find(obj);
|
const int32 index = Objects.Find(obj);
|
||||||
@@ -156,6 +167,7 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj
|
|||||||
cell->MinCullDistance = obj.CullDistance;
|
cell->MinCullDistance = obj.CullDistance;
|
||||||
}
|
}
|
||||||
cell->Node->AddObject(obj);
|
cell->Node->AddObject(obj);
|
||||||
|
_objectToCell[obj.Object] = coord;
|
||||||
|
|
||||||
// Cache minimum culling distance for a whole cell to skip it at once
|
// Cache minimum culling distance for a whole cell to skip it at once
|
||||||
cell->MinCullDistance = Math::Min(cell->MinCullDistance, obj.CullDistance);
|
cell->MinCullDistance = Math::Min(cell->MinCullDistance, obj.CullDistance);
|
||||||
@@ -163,14 +175,35 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj
|
|||||||
|
|
||||||
bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj)
|
bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj)
|
||||||
{
|
{
|
||||||
for (const auto& e : _children)
|
Int3 coord;
|
||||||
|
|
||||||
|
if (!_objectToCell.TryGet(obj, coord))
|
||||||
{
|
{
|
||||||
if (e.Value.Node->RemoveObject(obj))
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_children[coord].Node->RemoveObject(obj))
|
||||||
{
|
{
|
||||||
|
_objectToCell.Remove(obj);
|
||||||
// TODO: remove empty cells?
|
// TODO: remove empty cells?
|
||||||
// TODO: update MinCullDistance for cell?
|
// TODO: update MinCullDistance for cell?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkReplicationGridNode::GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result)
|
||||||
|
{
|
||||||
|
Int3 coord;
|
||||||
|
|
||||||
|
if (!_objectToCell.TryGet(obj, coord))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_children[coord].Node->GetObject(obj, result))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,6 +200,14 @@ API_CLASS(Abstract, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API Ne
|
|||||||
/// <returns>True on successful removal, otherwise false.</returns>
|
/// <returns>True on successful removal, otherwise false.</returns>
|
||||||
API_FUNCTION() virtual bool RemoveObject(ScriptingObject* obj);
|
API_FUNCTION() virtual bool RemoveObject(ScriptingObject* obj);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets object from the hierarchy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object to get.</param>
|
||||||
|
/// <param name="result">The hierarchy object to retrieve.</param>
|
||||||
|
/// <returns>True on successful retrieval, otherwise false.</returns>
|
||||||
|
API_FUNCTION() virtual bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization.
|
/// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -238,6 +246,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
Dictionary<Int3, Cell> _children;
|
Dictionary<Int3, Cell> _children;
|
||||||
|
Dictionary<ScriptingObject*, Int3> _objectToCell;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -247,6 +256,7 @@ public:
|
|||||||
|
|
||||||
void AddObject(NetworkReplicationHierarchyObject obj) override;
|
void AddObject(NetworkReplicationHierarchyObject obj) override;
|
||||||
bool RemoveObject(ScriptingObject* obj) override;
|
bool RemoveObject(ScriptingObject* obj) override;
|
||||||
|
bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result) override;
|
||||||
void Update(NetworkReplicationHierarchyUpdateResult* result) override;
|
void Update(NetworkReplicationHierarchyUpdateResult* result) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -700,9 +700,9 @@ void NetworkReplicator::AddRPC(const ScriptingTypeHandle& typeHandle, const Stri
|
|||||||
NetworkRpcInfo::RPCsTable[rpcName] = rpcInfo;
|
NetworkRpcInfo::RPCsTable[rpcName] = rpcInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkReplicator::CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds)
|
bool NetworkReplicator::CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds)
|
||||||
{
|
{
|
||||||
EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan<uint32>(targetIds));
|
return EndInvokeRPC(obj, type, GetCSharpCachedName(name), argsStream, MUtils::ToSpan<uint32>(targetIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name)
|
StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name)
|
||||||
@@ -1113,12 +1113,12 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
|
|||||||
return CachedWriteStream;
|
return CachedWriteStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
|
bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
|
||||||
{
|
{
|
||||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||||
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
||||||
if (!info || !obj || NetworkManager::IsOffline())
|
if (!info || !obj || NetworkManager::IsOffline())
|
||||||
return;
|
return false;
|
||||||
ObjectsLock.Lock();
|
ObjectsLock.Lock();
|
||||||
auto& rpc = RpcQueue.AddOne();
|
auto& rpc = RpcQueue.AddOne();
|
||||||
rpc.Object = obj;
|
rpc.Object = obj;
|
||||||
@@ -1135,12 +1135,23 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
ObjectsLock.Unlock();
|
ObjectsLock.Unlock();
|
||||||
|
|
||||||
|
// Check if skip local execution (eg. server rpc called from client or client rpc with specific targets)
|
||||||
|
const NetworkManagerMode networkMode = NetworkManager::Mode;
|
||||||
|
if (info->Server && networkMode == NetworkManagerMode::Client)
|
||||||
|
return true;
|
||||||
|
if (info->Client && networkMode == NetworkManagerMode::Server)
|
||||||
|
return true;
|
||||||
|
if (info->Client && networkMode == NetworkManagerMode::Host && targetIds.IsValid() && !SpanContains(targetIds, NetworkManager::LocalClientId))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client)
|
||||||
{
|
{
|
||||||
ScopeLock lock(ObjectsLock);
|
ScopeLock lock(ObjectsLock);
|
||||||
NewClients.Add(client);
|
NewClients.Add(client);
|
||||||
|
ASSERT(sizeof(NetworkClientsMask) * 8 >= (uint32)NetworkManager::Clients.Count()); // Ensure that clients mask can hold all of clients
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkInternal::NetworkReplicatorClientDisconnected(NetworkClient* client)
|
void NetworkInternal::NetworkReplicatorClientDisconnected(NetworkClient* client)
|
||||||
@@ -1193,6 +1204,7 @@ void NetworkInternal::NetworkReplicatorClear()
|
|||||||
Objects.Remove(it);
|
Objects.Remove(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Objects.Clear();
|
||||||
RpcQueue.Clear();
|
RpcQueue.Clear();
|
||||||
SpawnQueue.Clear();
|
SpawnQueue.Clear();
|
||||||
DespawnQueue.Clear();
|
DespawnQueue.Clear();
|
||||||
|
|||||||
@@ -120,10 +120,11 @@ namespace FlaxEngine.Networking
|
|||||||
/// <param name="name">The RPC name.</param>
|
/// <param name="name">The RPC name.</param>
|
||||||
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
|
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
|
||||||
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
|
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
|
||||||
|
/// <returns>True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).</returns>
|
||||||
[Unmanaged]
|
[Unmanaged]
|
||||||
public static void EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream, uint[] targetIds = null)
|
public static bool EndInvokeRPC(Object obj, Type type, string name, NetworkStream argsStream, uint[] targetIds = null)
|
||||||
{
|
{
|
||||||
Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream), targetIds);
|
return Internal_CSharpEndInvokeRPC(FlaxEngine.Object.GetUnmanagedPtr(obj), type, name, FlaxEngine.Object.GetUnmanagedPtr(argsStream), targetIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -195,13 +195,14 @@ public:
|
|||||||
/// <param name="name">The RPC name.</param>
|
/// <param name="name">The RPC name.</param>
|
||||||
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
|
/// <param name="argsStream">The RPC serialized arguments stream returned from BeginInvokeRPC.</param>
|
||||||
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
|
/// <param name="targetIds">Optional list with network client IDs that should receive RPC. Empty to send on all clients. Ignored by Server RPCs.</param>
|
||||||
static void EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds = Span<uint32>());
|
/// <returns>True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).</returns>
|
||||||
|
static bool EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds = Span<uint32>());
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if !COMPILE_WITHOUT_CSHARP
|
#if !COMPILE_WITHOUT_CSHARP
|
||||||
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize);
|
API_FUNCTION(NoProxy) static void AddSerializer(const ScriptingTypeHandle& typeHandle, const Function<void(void*, void*)>& serialize, const Function<void(void*, void*)>& deserialize);
|
||||||
API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function<void(void*, void*)>& execute, bool isServer, bool isClient, NetworkChannelType channel);
|
API_FUNCTION(NoProxy) static void AddRPC(const ScriptingTypeHandle& typeHandle, const StringAnsiView& name, const Function<void(void*, void*)>& execute, bool isServer, bool isClient, NetworkChannelType channel);
|
||||||
API_FUNCTION(NoProxy) static void CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds);
|
API_FUNCTION(NoProxy) static bool CSharpEndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, MArray* targetIds);
|
||||||
static StringAnsiView GetCSharpCachedName(const StringAnsiView& name);
|
static StringAnsiView GetCSharpCachedName(const StringAnsiView& name);
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct FLAXENGINE_API NetworkRpcInfo
|
|||||||
uint8 Client : 1;
|
uint8 Client : 1;
|
||||||
uint8 Channel : 4;
|
uint8 Channel : 4;
|
||||||
void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag);
|
void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag);
|
||||||
void (*Invoke)(ScriptingObject* obj, void** args);
|
bool (*Invoke)(ScriptingObject* obj, void** args);
|
||||||
void* Tag;
|
void* Tag;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -83,10 +83,7 @@ FORCE_INLINE void NetworkRpcInitArg(Array<void*, FixedAllocation<16>>& args, con
|
|||||||
{ \
|
{ \
|
||||||
Array<void*, FixedAllocation<16>> args; \
|
Array<void*, FixedAllocation<16>> args; \
|
||||||
NetworkRpcInitArg(args, __VA_ARGS__); \
|
NetworkRpcInitArg(args, __VA_ARGS__); \
|
||||||
rpcInfo.Invoke(this, args.Get()); \
|
if (rpcInfo.Invoke(this, args.Get())) \
|
||||||
if (rpcInfo.Server && networkMode == NetworkManagerMode::Client) \
|
|
||||||
return; \
|
|
||||||
if (rpcInfo.Client && networkMode == NetworkManagerMode::Server) \
|
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va
|
|||||||
const Matrix viewProjection = context.ViewTask ? context.ViewTask->View.PrevViewProjection : Matrix::Identity;
|
const Matrix viewProjection = context.ViewTask ? context.ViewTask->View.PrevViewProjection : Matrix::Identity;
|
||||||
const Float3 position = (Float3)TryGetValue(node->GetBox(0), Value::Zero);
|
const Float3 position = (Float3)TryGetValue(node->GetBox(0), Value::Zero);
|
||||||
Float4 projPos;
|
Float4 projPos;
|
||||||
Float3::Transform(position, viewProjection);
|
Float3::Transform(position, viewProjection, projPos);
|
||||||
projPos /= projPos.W;
|
projPos /= projPos.W;
|
||||||
value = Float2(projPos.X * 0.5f + 0.5f, projPos.Y * 0.5f + 0.5f);
|
value = Float2(projPos.X * 0.5f + 0.5f, projPos.Y * 0.5f + 0.5f);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -230,10 +230,11 @@ void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
|
|||||||
{
|
{
|
||||||
const Vector3 currentPos = wheel.Collider->GetPosition();
|
const Vector3 currentPos = wheel.Collider->GetPosition();
|
||||||
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
||||||
|
const Quaternion wheelDebugOrientation = GetOrientation() * Quaternion::Euler(-data.State.RotationAngle, data.State.SteerAngle, 0) * Quaternion::Euler(90, 0, 90);
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, true);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(basePos, wheel.Radius * 0.07f), Color::Blue * 0.3f, 0, true);
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, true);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, true);
|
||||||
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, true);
|
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, true);
|
||||||
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.8f, 0, true);
|
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheelDebugOrientation, wheel.Radius, wheel.Width, Color::Red * 0.8f, 0, true);
|
||||||
if (!data.State.IsInAir)
|
if (!data.State.IsInAir)
|
||||||
{
|
{
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.TireContactPoint, 5.0f), Color::Green, 0, true);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.TireContactPoint, 5.0f), Color::Green, 0, true);
|
||||||
@@ -260,6 +261,7 @@ void WheeledVehicle::OnDebugDrawSelected()
|
|||||||
{
|
{
|
||||||
const Vector3 currentPos = wheel.Collider->GetPosition();
|
const Vector3 currentPos = wheel.Collider->GetPosition();
|
||||||
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
const Vector3 basePos = currentPos - Vector3(0, data.State.SuspensionOffset, 0);
|
||||||
|
const Quaternion wheelDebugOrientation = GetOrientation() * Quaternion::Euler(-data.State.RotationAngle, data.State.SteerAngle, 0) * Quaternion::Euler(90, 0, 90);
|
||||||
Transform actorPose = Transform::Identity, shapePose = Transform::Identity;
|
Transform actorPose = Transform::Identity, shapePose = Transform::Identity;
|
||||||
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
|
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.Orientation);
|
||||||
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
|
PhysicsBackend::GetShapeLocalPose(wheel.Collider->GetPhysicsShape(), shapePose.Translation, shapePose.Orientation);
|
||||||
@@ -267,7 +269,7 @@ void WheeledVehicle::OnDebugDrawSelected()
|
|||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, false);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 0, false);
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(actorPose.LocalToWorld(shapePose.Translation), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(actorPose.LocalToWorld(shapePose.Translation), wheel.Radius * 0.11f), Color::OrangeRed * 0.8f, 0, false);
|
||||||
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, false);
|
DEBUG_DRAW_LINE(basePos, currentPos, Color::Blue, 0, false);
|
||||||
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheel.Collider->GetOrientation(), wheel.Radius, wheel.Width, Color::Red * 0.4f, 0, false);
|
DEBUG_DRAW_WIRE_CYLINDER(currentPos, wheelDebugOrientation, wheel.Radius, wheel.Width, Color::Red * 0.4f, 0, false);
|
||||||
if (!data.State.SuspensionTraceStart.IsZero())
|
if (!data.State.SuspensionTraceStart.IsZero())
|
||||||
{
|
{
|
||||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.SuspensionTraceStart, 5.0f), Color::AliceBlue, 0, false);
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.SuspensionTraceStart, 5.0f), Color::AliceBlue, 0, false);
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ WindowBase::~WindowBase()
|
|||||||
{
|
{
|
||||||
ASSERT(!RenderTask);
|
ASSERT(!RenderTask);
|
||||||
ASSERT(!_swapChain);
|
ASSERT(!_swapChain);
|
||||||
|
WindowsManager::Unregister((Window*)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowBase::IsMain() const
|
bool WindowBase::IsMain() const
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user