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": {
|
||||
"Major": 1,
|
||||
"Minor": 6,
|
||||
"Build": 6342
|
||||
"Build": 6344
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace FlaxEditor.Content
|
||||
if (asset)
|
||||
{
|
||||
var source = Editor.GetShaderSourceCode(asset);
|
||||
Utilities.Utils.ShowSourceCodeWindow(source, "Shader Source", item.RootWindow.Window);
|
||||
Utilities.Utils.ShowSourceCodeWindow(source, "Shader Source", item.RootWindow?.Window);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine;
|
||||
using DockState = FlaxEditor.GUI.Docking.DockState;
|
||||
|
||||
namespace FlaxEditor
|
||||
@@ -86,8 +85,12 @@ namespace FlaxEditor
|
||||
if (!FlaxEngine.Scripting.IsTypeFromGameScripts(type))
|
||||
return;
|
||||
|
||||
Editor.Instance.Windows.AddToRestore(this);
|
||||
if (!Window.IsHidden)
|
||||
{
|
||||
Editor.Instance.Windows.AddToRestore(this);
|
||||
}
|
||||
Window.Close();
|
||||
Window.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
Presenter.Undo?.AddAction(new MultiUndoAction(actions));
|
||||
|
||||
// Build ragdoll
|
||||
SceneGraph.Actors.AnimatedModelNode.BuildRagdoll(animatedModel, options, ragdoll);
|
||||
AnimatedModelNode.BuildRagdoll(animatedModel, options, ragdoll);
|
||||
}
|
||||
|
||||
private void OnRebuildBone(Button button)
|
||||
@@ -191,7 +191,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -33,7 +33,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
[CustomEditor(typeof(Asset)), DefaultEditor]
|
||||
public class AssetRefEditor : CustomEditor
|
||||
{
|
||||
private AssetPicker _picker;
|
||||
/// <summary>
|
||||
/// The asset picker used to get a reference to an asset.
|
||||
/// </summary>
|
||||
public AssetPicker Picker;
|
||||
|
||||
private bool _isRefreshing;
|
||||
private ScriptType _valueType;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -44,7 +49,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (HasDifferentTypes)
|
||||
return;
|
||||
_picker = layout.Custom<AssetPicker>().CustomControl;
|
||||
Picker = layout.Custom<AssetPicker>().CustomControl;
|
||||
|
||||
_valueType = Values.Type.Type != typeof(object) || Values[0] == null ? Values.Type : TypeUtils.GetObjectType(Values[0]);
|
||||
var assetType = _valueType;
|
||||
@@ -66,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
// Generic file picker
|
||||
assetType = ScriptType.Null;
|
||||
_picker.FileExtension = assetReference.TypeName;
|
||||
Picker.FileExtension = assetReference.TypeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -78,23 +83,25 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
}
|
||||
|
||||
_picker.AssetType = assetType;
|
||||
_picker.Height = height;
|
||||
_picker.SelectedItemChanged += OnSelectedItemChanged;
|
||||
Picker.AssetType = assetType;
|
||||
Picker.Height = height;
|
||||
Picker.SelectedItemChanged += OnSelectedItemChanged;
|
||||
}
|
||||
|
||||
private void OnSelectedItemChanged()
|
||||
{
|
||||
if (_isRefreshing)
|
||||
return;
|
||||
if (typeof(AssetItem).IsAssignableFrom(_valueType.Type))
|
||||
SetValue(_picker.SelectedItem);
|
||||
SetValue(Picker.SelectedItem);
|
||||
else if (_valueType.Type == typeof(Guid))
|
||||
SetValue(_picker.SelectedID);
|
||||
SetValue(Picker.SelectedID);
|
||||
else if (_valueType.Type == typeof(SceneReference))
|
||||
SetValue(new SceneReference(_picker.SelectedID));
|
||||
SetValue(new SceneReference(Picker.SelectedID));
|
||||
else if (_valueType.Type == typeof(string))
|
||||
SetValue(_picker.SelectedPath);
|
||||
SetValue(Picker.SelectedPath);
|
||||
else
|
||||
SetValue(_picker.SelectedAsset);
|
||||
SetValue(Picker.SelectedAsset);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -104,16 +111,18 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (!HasDifferentValues)
|
||||
{
|
||||
_isRefreshing = true;
|
||||
if (Values[0] is AssetItem assetItem)
|
||||
_picker.SelectedItem = assetItem;
|
||||
Picker.SelectedItem = assetItem;
|
||||
else if (Values[0] is Guid guid)
|
||||
_picker.SelectedID = guid;
|
||||
Picker.SelectedID = guid;
|
||||
else if (Values[0] is SceneReference sceneAsset)
|
||||
_picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
|
||||
Picker.SelectedItem = Editor.Instance.ContentDatabase.FindAsset(sceneAsset.ID);
|
||||
else if (Values[0] is string path)
|
||||
_picker.SelectedPath = path;
|
||||
Picker.SelectedPath = path;
|
||||
else
|
||||
_picker.SelectedAsset = Values[0] as Asset;
|
||||
Picker.SelectedAsset = Values[0] as Asset;
|
||||
_isRefreshing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
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.
|
||||
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
@@ -13,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
private GroupElement _group;
|
||||
private bool _updateName;
|
||||
private int _entryIndex;
|
||||
private bool _isRefreshing;
|
||||
private MaterialBase _material;
|
||||
private ModelInstanceActor _modelInstance;
|
||||
private AssetRefEditor _materialEditor;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -21,47 +28,122 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var group = layout.Group("Entry");
|
||||
_group = group;
|
||||
|
||||
if (ParentEditor == null)
|
||||
return;
|
||||
var entry = (ModelInstanceEntry)Values[0];
|
||||
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
|
||||
var materialLabel = new PropertyNameLabel("Material");
|
||||
materialLabel.TooltipText = "The mesh surface material used for the rendering.";
|
||||
if (ParentEditor.ParentEditor?.Values[0] is ModelInstanceActor modelInstance)
|
||||
{
|
||||
_entryIndex = entryIndex;
|
||||
_modelInstance = modelInstance;
|
||||
var slots = modelInstance.MaterialSlots;
|
||||
if (entry.Material == slots[entryIndex].Material)
|
||||
{
|
||||
// Ensure that entry with default material set is set back to null
|
||||
modelInstance.SetMaterial(entryIndex, null);
|
||||
}
|
||||
_material = modelInstance.GetMaterial(entryIndex);
|
||||
var defaultValue = GPUDevice.Instance.DefaultMaterial;
|
||||
if (slots[entryIndex].Material)
|
||||
{
|
||||
// Use default value set on asset (eg. Model Asset)
|
||||
defaultValue = slots[entryIndex].Material;
|
||||
}
|
||||
|
||||
// Create material picker
|
||||
var materialValue = new CustomValueContainer(new ScriptType(typeof(MaterialBase)), _material, (instance, index) => _material, (instance, index, value) => _material = value as MaterialBase);
|
||||
var materialEditor = (AssetRefEditor)_group.Property(materialLabel, materialValue);
|
||||
materialEditor.Values.SetDefaultValue(defaultValue);
|
||||
materialEditor.RefreshDefaultValue();
|
||||
materialEditor.Picker.SelectedItemChanged += OnSelectedMaterialChanged;
|
||||
_materialEditor = materialEditor;
|
||||
}
|
||||
|
||||
base.Initialize(group);
|
||||
}
|
||||
|
||||
private void OnSelectedMaterialChanged()
|
||||
{
|
||||
if (_isRefreshing)
|
||||
return;
|
||||
_isRefreshing = true;
|
||||
var slots = _modelInstance.MaterialSlots;
|
||||
var material = _materialEditor.Picker.SelectedAsset as MaterialBase;
|
||||
var defaultMaterial = GPUDevice.Instance.DefaultMaterial;
|
||||
var value = (ModelInstanceEntry)Values[0];
|
||||
var prevMaterial = value.Material;
|
||||
if (!material)
|
||||
{
|
||||
// Fallback to default material
|
||||
_materialEditor.Picker.SelectedAsset = defaultMaterial;
|
||||
value.Material = defaultMaterial;
|
||||
}
|
||||
else if (material == slots[_entryIndex].Material)
|
||||
{
|
||||
// Asset default material
|
||||
value.Material = null;
|
||||
}
|
||||
else if (material == defaultMaterial && !slots[_entryIndex].Material)
|
||||
{
|
||||
// Default material while asset has no set as well
|
||||
value.Material = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Custom material
|
||||
value.Material = material;
|
||||
}
|
||||
if (prevMaterial != value.Material)
|
||||
SetValue(value);
|
||||
_isRefreshing = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item)
|
||||
{
|
||||
// Skip material member as it is overridden
|
||||
if (item.Info.Name == "Material" && _materialEditor != null)
|
||||
return;
|
||||
base.SpawnProperty(itemLayout, itemValues, item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
// Update panel title to match material slot name
|
||||
if (_updateName &&
|
||||
_group != null &&
|
||||
ParentEditor?.ParentEditor != null &&
|
||||
ParentEditor.ParentEditor.Values.Count > 0)
|
||||
{
|
||||
var entryIndex = ParentEditor.ChildrenEditors.IndexOf(this);
|
||||
if (ParentEditor.ParentEditor.Values[0] is StaticModel staticModel)
|
||||
if (ParentEditor.ParentEditor.Values[0] is ModelInstanceActor modelInstance)
|
||||
{
|
||||
var model = staticModel.Model;
|
||||
if (model && model.IsLoaded)
|
||||
var slots = modelInstance.MaterialSlots;
|
||||
if (slots != null && slots.Length > entryIndex)
|
||||
{
|
||||
var slots = model.MaterialSlots;
|
||||
if (slots != null && slots.Length > entryIndex)
|
||||
{
|
||||
_group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
|
||||
_updateName = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel)
|
||||
{
|
||||
var model = animatedModel.SkinnedModel;
|
||||
if (model && model.IsLoaded)
|
||||
{
|
||||
var slots = model.MaterialSlots;
|
||||
if (slots != null && slots.Length > entryIndex)
|
||||
{
|
||||
_group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
|
||||
_updateName = false;
|
||||
}
|
||||
_updateName = false;
|
||||
_group.Panel.HeaderText = "Entry " + slots[entryIndex].Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh currently selected material
|
||||
_material = _modelInstance.GetMaterial(_entryIndex);
|
||||
|
||||
base.Refresh();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_material = null;
|
||||
_modelInstance = null;
|
||||
_materialEditor = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +390,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (addTagDropPanel.IsClosed)
|
||||
{
|
||||
Debug.Log("Hit");
|
||||
nameTextBox.BorderColor = Color.Transparent;
|
||||
nameTextBox.BorderSelectedColor = FlaxEngine.GUI.Style.Current.BackgroundSelected;
|
||||
return;
|
||||
|
||||
@@ -37,7 +37,8 @@ public class Editor : EditorModule
|
||||
{
|
||||
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.ComponentModel.TypeConverter");
|
||||
|
||||
@@ -101,5 +102,7 @@ public class Editor : EditorModule
|
||||
files.Add(Path.Combine(FolderPath, "Cooker/GameCooker.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Cooker/PlatformTools.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Cooker/Steps/CookAssetsStep.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Utilities/ScreenUtilities.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Utilities/ViewportIconsRenderer.h"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,17 @@ namespace FlaxEditor.GUI
|
||||
/// </summary>
|
||||
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>
|
||||
/// Initializes a new instance of the <see cref="TypeItemView"/> class.
|
||||
/// </summary>
|
||||
@@ -83,6 +94,7 @@ namespace FlaxEditor.GUI
|
||||
|
||||
// TODO: use async thread to search types without UI stall
|
||||
var allTypes = Editor.Instance.CodeEditing.All.Get();
|
||||
AddItem(new TypeItemView());
|
||||
for (int i = 0; i < allTypes.Count; i++)
|
||||
{
|
||||
var type = allTypes[i];
|
||||
|
||||
@@ -178,6 +178,8 @@ namespace FlaxEditor.Gizmo
|
||||
if (selection[i] is ActorNode actorNode && actorNode.Actor != null)
|
||||
CollectActors(actorNode.Actor);
|
||||
}
|
||||
if (_actors.Count == 0)
|
||||
return;
|
||||
|
||||
// Render selected objects depth
|
||||
Renderer.DrawSceneDepth(context, task, customDepth, _actors);
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace FlaxEditor
|
||||
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
|
||||
/// <param name="value">The value.</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)
|
||||
{
|
||||
@@ -44,7 +44,7 @@ namespace FlaxEditor
|
||||
/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
|
||||
/// <param name="serializer">The calling serializer.</param>
|
||||
/// <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)
|
||||
{
|
||||
|
||||
@@ -112,6 +112,7 @@ public:
|
||||
{
|
||||
Version = ::Version(1, 0);
|
||||
DefaultSceneSpawn = Ray(Vector3::Zero, Vector3::Forward);
|
||||
DefaultScene = Guid::Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -613,6 +613,28 @@ bool ScriptsBuilderService::Init()
|
||||
const String targetOutput = Globals::ProjectFolder / TEXT("Binaries") / target / platform / architecture / configuration;
|
||||
Array<String> files;
|
||||
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())
|
||||
LOG(Info, "Removing {0} files from previous Editor run hot-reloads", files.Count());
|
||||
for (auto& file : files)
|
||||
|
||||
@@ -363,7 +363,6 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
Profiler.BeginEvent("VisjectCM.RemoveGroup");
|
||||
if (group.Archetypes.Count == 0)
|
||||
{
|
||||
Debug.Log("Remove");
|
||||
_groups.RemoveAt(i);
|
||||
group.Dispose();
|
||||
}
|
||||
|
||||
@@ -292,7 +292,6 @@ void FoliageTools::Paint(Foliage* foliage, Span<int32> foliageTypesIndices, cons
|
||||
{
|
||||
PROFILE_CPU_NAMED("Place Instances");
|
||||
|
||||
Matrix matrix;
|
||||
FoliageInstance instance;
|
||||
Quaternion tmp;
|
||||
Matrix world;
|
||||
|
||||
@@ -1538,6 +1538,7 @@ namespace FlaxEditor.Viewport
|
||||
new ViewFlagOptions(ViewFlags.MotionBlur, "Motion Blur"),
|
||||
new ViewFlagOptions(ViewFlags.ContactShadows, "Contact Shadows"),
|
||||
new ViewFlagOptions(ViewFlags.PhysicsDebug, "Physics Debug"),
|
||||
new ViewFlagOptions(ViewFlags.LightsDebug, "Lights Debug"),
|
||||
new ViewFlagOptions(ViewFlags.DebugDraw, "Debug Draw"),
|
||||
};
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
using System;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEngine;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
@@ -15,11 +13,10 @@ namespace FlaxEditor.Viewport.Previews
|
||||
/// <seealso cref="AssetPreview" />
|
||||
public class AnimatedModelPreview : AssetPreview
|
||||
{
|
||||
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
|
||||
private bool _showNodes, _showBounds, _showFloor, _showCurrentLOD, _showNodesNames;
|
||||
private AnimatedModel _previewModel;
|
||||
private ContextMenuButton _showNodesButton, _showBoundsButton, _showFloorButton, _showNodesNamesButton;
|
||||
private bool _showNodes, _showBounds, _showFloor, _showNodesNames;
|
||||
private StaticModel _floorModel;
|
||||
private ContextMenuButton _showCurrentLODButton;
|
||||
private bool _playAnimation, _playAnimationOnce;
|
||||
private float _playSpeed = 1.0f;
|
||||
|
||||
@@ -188,8 +185,8 @@ namespace FlaxEditor.Viewport.Previews
|
||||
{
|
||||
UseTimeScale = false,
|
||||
UpdateWhenOffscreen = true,
|
||||
//_previewModel.BoundsScale = 1000.0f;
|
||||
UpdateMode = AnimatedModel.AnimationUpdateMode.Manual
|
||||
BoundsScale = 100.0f,
|
||||
UpdateMode = AnimatedModel.AnimationUpdateMode.Manual,
|
||||
};
|
||||
Task.AddCustomActor(_previewModel);
|
||||
|
||||
@@ -207,26 +204,6 @@ namespace FlaxEditor.Viewport.Previews
|
||||
// Show Floor
|
||||
_showFloorButton = ViewWidgetShowMenu.AddButton("Floor", button => ShowFloor = !ShowFloor);
|
||||
_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
|
||||
@@ -339,44 +316,6 @@ namespace FlaxEditor.Viewport.Previews
|
||||
_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 />
|
||||
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 />
|
||||
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 />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.F:
|
||||
// Pay respect..
|
||||
ViewportCamera.SetArcBallView(_previewModel.Box);
|
||||
ResetCamera();
|
||||
return true;
|
||||
case KeyboardKeys.Spacebar:
|
||||
PlayAnimation = !PlayAnimation;
|
||||
@@ -525,7 +432,6 @@ namespace FlaxEditor.Viewport.Previews
|
||||
_showNodesButton = null;
|
||||
_showBoundsButton = null;
|
||||
_showFloorButton = null;
|
||||
_showCurrentLODButton = null;
|
||||
_showNodesNamesButton = null;
|
||||
|
||||
base.OnDestroy();
|
||||
|
||||
@@ -4,6 +4,8 @@ using System;
|
||||
using FlaxEditor.Surface;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
@@ -46,6 +48,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
private int _selectedModelIndex;
|
||||
private Image _guiMaterialControl;
|
||||
private readonly MaterialBase[] _postFxMaterialsCache = new MaterialBase[1];
|
||||
private ContextMenu _modelWidgetButtonMenu;
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
|
||||
// Create context menu for primitive switching
|
||||
if (useWidgets && ViewWidgetButtonMenu != null)
|
||||
if (useWidgets)
|
||||
{
|
||||
ViewWidgetButtonMenu.AddSeparator();
|
||||
var modelSelect = ViewWidgetButtonMenu.AddChildMenu("Model").ContextMenu;
|
||||
|
||||
// Fill out all models
|
||||
for (int i = 0; i < Models.Length; i++)
|
||||
// Model mode widget
|
||||
var modelMode = new ViewportWidgetsContainer(ViewportWidgetLocation.UpperRight);
|
||||
_modelWidgetButtonMenu = new ContextMenu();
|
||||
_modelWidgetButtonMenu.VisibleChanged += control =>
|
||||
{
|
||||
var button = modelSelect.AddButton(Models[i]);
|
||||
button.Tag = i;
|
||||
}
|
||||
if (!control.Visible)
|
||||
return;
|
||||
_modelWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||
|
||||
// Link the action
|
||||
modelSelect.ButtonClicked += (button) => SelectedModelIndex = (int)button.Tag;
|
||||
// Fill out all models
|
||||
for (int i = 0; i < Models.Length; i++)
|
||||
{
|
||||
var index = i;
|
||||
var button = _modelWidgetButtonMenu.AddButton(Models[index]);
|
||||
button.ButtonClicked += _ => SelectedModelIndex = index;
|
||||
button.Checked = SelectedModelIndex == index;
|
||||
button.Tag = index;
|
||||
}
|
||||
};
|
||||
new ViewportWidgetButton("Model", SpriteHandle.Invalid, _modelWidgetButtonMenu)
|
||||
{
|
||||
TooltipText = "Change material model",
|
||||
Parent = modelMode,
|
||||
};
|
||||
modelMode.Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
@@ -56,25 +55,14 @@ namespace FlaxEditor.Viewport.Previews
|
||||
// Link actors for rendering
|
||||
Task.AddCustomActor(StaticModel);
|
||||
Task.AddCustomActor(AnimatedModel);
|
||||
}
|
||||
|
||||
if (useWidgets)
|
||||
{
|
||||
// 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 += () =>
|
||||
{
|
||||
StaticModel.ForcedLOD = previewLODValue.Value;
|
||||
AnimatedModel.ForcedLOD = previewLODValue.Value;
|
||||
};
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = StaticModel.ForcedLOD;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Resets the camera to focus on a object.
|
||||
/// </summary>
|
||||
public void ResetCamera()
|
||||
{
|
||||
ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
|
||||
}
|
||||
|
||||
private void OnBegin(RenderTask task, GPUContext context)
|
||||
@@ -103,8 +91,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.F:
|
||||
// Pay respect..
|
||||
ViewportCamera.SetArcBallView(StaticModel.Model != null ? StaticModel.Box : AnimatedModel.Box);
|
||||
ResetCamera();
|
||||
break;
|
||||
}
|
||||
return base.OnKeyDown(key);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
using FlaxEditor.Viewport.Widgets;
|
||||
using Object = FlaxEngine.Object;
|
||||
|
||||
namespace FlaxEditor.Viewport.Previews
|
||||
@@ -16,10 +16,27 @@ namespace FlaxEditor.Viewport.Previews
|
||||
public class ModelPreview : AssetPreview
|
||||
{
|
||||
private ContextMenuButton _showBoundsButton, _showCurrentLODButton, _showNormalsButton, _showTangentsButton, _showBitangentsButton, _showFloorButton;
|
||||
private ContextMenu _previewLODsWidgetButtonMenu;
|
||||
private StaticModel _previewModel, _floorModel;
|
||||
private bool _showBounds, _showCurrentLOD, _showNormals, _showTangents, _showBitangents, _showFloor;
|
||||
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>
|
||||
/// Gets or sets the model asset to preview.
|
||||
/// </summary>
|
||||
@@ -198,17 +215,36 @@ namespace FlaxEditor.Viewport.Previews
|
||||
});
|
||||
_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");
|
||||
previewLOD.CloseMenuOnClick = false;
|
||||
var previewLODValue = new IntValueBox(-1, 90, 2, 70.0f, -1, 10, 0.02f)
|
||||
if (!control.Visible)
|
||||
return;
|
||||
var model = _previewModel.Model;
|
||||
if (model && !model.WaitForLoaded())
|
||||
{
|
||||
Parent = previewLOD
|
||||
};
|
||||
previewLODValue.ValueChanged += () => _previewModel.ForcedLOD = previewLODValue.Value;
|
||||
ViewWidgetButtonMenu.VisibleChanged += control => previewLODValue.Value = _previewModel.ForcedLOD;
|
||||
}
|
||||
_previewLODsWidgetButtonMenu.ItemsContainer.DisposeChildren();
|
||||
var lods = model.LODs.Length;
|
||||
for (int i = -1; i < lods; i++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +348,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
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;
|
||||
@@ -346,8 +382,11 @@ namespace FlaxEditor.Viewport.Previews
|
||||
if (_showCurrentLOD)
|
||||
{
|
||||
var asset = Model;
|
||||
var lodIndex = ComputeLODIndex(asset, out var screenSize);
|
||||
string text = string.Format("Current LOD: {0}\nScreen Size: {1:F2}", lodIndex, screenSize);
|
||||
var lodIndex = ComputeLODIndex(asset, out var 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)
|
||||
{
|
||||
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 />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case KeyboardKeys.F:
|
||||
// Pay respect..
|
||||
ViewportCamera.SetArcBallView(_previewModel.Box);
|
||||
ResetCamera();
|
||||
break;
|
||||
}
|
||||
return base.OnKeyDown(key);
|
||||
@@ -389,6 +435,7 @@ namespace FlaxEditor.Viewport.Previews
|
||||
Object.Destroy(ref _previewModel);
|
||||
_showBoundsButton = null;
|
||||
_showCurrentLODButton = null;
|
||||
_previewLODsWidgetButtonMenu = null;
|
||||
_showNormalsButton = null;
|
||||
_showTangentsButton = 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.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");
|
||||
|
||||
// Split Panel
|
||||
|
||||
@@ -779,6 +779,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private MeshDataCache _meshData;
|
||||
private ModelImportSettings _importSettings = new ModelImportSettings();
|
||||
private float _backfacesThreshold = 0.6f;
|
||||
private ToolStripButton _showCurrentLODButton;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ModelWindow(Editor editor, AssetItem item)
|
||||
@@ -786,6 +787,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
// Toolstrip
|
||||
_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");
|
||||
|
||||
// Model preview
|
||||
@@ -869,6 +873,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
}
|
||||
|
||||
_showCurrentLODButton.Checked = _preview.ShowCurrentLOD;
|
||||
|
||||
base.Update(deltaTime);
|
||||
}
|
||||
|
||||
@@ -946,6 +952,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
base.OnDestroy();
|
||||
|
||||
Object.Destroy(ref _highlightActor);
|
||||
_showCurrentLODButton = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
||||
public sealed class SkinnedModelWindow : ModelBaseWindow<SkinnedModel, SkinnedModelWindow>
|
||||
{
|
||||
private sealed class Preview : AnimatedModelPreview
|
||||
private sealed class Preview : SkinnedModelPreview
|
||||
{
|
||||
private readonly SkinnedModelWindow _window;
|
||||
|
||||
@@ -1105,6 +1105,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
private Preview _preview;
|
||||
private AnimatedModel _highlightActor;
|
||||
private ToolStripButton _showNodesButton;
|
||||
private ToolStripButton _showCurrentLODButton;
|
||||
|
||||
private MeshData[][] _meshDatas;
|
||||
private bool _meshDatasInProgress;
|
||||
@@ -1116,7 +1117,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
// Toolstrip
|
||||
_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");
|
||||
_toolstrip.AddButton(editor.Icons.CenterView64, () => _preview.ResetCamera()).LinkTooltip("Show whole model");
|
||||
_toolstrip.AddSeparator();
|
||||
_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;
|
||||
|
||||
base.Update(deltaTime);
|
||||
@@ -1349,6 +1353,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
Object.Destroy(ref _highlightActor);
|
||||
_preview = null;
|
||||
_showNodesButton = null;
|
||||
_showCurrentLODButton = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace FlaxEditor.Windows
|
||||
public LogGroup Group;
|
||||
public LogEntryDescription Desc;
|
||||
public SpriteHandle Icon;
|
||||
public int LogCount = 1;
|
||||
|
||||
public LogEntry(DebugLogWindow window, ref LogEntryDescription desc)
|
||||
: base(0, 0, 120, DefaultHeight)
|
||||
@@ -137,7 +138,14 @@ namespace FlaxEditor.Windows
|
||||
// Title
|
||||
var textRect = new Rectangle(38, 2, clientRect.Width - 40, clientRect.Height - 10);
|
||||
Render2D.PushClip(ref clientRect);
|
||||
Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground);
|
||||
if (LogCount == 1)
|
||||
{
|
||||
Render2D.DrawText(style.FontMedium, Desc.Title, textRect, style.Foreground);
|
||||
}
|
||||
else if (LogCount > 1)
|
||||
{
|
||||
Render2D.DrawText(style.FontMedium, $"{Desc.Title} ({LogCount})", textRect, style.Foreground);
|
||||
}
|
||||
Render2D.PopClip();
|
||||
}
|
||||
|
||||
@@ -289,6 +297,7 @@ namespace FlaxEditor.Windows
|
||||
private readonly List<LogEntry> _pendingEntries = new List<LogEntry>(32);
|
||||
|
||||
private readonly ToolStripButton _clearOnPlayButton;
|
||||
private readonly ToolStripButton _collapseLogsButton;
|
||||
private readonly ToolStripButton _pauseOnErrorButton;
|
||||
private readonly ToolStripButton[] _groupButtons = new ToolStripButton[3];
|
||||
|
||||
@@ -316,6 +325,7 @@ namespace FlaxEditor.Windows
|
||||
};
|
||||
toolstrip.AddButton("Clear", Clear).LinkTooltip("Clears all log entries");
|
||||
_clearOnPlayButton = (ToolStripButton)toolstrip.AddButton("Clear on Play").SetAutoCheck(true).SetChecked(true).LinkTooltip("Clears all log entries on enter playmode");
|
||||
_collapseLogsButton = (ToolStripButton)toolstrip.AddButton("Collapse").SetAutoCheck(true).SetChecked(true).LinkTooltip("Collapses similar logs.");
|
||||
_pauseOnErrorButton = (ToolStripButton)toolstrip.AddButton("Pause on Error").SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
|
||||
toolstrip.AddSeparator();
|
||||
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides error messages");
|
||||
@@ -612,6 +622,30 @@ namespace FlaxEditor.Windows
|
||||
var top = _entriesPanel.Children.Count != 0 ? _entriesPanel.Children[_entriesPanel.Children.Count - 1].Bottom + spacing : margin.Top;
|
||||
for (int i = 0; i < _pendingEntries.Count; i++)
|
||||
{
|
||||
if (_collapseLogsButton.Checked)
|
||||
{
|
||||
bool logExists = false;
|
||||
foreach (var child in _entriesPanel.Children)
|
||||
{
|
||||
if (child is LogEntry entry)
|
||||
{
|
||||
var pendingEntry = _pendingEntries[i];
|
||||
if (string.Equals(entry.Desc.Title, pendingEntry.Desc.Title, StringComparison.Ordinal) &&
|
||||
string.Equals(entry.Desc.LocationFile, pendingEntry.Desc.LocationFile, StringComparison.Ordinal) &&
|
||||
entry.Desc.Level == pendingEntry.Desc.Level &&
|
||||
string.Equals(entry.Desc.Description, pendingEntry.Desc.Description, StringComparison.Ordinal) &&
|
||||
entry.Desc.LocationLine == pendingEntry.Desc.LocationLine)
|
||||
{
|
||||
entry.LogCount += 1;
|
||||
newEntry = entry;
|
||||
logExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logExists)
|
||||
continue;
|
||||
}
|
||||
newEntry = _pendingEntries[i];
|
||||
newEntry.Visible = _groupButtons[(int)newEntry.Group].Checked;
|
||||
anyVisible |= newEntry.Visible;
|
||||
|
||||
@@ -217,6 +217,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
const float animPrevPos = GetAnimSamplePos(length, anim, prevPos, speed);
|
||||
|
||||
// Evaluate nested animations
|
||||
bool hasNested = false;
|
||||
if (anim->NestedAnims.Count() != 0)
|
||||
{
|
||||
for (auto& e : anim->NestedAnims)
|
||||
@@ -239,6 +240,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
GetAnimSamplePos(nestedAnim.Loop, nestedAnimLength, nestedAnim.StartTime, nestedAnimPrevPos, nestedAnimPos, nestedAnimPos, nestedAnimPrevPos);
|
||||
|
||||
ProcessAnimation(nodes, node, true, nestedAnimLength, nestedAnimPos, nestedAnimPrevPos, nestedAnim.Anim, 1.0f, weight, mode);
|
||||
hasNested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,7 +293,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode*
|
||||
dstNode.Scale = srcNode.Scale * weight;
|
||||
dstNode.Orientation = srcNode.Orientation * weight;
|
||||
}
|
||||
else
|
||||
else if (!hasNested)
|
||||
{
|
||||
dstNode = srcNode;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ public:
|
||||
/// <summary>
|
||||
/// Returns true if material is an material instance.
|
||||
/// </summary>
|
||||
/// <returns>True if it's a material instance, otherwise false.</returns>
|
||||
virtual bool IsMaterialInstance() const = 0;
|
||||
|
||||
public:
|
||||
|
||||
@@ -618,7 +618,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
// Return
|
||||
case 5:
|
||||
{
|
||||
auto& scope = ThreadStacks.Get().Stack->Scope;
|
||||
auto scope = ThreadStacks.Get().Stack->Scope;
|
||||
scope->FunctionReturn = tryGetValue(node->GetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
@@ -634,7 +634,7 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
if (index < scope->Parameters.Length())
|
||||
value = scope->Parameters.Get()[index];
|
||||
@@ -1138,7 +1138,7 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val
|
||||
break;
|
||||
}
|
||||
int32 arrayIndex = 0;
|
||||
for (; iteratorIndex < scope->ReturnedValues.Count(); arrayIndex++)
|
||||
for (; arrayIndex < scope->ReturnedValues.Count(); arrayIndex++)
|
||||
{
|
||||
const auto& e = scope->ReturnedValues[arrayIndex];
|
||||
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(Path.Combine(FolderPath, "Assets"), "*.h", SearchOption.TopDirectoryOnly));
|
||||
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Cache"), "*.h", SearchOption.TopDirectoryOnly));
|
||||
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Factories"), "*.h", SearchOption.TopDirectoryOnly));
|
||||
files.AddRange(Directory.GetFiles(Path.Combine(FolderPath, "Storage"), "*.h", SearchOption.TopDirectoryOnly));
|
||||
files.Add(Path.Combine(FolderPath, "Upgraders/BinaryAssetUpgrader.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Upgraders/IAssetUpgrader.h"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
@@ -23,5 +24,7 @@ public class ContentExporters : EngineModule
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
files.Add(Path.Combine(FolderPath, "AssetsExportingManager.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Types.h"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
@@ -31,5 +32,7 @@ public class ContentImporters : EngineModule
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
files.Add(Path.Combine(FolderPath, "AssetsImportingManager.h"));
|
||||
files.Add(Path.Combine(FolderPath, "Types.h"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,8 +357,8 @@ public:
|
||||
{
|
||||
for (Iterator i = Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Value)
|
||||
::Delete(i->Value);
|
||||
if (i->Item)
|
||||
::Delete(i->Item);
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
|
||||
@@ -38,9 +38,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
BoundingBox()
|
||||
{
|
||||
}
|
||||
BoundingBox() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoundingBox"/> struct.
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
/// </summary>
|
||||
API_STRUCT(InBuild) struct FLAXENGINE_API BoundingFrustum
|
||||
{
|
||||
friend CollisionsHelper;
|
||||
private:
|
||||
Matrix _matrix;
|
||||
|
||||
@@ -33,9 +34,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
BoundingFrustum()
|
||||
{
|
||||
}
|
||||
BoundingFrustum() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoundingFrustum"/> struct.
|
||||
|
||||
@@ -34,9 +34,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
BoundingSphere()
|
||||
{
|
||||
}
|
||||
BoundingSphere() = default;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
return false;
|
||||
}
|
||||
|
||||
const Vector3 point = ray.Position + ray.Direction * distance;
|
||||
normal = Vector3::Normalize(point - sphere.Center);
|
||||
return true;
|
||||
@@ -953,22 +952,17 @@ bool CollisionsHelper::RayIntersectsSphere(const Ray& ray, const BoundingSphere&
|
||||
point = Vector3::Zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
point = ray.Position + ray.Direction * distance;
|
||||
return true;
|
||||
}
|
||||
|
||||
PlaneIntersectionType CollisionsHelper::PlaneIntersectsPoint(const Plane& plane, const Vector3& point)
|
||||
{
|
||||
Real distance = Vector3::Dot(plane.Normal, point);
|
||||
distance += plane.D;
|
||||
|
||||
const Real distance = Vector3::Dot(plane.Normal, point) + plane.D;
|
||||
if (distance > Plane::DistanceEpsilon)
|
||||
return PlaneIntersectionType::Front;
|
||||
|
||||
if (distance < Plane::DistanceEpsilon)
|
||||
return PlaneIntersectionType::Back;
|
||||
|
||||
return PlaneIntersectionType::Intersecting;
|
||||
}
|
||||
|
||||
@@ -1169,7 +1163,6 @@ ContainmentType CollisionsHelper::SphereContainsPoint(const BoundingSphere& sphe
|
||||
{
|
||||
if (Vector3::DistanceSquared(point, sphere.Center) <= sphere.Radius * sphere.Radius)
|
||||
return ContainmentType::Contains;
|
||||
|
||||
return ContainmentType::Disjoint;
|
||||
}
|
||||
|
||||
@@ -1254,13 +1247,10 @@ ContainmentType CollisionsHelper::SphereContainsBox(const BoundingSphere& sphere
|
||||
ContainmentType CollisionsHelper::SphereContainsSphere(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
|
||||
{
|
||||
const Real distance = Vector3::Distance(sphere1.Center, sphere2.Center);
|
||||
|
||||
if (sphere1.Radius + sphere2.Radius < distance)
|
||||
return ContainmentType::Disjoint;
|
||||
|
||||
if (sphere1.Radius - sphere2.Radius < distance)
|
||||
return ContainmentType::Intersects;
|
||||
|
||||
return ContainmentType::Contains;
|
||||
}
|
||||
|
||||
@@ -1274,7 +1264,8 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
||||
auto result = ContainmentType::Contains;
|
||||
for (int32 i = 0; i < 6; i++)
|
||||
{
|
||||
Plane plane = frustum.GetPlane(i);
|
||||
Plane plane = frustum._planes[i];
|
||||
|
||||
Vector3 p = box.Minimum;
|
||||
if (plane.Normal.X >= 0)
|
||||
p.X = box.Maximum.X;
|
||||
@@ -1282,7 +1273,7 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
||||
p.Y = box.Maximum.Y;
|
||||
if (plane.Normal.Z >= 0)
|
||||
p.Z = box.Maximum.Z;
|
||||
if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back)
|
||||
if (Vector3::Dot(plane.Normal, p) + plane.D < Plane::DistanceEpsilon)
|
||||
return ContainmentType::Disjoint;
|
||||
|
||||
p = box.Maximum;
|
||||
@@ -1292,7 +1283,7 @@ ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frus
|
||||
p.Y = box.Minimum.Y;
|
||||
if (plane.Normal.Z >= 0)
|
||||
p.Z = box.Minimum.Z;
|
||||
if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back)
|
||||
if (Vector3::Dot(plane.Normal, p) + plane.D < Plane::DistanceEpsilon)
|
||||
result = ContainmentType::Intersects;
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -50,9 +50,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Color()
|
||||
{
|
||||
}
|
||||
Color() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Color"/> struct.
|
||||
|
||||
@@ -53,9 +53,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Color32()
|
||||
{
|
||||
}
|
||||
Color32() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new Color32 with given r, g, b, a components.
|
||||
|
||||
@@ -121,9 +121,7 @@ public:
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
Half2()
|
||||
{
|
||||
}
|
||||
Half2() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
@@ -185,9 +183,7 @@ public:
|
||||
Half Z;
|
||||
|
||||
public:
|
||||
Half3()
|
||||
{
|
||||
}
|
||||
Half3() = default;
|
||||
|
||||
Half3(Half x, Half y, Half z)
|
||||
: X(x)
|
||||
@@ -242,9 +238,7 @@ public:
|
||||
Half W;
|
||||
|
||||
public:
|
||||
Half4()
|
||||
{
|
||||
}
|
||||
Half4() = default;
|
||||
|
||||
Half4(Half x, Half y, Half z, Half w)
|
||||
: X(x)
|
||||
|
||||
@@ -83,9 +83,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Matrix()
|
||||
{
|
||||
}
|
||||
Matrix() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Matrix"/> struct.
|
||||
|
||||
@@ -63,9 +63,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Matrix3x3()
|
||||
{
|
||||
}
|
||||
Matrix3x3() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Matrix3x3"/> struct.
|
||||
|
||||
@@ -30,9 +30,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Plane()
|
||||
{
|
||||
}
|
||||
Plane() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
|
||||
@@ -67,9 +67,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Quaternion()
|
||||
{
|
||||
}
|
||||
Quaternion() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
|
||||
@@ -35,9 +35,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Ray()
|
||||
{
|
||||
}
|
||||
Ray() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Ray"/> struct.
|
||||
|
||||
@@ -31,9 +31,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Rectangle()
|
||||
{
|
||||
}
|
||||
Rectangle() = default;
|
||||
|
||||
// Init
|
||||
// @param x Rectangle location X coordinate
|
||||
|
||||
@@ -40,9 +40,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Transform()
|
||||
{
|
||||
}
|
||||
Transform() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Transform"/> struct.
|
||||
|
||||
@@ -30,9 +30,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Triangle()
|
||||
{
|
||||
}
|
||||
Triangle() = default;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Triangle"/> struct.
|
||||
|
||||
@@ -60,9 +60,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Vector2Base()
|
||||
{
|
||||
}
|
||||
Vector2Base() = default;
|
||||
|
||||
FORCE_INLINE Vector2Base(T xy)
|
||||
: X(xy)
|
||||
|
||||
@@ -89,9 +89,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Vector3Base()
|
||||
{
|
||||
}
|
||||
Vector3Base() = default;
|
||||
|
||||
FORCE_INLINE Vector3Base(T xyz)
|
||||
: X(xyz)
|
||||
|
||||
@@ -76,9 +76,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Vector4Base()
|
||||
{
|
||||
}
|
||||
Vector4Base() = default;
|
||||
|
||||
FORCE_INLINE Vector4Base(T xyzw)
|
||||
: X(xyzw)
|
||||
|
||||
@@ -52,9 +52,7 @@ public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Viewport()
|
||||
{
|
||||
}
|
||||
Viewport() = default;
|
||||
|
||||
// Init
|
||||
// @param x The x coordinate of the upper-left corner of the viewport in pixels
|
||||
|
||||
@@ -36,6 +36,32 @@ namespace AllocatorExt
|
||||
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>
|
||||
/// Reallocates block of the memory.
|
||||
/// </summary>
|
||||
|
||||
@@ -114,3 +114,20 @@ inline Span<T> ToSpan(const T* ptr, int32 length)
|
||||
{
|
||||
return Span<T>(ptr, length);
|
||||
}
|
||||
|
||||
template<typename T, typename U = T, typename AllocationType = HeapAllocation>
|
||||
inline Span<U> ToSpan(const Array<T, AllocationType>& data)
|
||||
{
|
||||
return Span<U>((U*)data.Get(), data.Count());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool SpanContains(const Span<T> span, const T& value)
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (chars)
|
||||
StringUtils::ConvertANSI2UTF16(chars, _data, length);
|
||||
StringUtils::ConvertANSI2UTF16(chars, _data, length, _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);
|
||||
|
||||
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;
|
||||
|
||||
Platform::Free(oldData);
|
||||
|
||||
@@ -125,7 +125,8 @@ public:
|
||||
const int32 length = str && *str ? StringUtils::Length(str) : 0;
|
||||
const int32 prevCnt = _data.Count();
|
||||
_data.AddDefault(length);
|
||||
StringUtils::ConvertANSI2UTF16(str, _data.Get() + prevCnt, length);
|
||||
int32 tmp;
|
||||
StringUtils::ConvertANSI2UTF16(str, _data.Get() + prevCnt, length, tmp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -660,7 +660,8 @@ Variant::Variant(const StringAnsiView& v)
|
||||
const int32 length = v.Length() * sizeof(Char) + 2;
|
||||
AsBlob.Data = Allocator::Allocate(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;
|
||||
}
|
||||
else
|
||||
@@ -2578,7 +2579,8 @@ void Variant::SetString(const StringAnsiView& str)
|
||||
AsBlob.Data = Allocator::Allocate(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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
if (v.Type == to)
|
||||
@@ -3680,6 +3798,7 @@ void Variant::AllocStructure()
|
||||
const ScriptingType& type = typeHandle.GetType();
|
||||
AsBlob.Length = type.Size;
|
||||
AsBlob.Data = Allocator::Allocate(AsBlob.Length);
|
||||
Platform::MemoryClear(AsBlob.Data, AsBlob.Length);
|
||||
type.Struct.Ctor(AsBlob.Data);
|
||||
}
|
||||
else if (typeName == "System.Int16" || typeName == "System.UInt16")
|
||||
|
||||
@@ -359,6 +359,9 @@ public:
|
||||
void SetAsset(Asset* asset);
|
||||
String ToString() const;
|
||||
|
||||
// Inlines potential value type into in-built format (eg. Vector3 stored as Structure, or String stored as ManagedObject).
|
||||
void Inline();
|
||||
|
||||
FORCE_INLINE Variant Cast(const VariantType& to) const
|
||||
{
|
||||
return Cast(*this, to);
|
||||
|
||||
@@ -50,13 +50,13 @@ namespace FlaxEngine.Interop
|
||||
/// <remarks>The resources must be released by calling FreePooled() instead of Free()-method.</remarks>
|
||||
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);
|
||||
return managedArray;
|
||||
}
|
||||
|
||||
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)
|
||||
=> new ManagedArray(ptr, length, arrayType, elementType);
|
||||
@@ -86,7 +86,7 @@ namespace FlaxEngine.Interop
|
||||
_length = arr.Length;
|
||||
_arrayType = arrayType;
|
||||
_elementType = arr.GetType().GetElementType();
|
||||
_elementSize = Marshal.SizeOf(_elementType);
|
||||
_elementSize = NativeInterop.GetTypeSize(_elementType);
|
||||
}
|
||||
|
||||
internal void Allocate<T>(int length) where T : unmanaged
|
||||
@@ -117,7 +117,7 @@ namespace FlaxEngine.Interop
|
||||
_length = length;
|
||||
_arrayType = arrayType;
|
||||
_elementType = elementType;
|
||||
_elementSize = Marshal.SizeOf(elementType);
|
||||
_elementSize = NativeInterop.GetTypeSize(_elementType);
|
||||
}
|
||||
|
||||
~ManagedArray()
|
||||
|
||||
@@ -350,14 +350,14 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
|
||||
public static T[] AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
|
||||
{
|
||||
if (unmanaged is null)
|
||||
return null;
|
||||
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)
|
||||
{
|
||||
@@ -390,7 +390,7 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
@@ -399,10 +399,10 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
numElements = 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)
|
||||
{
|
||||
@@ -431,7 +431,7 @@ namespace FlaxEngine.Interop
|
||||
ManagedArray unmanagedArray;
|
||||
ManagedHandle handle;
|
||||
|
||||
public void FromManaged(T[]? managed)
|
||||
public void FromManaged(T[] managed)
|
||||
{
|
||||
if (managed == null)
|
||||
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)
|
||||
{
|
||||
@@ -489,7 +489,7 @@ namespace FlaxEngine.Interop
|
||||
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)
|
||||
{
|
||||
@@ -499,9 +499,9 @@ namespace FlaxEngine.Interop
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -244,7 +244,25 @@ namespace FlaxEngine.Interop
|
||||
@namespace = NativeAllocStringAnsi(type.Namespace ?? ""),
|
||||
typeAttributes = (uint)type.Attributes,
|
||||
};
|
||||
*assemblyHandle = GetAssemblyHandle(type.Assembly);
|
||||
|
||||
Assembly assembly = null;
|
||||
if (type.IsGenericType && !type.Assembly.IsCollectible)
|
||||
{
|
||||
// The owning assembly of a generic type with type arguments referencing
|
||||
// collectible assemblies must be one of the collectible assemblies.
|
||||
foreach (var genericType in type.GetGenericArguments())
|
||||
{
|
||||
if (genericType.Assembly.IsCollectible)
|
||||
{
|
||||
assembly = genericType.Assembly;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (assembly == null)
|
||||
assembly = type.Assembly;
|
||||
|
||||
*assemblyHandle = GetAssemblyHandle(assembly);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -770,6 +788,13 @@ namespace FlaxEngine.Interop
|
||||
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]
|
||||
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);
|
||||
}
|
||||
|
||||
[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]
|
||||
internal static void WriteArrayReference(ManagedHandle arrayHandle, IntPtr valueHandle, int index)
|
||||
{
|
||||
@@ -885,6 +919,7 @@ namespace FlaxEngine.Interop
|
||||
handle.Free();
|
||||
fieldHandleCacheCollectible.Clear();
|
||||
#endif
|
||||
_typeSizeCache.Clear();
|
||||
|
||||
foreach (var pair in classAttributesCacheCollectible)
|
||||
pair.Value.Free();
|
||||
@@ -923,7 +958,7 @@ namespace FlaxEngine.Interop
|
||||
if (nativeType.IsClass)
|
||||
size = sizeof(IntPtr);
|
||||
else
|
||||
size = Marshal.SizeOf(nativeType);
|
||||
size = GetTypeSize(nativeType);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
private static Dictionary<object, ManagedHandle> classAttributesCacheCollectible = new();
|
||||
private static Dictionary<Assembly, ManagedHandle> assemblyHandles = new();
|
||||
private static Dictionary<Type, int> _typeSizeCache = new();
|
||||
|
||||
private static Dictionary<string, IntPtr> loadedNativeLibraries = new();
|
||||
internal static Dictionary<string, string> nativeLibraryPaths = new();
|
||||
@@ -96,14 +97,17 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
#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
|
||||
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");
|
||||
if (File.Exists(assemblyPath))
|
||||
return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
||||
var assemblyPath = Path.Combine(editorTargetPath, assemblyName.Name + ".dll");
|
||||
if (File.Exists(assemblyPath))
|
||||
return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
@@ -581,7 +585,7 @@ namespace FlaxEngine.Interop
|
||||
else if (fieldType.IsClass || fieldType.IsPointer)
|
||||
fieldAlignment = IntPtr.Size;
|
||||
else
|
||||
fieldAlignment = Marshal.SizeOf(fieldType);
|
||||
fieldAlignment = GetTypeSize(fieldType);
|
||||
}
|
||||
|
||||
internal static void ToManagedField(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset)
|
||||
@@ -1085,6 +1089,29 @@ namespace FlaxEngine.Interop
|
||||
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
|
||||
{
|
||||
#if USE_AOT
|
||||
|
||||
@@ -1031,6 +1031,11 @@ API_ENUM(Attributes="Flags") enum class ViewFlags : uint64
|
||||
/// </summary>
|
||||
Sky = 1 << 26,
|
||||
|
||||
/// <summary>
|
||||
/// Shows/hides light debug shapes.
|
||||
/// </summary>
|
||||
LightsDebug = 1 << 27,
|
||||
|
||||
/// <summary>
|
||||
/// Default flags for Game.
|
||||
/// </summary>
|
||||
|
||||
@@ -238,7 +238,7 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the default material.
|
||||
/// </summary>
|
||||
MaterialBase* GetDefaultMaterial() const;
|
||||
API_PROPERTY() MaterialBase* GetDefaultMaterial() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default material (Deformable domain).
|
||||
|
||||
@@ -145,7 +145,7 @@ bool GPUShader::Create(MemoryReadStream& stream)
|
||||
|
||||
// Create CB
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
String name = ToString() + TEXT(".CB") + i;
|
||||
String name = String::Format(TEXT("{}.CB{}"), ToString(), i);
|
||||
#else
|
||||
String name;
|
||||
#endif
|
||||
|
||||
@@ -1337,6 +1337,20 @@ Actor* Actor::FindActor(const MClass* type, const StringView& name) const
|
||||
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
|
||||
{
|
||||
CHECK_RETURN(type, nullptr);
|
||||
|
||||
@@ -269,6 +269,17 @@ namespace FlaxEngine
|
||||
{
|
||||
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>
|
||||
/// Searches for all actors of a specific type in this actor children list.
|
||||
|
||||
@@ -739,6 +739,14 @@ public:
|
||||
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||
API_FUNCTION() Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name) const;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the actor of the given type 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>
|
||||
/// Tries to find the actor of the given type in this actor hierarchy (checks this actor and all children hierarchy).
|
||||
/// </summary>
|
||||
@@ -759,6 +767,17 @@ public:
|
||||
{
|
||||
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>
|
||||
/// Tries to find the script of the given type in this actor hierarchy (checks this actor and all children hierarchy).
|
||||
|
||||
@@ -144,7 +144,7 @@ void AnimatedModel::SetCurrentPose(const Array<Matrix>& nodesTransformation, boo
|
||||
Matrix invWorld;
|
||||
Matrix::Invert(world, invWorld);
|
||||
for (auto& m : GraphInstance.NodesPose)
|
||||
m = invWorld * m;
|
||||
m = m * invWorld;
|
||||
}
|
||||
OnAnimationUpdated();
|
||||
}
|
||||
@@ -603,7 +603,13 @@ void AnimatedModel::OnAnimationUpdated_Sync()
|
||||
// Update synchronous stuff
|
||||
UpdateSockets();
|
||||
ApplyRootMotion(GraphInstance.RootMotion);
|
||||
AnimationUpdated();
|
||||
if (!_isDuringUpdateEvent)
|
||||
{
|
||||
// Prevent stack-overflow when gameplay modifies the pose within the event
|
||||
_isDuringUpdateEvent = true;
|
||||
AnimationUpdated();
|
||||
_isDuringUpdateEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatedModel::OnAnimationUpdated()
|
||||
@@ -892,6 +898,31 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
|
||||
DrawModes |= DrawPass::GlobalSurfaceAtlas;
|
||||
}
|
||||
|
||||
const Span<MaterialSlot> AnimatedModel::GetMaterialSlots() const
|
||||
{
|
||||
const auto model = SkinnedModel.Get();
|
||||
if (model && !model->WaitForLoaded())
|
||||
return ToSpan(model->MaterialSlots);
|
||||
return Span<MaterialSlot>();
|
||||
}
|
||||
|
||||
MaterialBase* AnimatedModel::GetMaterial(int32 entryIndex)
|
||||
{
|
||||
if (SkinnedModel)
|
||||
SkinnedModel->WaitForLoaded();
|
||||
else
|
||||
return nullptr;
|
||||
CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
|
||||
MaterialBase* material = Entries[entryIndex].Material.Get();
|
||||
if (!material)
|
||||
{
|
||||
material = SkinnedModel->MaterialSlots[entryIndex].Material.Get();
|
||||
if (!material)
|
||||
material = GPUDevice::Instance->GetDefaultMaterial();
|
||||
}
|
||||
return material;
|
||||
}
|
||||
|
||||
bool AnimatedModel::IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal)
|
||||
{
|
||||
auto model = SkinnedModel.Get();
|
||||
|
||||
@@ -60,6 +60,7 @@ private:
|
||||
AnimationUpdateMode _actualMode;
|
||||
uint32 _counter;
|
||||
Real _lastMinDstSqr;
|
||||
bool _isDuringUpdateEvent = false;
|
||||
uint64 _lastUpdateFrame;
|
||||
BlendShapesInstance _blendShapes;
|
||||
ScriptingObjectReference<AnimatedModel> _masterPose;
|
||||
@@ -372,6 +373,8 @@ public:
|
||||
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
const Span<MaterialSlot> GetMaterialSlots() const override;
|
||||
MaterialBase* GetMaterial(int32 entryIndex) override;
|
||||
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
|
||||
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
|
||||
void OnDeleteObject() override;
|
||||
|
||||
@@ -26,6 +26,7 @@ void Light::OnEnable()
|
||||
GetSceneRendering()->AddActor(this, _sceneRenderingKey);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
GetSceneRendering()->AddLightsDebug<Light, &Light::DrawLightsDebug>(this);
|
||||
#endif
|
||||
|
||||
// Base
|
||||
@@ -36,6 +37,7 @@ void Light::OnDisable()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
GetSceneRendering()->RemoveLightsDebug<Light, &Light::DrawLightsDebug>(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveActor(this, _sceneRenderingKey);
|
||||
|
||||
@@ -43,6 +45,12 @@ void Light::OnDisable()
|
||||
Actor::OnDisable();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
void Light::DrawLightsDebug(RenderView& view)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void Light::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
// Base
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
const Vector3 size(50);
|
||||
return BoundingBox(_transform.Translation - size, _transform.Translation + size);
|
||||
}
|
||||
|
||||
virtual void DrawLightsDebug(RenderView& view);
|
||||
#endif
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
|
||||
@@ -39,10 +39,9 @@ void ModelInstanceActor::SetMaterial(int32 entryIndex, MaterialBase* material)
|
||||
MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32 entryIndex)
|
||||
{
|
||||
WaitForModelLoad();
|
||||
CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
|
||||
auto material = Entries[entryIndex].Material.Get();
|
||||
MaterialBase* material = GetMaterial(entryIndex);
|
||||
CHECK_RETURN(material && !material->WaitForLoaded(), nullptr);
|
||||
const auto result = material->CreateVirtualInstance();
|
||||
MaterialInstance* result = material->CreateVirtualInstance();
|
||||
Entries[entryIndex].Material = result;
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
|
||||
|
||||
@@ -35,6 +35,17 @@ public:
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetEntries(const Array<ModelInstanceEntry>& value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the material slots array set on the asset (eg. model or skinned model asset).
|
||||
/// </summary>
|
||||
API_PROPERTY(Sealed) virtual const Span<class MaterialSlot> GetMaterialSlots() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the material used to draw the meshes which are assigned to that slot (set in Entries or model's default).
|
||||
/// </summary>
|
||||
/// <param name="entryIndex">The material slot entry index.</param>
|
||||
API_FUNCTION(Sealed) virtual MaterialBase* GetMaterial(int32 entryIndex) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the material to the entry slot. Can be used to override the material of the meshes using this slot.
|
||||
/// </summary>
|
||||
|
||||
@@ -143,6 +143,16 @@ void PointLight::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
|
||||
|
||||
void PointLight::OnLayerChanged()
|
||||
|
||||
@@ -95,6 +95,7 @@ public:
|
||||
#if USE_EDITOR
|
||||
void OnDebugDraw() override;
|
||||
void OnDebugDrawSelected() override;
|
||||
void DrawLightsDebug(RenderView& view) override;
|
||||
#endif
|
||||
void OnLayerChanged() override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
|
||||
@@ -341,6 +341,31 @@ void SplineModel::OnParentChanged()
|
||||
OnSplineUpdated();
|
||||
}
|
||||
|
||||
const Span<MaterialSlot> SplineModel::GetMaterialSlots() const
|
||||
{
|
||||
const auto model = Model.Get();
|
||||
if (model && !model->WaitForLoaded())
|
||||
return ToSpan(model->MaterialSlots);
|
||||
return Span<MaterialSlot>();
|
||||
}
|
||||
|
||||
MaterialBase* SplineModel::GetMaterial(int32 entryIndex)
|
||||
{
|
||||
if (Model)
|
||||
Model->WaitForLoaded();
|
||||
else
|
||||
return nullptr;
|
||||
CHECK_RETURN(entryIndex >= 0 && entryIndex < Entries.Count(), nullptr);
|
||||
MaterialBase* material = Entries[entryIndex].Material.Get();
|
||||
if (!material)
|
||||
{
|
||||
material = Model->MaterialSlots[entryIndex].Material.Get();
|
||||
if (!material)
|
||||
material = GPUDevice::Instance->GetDefaultDeformableMaterial();
|
||||
}
|
||||
return material;
|
||||
}
|
||||
|
||||
bool SplineModel::HasContentLoaded() const
|
||||
{
|
||||
return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded();
|
||||
|
||||
@@ -115,6 +115,8 @@ public:
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
void OnParentChanged() override;
|
||||
const Span<MaterialSlot> GetMaterialSlots() const override;
|
||||
MaterialBase* GetMaterial(int32 entryIndex) override;
|
||||
|
||||
protected:
|
||||
// [ModelInstanceActor]
|
||||
|
||||
@@ -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 + 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);
|
||||
|
||||
@@ -210,6 +215,34 @@ void SpotLight::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
|
||||
|
||||
void SpotLight::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
|
||||
@@ -127,6 +127,7 @@ public:
|
||||
#if USE_EDITOR
|
||||
void OnDebugDraw() override;
|
||||
void OnDebugDrawSelected() override;
|
||||
void DrawLightsDebug(RenderView& view) override;
|
||||
#endif
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) 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)
|
||||
{
|
||||
auto model = Model.Get();
|
||||
|
||||
@@ -121,7 +121,7 @@ public:
|
||||
/// <param name="meshIndex">The zero-based mesh index.</param>
|
||||
/// <param name="lodIndex">The LOD index.</param>
|
||||
/// <returns>Material or null if not assigned.</returns>
|
||||
API_FUNCTION() MaterialBase* GetMaterial(int32 meshIndex, int32 lodIndex = 0) const;
|
||||
API_FUNCTION() MaterialBase* GetMaterial(int32 meshIndex, int32 lodIndex) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color of the painter vertex (this model instance).
|
||||
@@ -166,6 +166,8 @@ public:
|
||||
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
const Span<MaterialSlot> GetMaterialSlots() const override;
|
||||
MaterialBase* GetMaterial(int32 entryIndex) override;
|
||||
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
|
||||
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
|
||||
|
||||
|
||||
@@ -726,116 +726,6 @@ int32 Level::GetLayerIndex(const StringView& layer)
|
||||
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)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
@@ -1505,6 +1395,143 @@ Actor* Level::FindActor(const MClass* type, const StringView& name)
|
||||
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)
|
||||
{
|
||||
CHECK_RETURN(type, nullptr);
|
||||
|
||||
@@ -77,6 +77,18 @@ namespace FlaxEngine
|
||||
{
|
||||
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>
|
||||
/// Tries to find actor with the given ID in all loaded scenes. It's very fast O(1) lookup.
|
||||
|
||||
@@ -371,6 +371,41 @@ public:
|
||||
/// <returns>Actor instance if found, null otherwise.</returns>
|
||||
API_FUNCTION() static Actor* FindActor(API_PARAM(Attributes="TypeReference(typeof(Actor))") const MClass* type, const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the actor 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>
|
||||
/// Tries to find the actor of the given type in all the loaded scenes.
|
||||
/// </summary>
|
||||
@@ -392,6 +427,18 @@ public:
|
||||
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>
|
||||
/// Tries to find the script of the given type in all the loaded scenes.
|
||||
/// </summary>
|
||||
@@ -481,33 +528,6 @@ public:
|
||||
/// </summary>
|
||||
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:
|
||||
// Actor API
|
||||
enum class ActorEventType
|
||||
|
||||
@@ -96,6 +96,16 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c
|
||||
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
|
||||
}
|
||||
@@ -157,14 +167,16 @@ void SceneRendering::UpdateActor(Actor* a, int32& key)
|
||||
const int32 category = a->_drawCategory;
|
||||
ScopeLock lock(Locker);
|
||||
auto& list = Actors[category];
|
||||
if (list.IsEmpty())
|
||||
if (list.Count() <= key) // Ignore invalid key softly
|
||||
return;
|
||||
auto& e = list[key];
|
||||
ASSERT_LOW_LAYER(a == e.Actor);
|
||||
for (auto* listener : _listeners)
|
||||
listener->OnSceneRenderingUpdateActor(a, e.Bounds);
|
||||
e.LayerMask = a->GetLayerMask();
|
||||
e.Bounds = a->GetSphere();
|
||||
if (e.Actor == a)
|
||||
{
|
||||
for (auto* listener : _listeners)
|
||||
listener->OnSceneRenderingUpdateActor(a, e.Bounds);
|
||||
e.LayerMask = a->GetLayerMask();
|
||||
e.Bounds = a->GetSphere();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneRendering::RemoveActor(Actor* a, int32& key)
|
||||
@@ -172,14 +184,16 @@ void SceneRendering::RemoveActor(Actor* a, int32& key)
|
||||
const int32 category = a->_drawCategory;
|
||||
ScopeLock lock(Locker);
|
||||
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[key];
|
||||
ASSERT_LOW_LAYER(a == e.Actor);
|
||||
for (auto* listener : _listeners)
|
||||
listener->OnSceneRenderingRemoveActor(a);
|
||||
e.Actor = nullptr;
|
||||
e.LayerMask = 0;
|
||||
auto& e = list.Get()[key];
|
||||
if (e.Actor == a)
|
||||
{
|
||||
for (auto* listener : _listeners)
|
||||
listener->OnSceneRenderingRemoveActor(a);
|
||||
e.Actor = nullptr;
|
||||
e.LayerMask = 0;
|
||||
}
|
||||
}
|
||||
key = -1;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ class FLAXENGINE_API SceneRendering
|
||||
{
|
||||
#if USE_EDITOR
|
||||
typedef Function<void(RenderView&)> PhysicsDebugCallback;
|
||||
typedef Function<void(RenderView&)> LightsDebugCallback;
|
||||
friend class ViewportIconsRendererService;
|
||||
#endif
|
||||
public:
|
||||
@@ -95,6 +96,7 @@ public:
|
||||
private:
|
||||
#if USE_EDITOR
|
||||
Array<PhysicsDebugCallback> PhysicsDebug;
|
||||
Array<LightsDebugCallback> LightsDebug;
|
||||
Array<Actor*> ViewportIcons;
|
||||
#endif
|
||||
|
||||
@@ -153,6 +155,22 @@ public:
|
||||
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)
|
||||
{
|
||||
ViewportIcons.Add(obj);
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace FlaxEngine
|
||||
if (_keyframes == null || _keyframes.Length != count)
|
||||
_keyframes = new BezierCurve<Transform>.Keyframe[count];
|
||||
#if !BUILD_RELEASE
|
||||
if (Marshal.SizeOf(typeof(BezierCurve<Transform>.Keyframe)) != Transform.SizeInBytes * 3 + sizeof(float))
|
||||
throw new Exception("Invalid size of BezierCurve keyframe " + Marshal.SizeOf(typeof(BezierCurve<Transform>.Keyframe)) + " bytes.");
|
||||
if (System.Runtime.CompilerServices.Unsafe.SizeOf<BezierCurve<Transform>.Keyframe>() != Transform.SizeInBytes * 3 + sizeof(float))
|
||||
throw new Exception("Invalid size of BezierCurve keyframe " + System.Runtime.CompilerServices.Unsafe.SizeOf<BezierCurve<Transform>.Keyframe>() + " bytes.");
|
||||
#endif
|
||||
Internal_GetKeyframes(__unmanagedPtr, _keyframes);
|
||||
return _keyframes;
|
||||
|
||||
@@ -23,7 +23,7 @@ void NetworkReplicationHierarchyUpdateResult::Init()
|
||||
{
|
||||
_clientsHaveLocation = false;
|
||||
_clients.Resize(NetworkManager::Clients.Count());
|
||||
_clientsMask = NetworkClientsMask();
|
||||
_clientsMask = NetworkManager::Mode == NetworkManagerMode::Client ? NetworkClientsMask::All : NetworkClientsMask();
|
||||
for (int32 i = 0; i < _clients.Count(); i++)
|
||||
_clientsMask.SetBit(i);
|
||||
_entries.Clear();
|
||||
@@ -63,6 +63,17 @@ bool NetworkReplicationNode::RemoveObject(ScriptingObject* 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)
|
||||
{
|
||||
const int32 index = Objects.Find(obj);
|
||||
@@ -156,6 +167,7 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj
|
||||
cell->MinCullDistance = obj.CullDistance;
|
||||
}
|
||||
cell->Node->AddObject(obj);
|
||||
_objectToCell[obj.Object] = coord;
|
||||
|
||||
// Cache minimum culling distance for a whole cell to skip it at once
|
||||
cell->MinCullDistance = Math::Min(cell->MinCullDistance, obj.CullDistance);
|
||||
@@ -163,14 +175,35 @@ void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj
|
||||
|
||||
bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj)
|
||||
{
|
||||
for (const auto& e : _children)
|
||||
Int3 coord;
|
||||
|
||||
if (!_objectToCell.TryGet(obj, coord))
|
||||
{
|
||||
if (e.Value.Node->RemoveObject(obj))
|
||||
{
|
||||
// TODO: remove empty cells?
|
||||
// TODO: update MinCullDistance for cell?
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_children[coord].Node->RemoveObject(obj))
|
||||
{
|
||||
_objectToCell.Remove(obj);
|
||||
// TODO: remove empty cells?
|
||||
// TODO: update MinCullDistance for cell?
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -200,6 +200,14 @@ API_CLASS(Abstract, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API Ne
|
||||
/// <returns>True on successful removal, otherwise false.</returns>
|
||||
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>
|
||||
/// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization.
|
||||
/// </summary>
|
||||
@@ -238,6 +246,7 @@ private:
|
||||
};
|
||||
|
||||
Dictionary<Int3, Cell> _children;
|
||||
Dictionary<ScriptingObject*, Int3> _objectToCell;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -247,6 +256,7 @@ public:
|
||||
|
||||
void AddObject(NetworkReplicationHierarchyObject obj) override;
|
||||
bool RemoveObject(ScriptingObject* obj) override;
|
||||
bool GetObject(ScriptingObject* obj, NetworkReplicationHierarchyObject& result) override;
|
||||
void Update(NetworkReplicationHierarchyUpdateResult* result) override;
|
||||
};
|
||||
|
||||
|
||||
@@ -700,9 +700,9 @@ void NetworkReplicator::AddRPC(const ScriptingTypeHandle& typeHandle, const Stri
|
||||
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)
|
||||
@@ -1113,12 +1113,12 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
|
||||
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);
|
||||
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
||||
if (!info || !obj || NetworkManager::IsOffline())
|
||||
return;
|
||||
return false;
|
||||
ObjectsLock.Lock();
|
||||
auto& rpc = RpcQueue.AddOne();
|
||||
rpc.Object = obj;
|
||||
@@ -1135,12 +1135,23 @@ void NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa
|
||||
}
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
ScopeLock lock(ObjectsLock);
|
||||
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)
|
||||
@@ -1193,6 +1204,7 @@ void NetworkInternal::NetworkReplicatorClear()
|
||||
Objects.Remove(it);
|
||||
}
|
||||
}
|
||||
Objects.Clear();
|
||||
RpcQueue.Clear();
|
||||
SpawnQueue.Clear();
|
||||
DespawnQueue.Clear();
|
||||
|
||||
@@ -120,10 +120,11 @@ namespace FlaxEngine.Networking
|
||||
/// <param name="name">The RPC name.</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>
|
||||
/// <returns>True if RPC cannot be executed locally, false if execute it locally too (checks RPC mode and target client ids).</returns>
|
||||
[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>
|
||||
|
||||
@@ -195,13 +195,14 @@ public:
|
||||
/// <param name="name">The RPC name.</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>
|
||||
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:
|
||||
#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 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);
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ struct FLAXENGINE_API NetworkRpcInfo
|
||||
uint8 Client : 1;
|
||||
uint8 Channel : 4;
|
||||
void (*Execute)(ScriptingObject* obj, NetworkStream* stream, void* tag);
|
||||
void (*Invoke)(ScriptingObject* obj, void** args);
|
||||
bool (*Invoke)(ScriptingObject* obj, void** args);
|
||||
void* Tag;
|
||||
|
||||
/// <summary>
|
||||
@@ -83,10 +83,7 @@ FORCE_INLINE void NetworkRpcInitArg(Array<void*, FixedAllocation<16>>& args, con
|
||||
{ \
|
||||
Array<void*, FixedAllocation<16>> args; \
|
||||
NetworkRpcInitArg(args, __VA_ARGS__); \
|
||||
rpcInfo.Invoke(this, args.Get()); \
|
||||
if (rpcInfo.Server && networkMode == NetworkManagerMode::Client) \
|
||||
return; \
|
||||
if (rpcInfo.Client && networkMode == NetworkManagerMode::Server) \
|
||||
if (rpcInfo.Invoke(this, args.Get())) \
|
||||
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 Float3 position = (Float3)TryGetValue(node->GetBox(0), Value::Zero);
|
||||
Float4 projPos;
|
||||
Float3::Transform(position, viewProjection);
|
||||
Float3::Transform(position, viewProjection, projPos);
|
||||
projPos /= projPos.W;
|
||||
value = Float2(projPos.X * 0.5f + 0.5f, projPos.Y * 0.5f + 0.5f);
|
||||
break;
|
||||
|
||||
@@ -230,10 +230,11 @@ void WheeledVehicle::DrawPhysicsDebug(RenderView& view)
|
||||
{
|
||||
const Vector3 currentPos = wheel.Collider->GetPosition();
|
||||
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(currentPos, wheel.Radius * 0.08f), Color::Blue * 0.8f, 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)
|
||||
{
|
||||
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 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;
|
||||
PhysicsBackend::GetRigidActorPose(_actor, actorPose.Translation, actorPose.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(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_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())
|
||||
{
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(data.State.SuspensionTraceStart, 5.0f), Color::AliceBlue, 0, false);
|
||||
|
||||
@@ -125,6 +125,7 @@ WindowBase::~WindowBase()
|
||||
{
|
||||
ASSERT(!RenderTask);
|
||||
ASSERT(!_swapChain);
|
||||
WindowsManager::Unregister((Window*)this);
|
||||
}
|
||||
|
||||
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