Merge remote-tracking branch 'origin/master' into 1.7
# Conflicts: # Flax.flaxproj
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
"Version": {
|
"Version": {
|
||||||
"Major": 1,
|
"Major": 1,
|
||||||
"Minor": 7,
|
"Minor": 7,
|
||||||
"Build": 6400
|
"Build": 6401
|
||||||
},
|
},
|
||||||
"Company": "Flax",
|
"Company": "Flax",
|
||||||
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
"Copyright": "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.",
|
||||||
|
|||||||
@@ -520,8 +520,8 @@ namespace FlaxEditor.Content.GUI
|
|||||||
{
|
{
|
||||||
int min = _selection.Min(x => x.IndexInParent);
|
int min = _selection.Min(x => x.IndexInParent);
|
||||||
int max = _selection.Max(x => x.IndexInParent);
|
int max = _selection.Max(x => x.IndexInParent);
|
||||||
min = Mathf.Min(min, item.IndexInParent);
|
min = Mathf.Max(Mathf.Min(min, item.IndexInParent), 0);
|
||||||
max = Mathf.Max(max, item.IndexInParent);
|
max = Mathf.Min(Mathf.Max(max, item.IndexInParent), _children.Count - 1);
|
||||||
var selection = new List<ContentItem>(_selection);
|
var selection = new List<ContentItem>(_selection);
|
||||||
for (int i = min; i <= max; i++)
|
for (int i = min; i <= max; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,144 +1,52 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using FlaxEditor.CustomEditors.Editors;
|
||||||
using System.Runtime.InteropServices;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.Interop;
|
using FlaxEngine.Tools;
|
||||||
|
|
||||||
|
namespace FlaxEngine.Tools
|
||||||
|
{
|
||||||
|
partial class AudioTool
|
||||||
|
{
|
||||||
|
partial struct Options
|
||||||
|
{
|
||||||
|
private bool ShowBtiDepth => Format != AudioFormat.Vorbis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace FlaxEditor.CustomEditors.Dedicated
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Custom editor for <see cref="FlaxEngine.Tools.AudioTool.Options"/>.
|
||||||
|
/// </summary>
|
||||||
|
[CustomEditor(typeof(FlaxEngine.Tools.AudioTool.Options)), DefaultEditor]
|
||||||
|
public class AudioToolOptionsEditor : GenericEditor
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override List<ItemInfo> GetItemsForType(ScriptType type)
|
||||||
|
{
|
||||||
|
// Show both fields and properties
|
||||||
|
return GetItemsForType(type, true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace FlaxEditor.Content.Import
|
namespace FlaxEditor.Content.Import
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Proxy object to present audio import settings in <see cref="ImportFilesDialog"/>.
|
/// Proxy object to present audio import settings in <see cref="ImportFilesDialog"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
public class AudioImportSettings
|
public class AudioImportSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A custom set of bit depth audio import sizes.
|
/// The settings data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum CustomBitDepth
|
[EditorDisplay(null, EditorDisplayAttribute.InlineStyle)]
|
||||||
{
|
public AudioTool.Options Settings = AudioTool.Options.Default;
|
||||||
/// <summary>
|
|
||||||
/// The 8.
|
|
||||||
/// </summary>
|
|
||||||
_8 = 8,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The 16.
|
|
||||||
/// </summary>
|
|
||||||
_16 = 16,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The 24.
|
|
||||||
/// </summary>
|
|
||||||
_24 = 24,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The 32.
|
|
||||||
/// </summary>
|
|
||||||
_32 = 32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the bit depth to enum.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="f">The bit depth.</param>
|
|
||||||
/// <returns>The converted enum.</returns>
|
|
||||||
public static CustomBitDepth ConvertBitDepth(int f)
|
|
||||||
{
|
|
||||||
FieldInfo[] fields = typeof(CustomBitDepth).GetFields();
|
|
||||||
for (int i = 0; i < fields.Length; i++)
|
|
||||||
{
|
|
||||||
var field = fields[i];
|
|
||||||
if (field.Name.Equals("value__"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (f == (int)field.GetRawConstantValue())
|
|
||||||
return (CustomBitDepth)f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CustomBitDepth._16;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The audio data format to import the audio clip as.
|
|
||||||
/// </summary>
|
|
||||||
[EditorOrder(10), DefaultValue(AudioFormat.Vorbis), Tooltip("The audio data format to import the audio clip as.")]
|
|
||||||
public AudioFormat Format { get; set; } = AudioFormat.Vorbis;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.
|
|
||||||
/// </summary>
|
|
||||||
[EditorOrder(15), DefaultValue(0.4f), Limit(0, 1, 0.01f), Tooltip("The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.")]
|
|
||||||
public float CompressionQuality { get; set; } = 0.4f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).
|
|
||||||
/// </summary>
|
|
||||||
[EditorOrder(20), DefaultValue(false), Tooltip("Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).")]
|
|
||||||
public bool DisableStreaming { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.
|
|
||||||
/// </summary>
|
|
||||||
[EditorOrder(30), DefaultValue(false), EditorDisplay(null, "Is 3D"), Tooltip("Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.")]
|
|
||||||
public bool Is3D { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size of a single sample in bits. The clip will be converted to this bit depth on import.
|
|
||||||
/// </summary>
|
|
||||||
[EditorOrder(40), DefaultValue(CustomBitDepth._16), Tooltip("The size of a single sample in bits. The clip will be converted to this bit depth on import.")]
|
|
||||||
public CustomBitDepth BitDepth { get; set; } = CustomBitDepth._16;
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct InternalOptions
|
|
||||||
{
|
|
||||||
[MarshalAs(UnmanagedType.I1)]
|
|
||||||
public AudioFormat Format;
|
|
||||||
public byte DisableStreaming;
|
|
||||||
public byte Is3D;
|
|
||||||
public int BitDepth;
|
|
||||||
public float Quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ToInternal(out InternalOptions options)
|
|
||||||
{
|
|
||||||
options = new InternalOptions
|
|
||||||
{
|
|
||||||
Format = Format,
|
|
||||||
DisableStreaming = (byte)(DisableStreaming ? 1 : 0),
|
|
||||||
Is3D = (byte)(Is3D ? 1 : 0),
|
|
||||||
Quality = CompressionQuality,
|
|
||||||
BitDepth = (int)BitDepth,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void FromInternal(ref InternalOptions options)
|
|
||||||
{
|
|
||||||
Format = options.Format;
|
|
||||||
DisableStreaming = options.DisableStreaming != 0;
|
|
||||||
Is3D = options.Is3D != 0;
|
|
||||||
CompressionQuality = options.Quality;
|
|
||||||
BitDepth = ConvertBitDepth(options.BitDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries the restore the asset import options from the target resource file.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="options">The options.</param>
|
|
||||||
/// <param name="assetPath">The asset path.</param>
|
|
||||||
/// <returns>True settings has been restored, otherwise false.</returns>
|
|
||||||
public static bool TryRestore(ref AudioImportSettings options, string assetPath)
|
|
||||||
{
|
|
||||||
if (AudioImportEntry.Internal_GetAudioImportOptions(assetPath, out var internalOptions))
|
|
||||||
{
|
|
||||||
// Restore settings
|
|
||||||
options.FromInternal(ref internalOptions);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -147,7 +55,7 @@ namespace FlaxEditor.Content.Import
|
|||||||
/// <seealso cref="AssetImportEntry" />
|
/// <seealso cref="AssetImportEntry" />
|
||||||
public partial class AudioImportEntry : AssetImportEntry
|
public partial class AudioImportEntry : AssetImportEntry
|
||||||
{
|
{
|
||||||
private AudioImportSettings _settings = new AudioImportSettings();
|
private AudioImportSettings _settings = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AudioImportEntry"/> class.
|
/// Initializes a new instance of the <see cref="AudioImportEntry"/> class.
|
||||||
@@ -157,7 +65,7 @@ namespace FlaxEditor.Content.Import
|
|||||||
: base(ref request)
|
: base(ref request)
|
||||||
{
|
{
|
||||||
// Try to restore target asset Audio import options (useful for fast reimport)
|
// Try to restore target asset Audio import options (useful for fast reimport)
|
||||||
AudioImportSettings.TryRestore(ref _settings, ResultUrl);
|
Editor.TryRestoreImportOptions(ref _settings.Settings, ResultUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -166,27 +74,23 @@ namespace FlaxEditor.Content.Import
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool TryOverrideSettings(object settings)
|
public override bool TryOverrideSettings(object settings)
|
||||||
{
|
{
|
||||||
if (settings is AudioImportSettings o)
|
if (settings is AudioImportSettings s)
|
||||||
{
|
{
|
||||||
_settings = o;
|
_settings.Settings = s.Settings;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (settings is AudioTool.Options o)
|
||||||
|
{
|
||||||
|
_settings.Settings = o;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Import()
|
public override bool Import()
|
||||||
{
|
{
|
||||||
return Editor.Import(SourceUrl, ResultUrl, _settings);
|
return Editor.Import(SourceUrl, ResultUrl, _settings.Settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Internal Calls
|
|
||||||
|
|
||||||
[LibraryImport("FlaxEngine", EntryPoint = "AudioImportEntryInternal_GetAudioImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
|
||||||
[return: MarshalAs(UnmanagedType.U1)]
|
|
||||||
internal static partial bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,12 +54,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||||
{
|
{
|
||||||
if (!_preview.HasLoadedAssets)
|
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Texture)request.Asset);
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if all mip maps are streamed
|
|
||||||
var asset = (CubeTexture)request.Asset;
|
|
||||||
return asset.ResidentMipLevels >= Mathf.Max(1, (int)(asset.MipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||||
{
|
{
|
||||||
return _preview.HasLoadedAssets;
|
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((MaterialInstance)request.Asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||||
{
|
{
|
||||||
return _preview.HasLoadedAssets;
|
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Material)request.Asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -82,12 +82,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||||
{
|
{
|
||||||
if (!_preview.HasLoadedAssets)
|
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((Model)request.Asset);
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if asset is streamed enough
|
|
||||||
var asset = (Model)request.Asset;
|
|
||||||
return asset.LoadedLODs >= Mathf.Max(1, (int)(asset.LODs.Length * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -54,15 +54,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||||
{
|
{
|
||||||
if (!_preview.HasLoadedAssets)
|
return _preview.HasLoadedAssets && ThumbnailsModule.HasMinimumQuality((SkinnedModel)request.Asset);
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check if asset is streamed enough
|
|
||||||
var asset = (SkinnedModel)request.Asset;
|
|
||||||
var lods = asset.LODs.Length;
|
|
||||||
if (asset.IsLoaded && lods == 0)
|
|
||||||
return true; // Skeleton-only model
|
|
||||||
return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -57,11 +57,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
public override bool CanDrawThumbnail(ThumbnailRequest request)
|
||||||
{
|
{
|
||||||
// Check if asset is streamed enough
|
return ThumbnailsModule.HasMinimumQuality((Texture)request.Asset);
|
||||||
var asset = (Texture)request.Asset;
|
|
||||||
var mipLevels = asset.MipLevels;
|
|
||||||
var minMipLevels = Mathf.Min(mipLevels, 7);
|
|
||||||
return asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * ThumbnailsModule.MinimumRequiredResourcesQuality));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -125,6 +125,74 @@ namespace FlaxEditor.Content.Thumbnails
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static bool HasMinimumQuality(TextureBase asset)
|
||||||
|
{
|
||||||
|
var mipLevels = asset.MipLevels;
|
||||||
|
var minMipLevels = Mathf.Min(mipLevels, 7);
|
||||||
|
return asset.IsLoaded && asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * MinimumRequiredResourcesQuality));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool HasMinimumQuality(Model asset)
|
||||||
|
{
|
||||||
|
if (!asset.IsLoaded)
|
||||||
|
return false;
|
||||||
|
var lods = asset.LODs.Length;
|
||||||
|
var slots = asset.MaterialSlots;
|
||||||
|
foreach (var slot in slots)
|
||||||
|
{
|
||||||
|
if (slot.Material && !HasMinimumQuality(slot.Material))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool HasMinimumQuality(SkinnedModel asset)
|
||||||
|
{
|
||||||
|
var lods = asset.LODs.Length;
|
||||||
|
if (asset.IsLoaded && lods == 0)
|
||||||
|
return true; // Skeleton-only model
|
||||||
|
var slots = asset.MaterialSlots;
|
||||||
|
foreach (var slot in slots)
|
||||||
|
{
|
||||||
|
if (slot.Material && !HasMinimumQuality(slot.Material))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return asset.LoadedLODs >= Mathf.Max(1, (int)(lods * MinimumRequiredResourcesQuality));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool HasMinimumQuality(MaterialBase asset)
|
||||||
|
{
|
||||||
|
if (asset is MaterialInstance asInstance)
|
||||||
|
return HasMinimumQuality(asInstance);
|
||||||
|
return HasMinimumQualityInternal(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool HasMinimumQuality(Material asset)
|
||||||
|
{
|
||||||
|
return HasMinimumQualityInternal(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool HasMinimumQuality(MaterialInstance asset)
|
||||||
|
{
|
||||||
|
if (!HasMinimumQualityInternal(asset))
|
||||||
|
return false;
|
||||||
|
var baseMaterial = asset.BaseMaterial;
|
||||||
|
return baseMaterial == null || HasMinimumQualityInternal(baseMaterial);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasMinimumQualityInternal(MaterialBase asset)
|
||||||
|
{
|
||||||
|
if (!asset.IsLoaded)
|
||||||
|
return false;
|
||||||
|
var parameters = asset.Parameters;
|
||||||
|
foreach (var parameter in parameters)
|
||||||
|
{
|
||||||
|
if (parameter.Value is TextureBase asTexture && !HasMinimumQuality(asTexture))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#region IContentItemOwner
|
#region IContentItemOwner
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.InteropServices.Marshalling;
|
using System.Runtime.InteropServices.Marshalling;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.Content.Import;
|
|
||||||
using FlaxEditor.Content.Settings;
|
using FlaxEditor.Content.Settings;
|
||||||
using FlaxEditor.Content.Thumbnails;
|
using FlaxEditor.Content.Thumbnails;
|
||||||
using FlaxEditor.Modules;
|
using FlaxEditor.Modules;
|
||||||
@@ -154,12 +153,12 @@ namespace FlaxEditor
|
|||||||
public ContentFindingModule ContentFinding;
|
public ContentFindingModule ContentFinding;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scripts editing
|
/// The scripts editing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CodeEditingModule CodeEditing;
|
public CodeEditingModule CodeEditing;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scripts documentation
|
/// The scripts documentation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CodeDocsModule CodeDocs;
|
public CodeDocsModule CodeDocs;
|
||||||
|
|
||||||
@@ -179,7 +178,7 @@ namespace FlaxEditor
|
|||||||
public ProjectCacheModule ProjectCache;
|
public ProjectCacheModule ProjectCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The undo/redo
|
/// The undo/redo.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EditorUndo Undo;
|
public EditorUndo Undo;
|
||||||
|
|
||||||
@@ -939,21 +938,6 @@ namespace FlaxEditor
|
|||||||
BehaviorTree = 12,
|
BehaviorTree = 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Imports the audio asset file to the target location.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="inputPath">The source file path.</param>
|
|
||||||
/// <param name="outputPath">The result asset file path.</param>
|
|
||||||
/// <param name="settings">The settings.</param>
|
|
||||||
/// <returns>True if importing failed, otherwise false.</returns>
|
|
||||||
public static bool Import(string inputPath, string outputPath, AudioImportSettings settings)
|
|
||||||
{
|
|
||||||
if (settings == null)
|
|
||||||
throw new ArgumentNullException();
|
|
||||||
settings.ToInternal(out var internalOptions);
|
|
||||||
return Internal_ImportAudio(inputPath, outputPath, ref internalOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializes the given object to json asset.
|
/// Serializes the given object to json asset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1671,10 +1655,6 @@ namespace FlaxEditor
|
|||||||
[return: MarshalAs(UnmanagedType.U1)]
|
[return: MarshalAs(UnmanagedType.U1)]
|
||||||
internal static partial bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId);
|
internal static partial bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId);
|
||||||
|
|
||||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_ImportAudio", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
|
||||||
[return: MarshalAs(UnmanagedType.U1)]
|
|
||||||
internal static partial bool Internal_ImportAudio(string inputPath, string outputPath, ref AudioImportSettings.InternalOptions options);
|
|
||||||
|
|
||||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAudioClipMetadata", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAudioClipMetadata", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||||
internal static partial void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize);
|
internal static partial void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize);
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
fov = cam.FieldOfView;
|
fov = cam.FieldOfView;
|
||||||
customAspectRatio = cam.CustomAspectRatio;
|
customAspectRatio = cam.CustomAspectRatio;
|
||||||
view.RenderLayersMask = cam.RenderLayersMask;
|
view.RenderLayersMask = cam.RenderLayersMask;
|
||||||
|
view.Flags = cam.RenderFlags;
|
||||||
|
view.Mode = cam.RenderMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to evaluate camera properties based on the animated tracks
|
// Try to evaluate camera properties based on the animated tracks
|
||||||
|
|||||||
@@ -50,43 +50,6 @@
|
|||||||
|
|
||||||
Guid ManagedEditor::ObjectID(0x91970b4e, 0x99634f61, 0x84723632, 0x54c776af);
|
Guid ManagedEditor::ObjectID(0x91970b4e, 0x99634f61, 0x84723632, 0x54c776af);
|
||||||
|
|
||||||
// Disable warning C4800: 'const byte': forcing value to bool 'true' or 'false' (performance warning)
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#pragma warning( push )
|
|
||||||
#pragma warning( disable : 4800)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct InternalAudioOptions
|
|
||||||
{
|
|
||||||
AudioFormat Format;
|
|
||||||
byte DisableStreaming;
|
|
||||||
byte Is3D;
|
|
||||||
int32 BitDepth;
|
|
||||||
float Quality;
|
|
||||||
|
|
||||||
static void Convert(InternalAudioOptions* from, ImportAudio::Options* to)
|
|
||||||
{
|
|
||||||
to->Format = from->Format;
|
|
||||||
to->DisableStreaming = from->DisableStreaming;
|
|
||||||
to->Is3D = from->Is3D;
|
|
||||||
to->BitDepth = from->BitDepth;
|
|
||||||
to->Quality = from->Quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Convert(ImportAudio::Options* from, InternalAudioOptions* to)
|
|
||||||
{
|
|
||||||
to->Format = from->Format;
|
|
||||||
to->DisableStreaming = from->DisableStreaming;
|
|
||||||
to->Is3D = from->Is3D;
|
|
||||||
to->BitDepth = from->BitDepth;
|
|
||||||
to->Quality = from->Quality;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#pragma warning( pop )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Pack log messages into a single scratch buffer to reduce dynamic memory allocations
|
// Pack log messages into a single scratch buffer to reduce dynamic memory allocations
|
||||||
CriticalSection CachedLogDataLocker;
|
CriticalSection CachedLogDataLocker;
|
||||||
Array<byte> CachedLogData;
|
Array<byte> CachedLogData;
|
||||||
@@ -299,16 +262,6 @@ DEFINE_INTERNAL_CALL(MString*) EditorInternal_CanImport(MString* extensionObj)
|
|||||||
return importer ? MUtils::ToString(importer->ResultExtension) : nullptr;
|
return importer ? MUtils::ToString(importer->ResultExtension) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_INTERNAL_CALL(bool) EditorInternal_ImportAudio(MString* inputPathObj, MString* outputPathObj, InternalAudioOptions* optionsObj)
|
|
||||||
{
|
|
||||||
ImportAudio::Options options;
|
|
||||||
InternalAudioOptions::Convert(optionsObj, &options);
|
|
||||||
String inputPath, outputPath;
|
|
||||||
MUtils::ToString(inputPathObj, inputPath);
|
|
||||||
MUtils::ToString(outputPathObj, outputPath);
|
|
||||||
return ManagedEditor::Import(inputPath, outputPath, &options);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_INTERNAL_CALL(void) EditorInternal_GetAudioClipMetadata(AudioClip* clip, int32* originalSize, int32* importedSize)
|
DEFINE_INTERNAL_CALL(void) EditorInternal_GetAudioClipMetadata(AudioClip* clip, int32* originalSize, int32* importedSize)
|
||||||
{
|
{
|
||||||
INTERNAL_CALL_CHECK(clip);
|
INTERNAL_CALL_CHECK(clip);
|
||||||
@@ -769,24 +722,6 @@ DEFINE_INTERNAL_CALL(MTypeObject*) CustomEditorsUtilInternal_GetCustomEditor(MTy
|
|||||||
return CustomEditorsUtil::GetCustomEditor(targetType);
|
return CustomEditorsUtil::GetCustomEditor(targetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_INTERNAL_CALL(bool) AudioImportEntryInternal_GetAudioImportOptions(MString* pathObj, InternalAudioOptions* result)
|
|
||||||
{
|
|
||||||
String path;
|
|
||||||
MUtils::ToString(pathObj, path);
|
|
||||||
FileSystem::NormalizePath(path);
|
|
||||||
|
|
||||||
ImportAudio::Options options;
|
|
||||||
if (ImportAudio::TryGetImportOptions(path, options))
|
|
||||||
{
|
|
||||||
// Convert into managed storage
|
|
||||||
InternalAudioOptions::Convert(&options, result);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_INTERNAL_CALL(MArray*) LayersAndTagsSettingsInternal_GetCurrentLayers(int* layersCount)
|
DEFINE_INTERNAL_CALL(MArray*) LayersAndTagsSettingsInternal_GetCurrentLayers(int* layersCount)
|
||||||
{
|
{
|
||||||
*layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount());
|
*layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount());
|
||||||
@@ -834,3 +769,15 @@ bool ManagedEditor::TryRestoreImportOptions(ModelTool::Options& options, String
|
|||||||
FileSystem::NormalizePath(assetPath);
|
FileSystem::NormalizePath(assetPath);
|
||||||
return ImportModelFile::TryGetImportOptions(assetPath, options);
|
return ImportModelFile::TryGetImportOptions(assetPath, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ManagedEditor::Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options)
|
||||||
|
{
|
||||||
|
return Import(inputPath, outputPath, (void*)&options);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ManagedEditor::TryRestoreImportOptions(AudioTool::Options& options, String assetPath)
|
||||||
|
{
|
||||||
|
// Get options from model
|
||||||
|
FileSystem::NormalizePath(assetPath);
|
||||||
|
return ImportAudio::TryGetImportOptions(assetPath, options);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Engine/ShadowsOfMordor/Types.h"
|
#include "Engine/ShadowsOfMordor/Types.h"
|
||||||
#include "Engine/Tools/TextureTool/TextureTool.h"
|
#include "Engine/Tools/TextureTool/TextureTool.h"
|
||||||
#include "Engine/Tools/ModelTool/ModelTool.h"
|
#include "Engine/Tools/ModelTool/ModelTool.h"
|
||||||
|
#include "Engine/Tools/AudioTool/AudioTool.h"
|
||||||
|
|
||||||
namespace CSG
|
namespace CSG
|
||||||
{
|
{
|
||||||
@@ -190,6 +191,25 @@ public:
|
|||||||
API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) ModelTool::Options& options, String assetPath);
|
API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) ModelTool::Options& options, String assetPath);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if COMPILE_WITH_AUDIO_TOOL
|
||||||
|
/// <summary>
|
||||||
|
/// Imports the audio asset file to the target location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inputPath">The source file path.</param>
|
||||||
|
/// <param name="outputPath">The result asset file path.</param>
|
||||||
|
/// <param name="options">The import settings.</param>
|
||||||
|
/// <returns>True if importing failed, otherwise false.</returns>
|
||||||
|
API_FUNCTION() static bool Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries the restore the asset import options from the target resource file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The options.</param>
|
||||||
|
/// <param name="assetPath">The asset path.</param>
|
||||||
|
/// <returns>True settings has been restored, otherwise false.</returns>
|
||||||
|
API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) AudioTool::Options& options, String assetPath);
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
||||||
|
|
||||||
|
|||||||
@@ -37,11 +37,6 @@ namespace FlaxEditor.Modules
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<Prefab, Actor> PrefabApplied;
|
public event Action<Prefab, Actor> PrefabApplied;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Locally cached actor for prefab creation.
|
|
||||||
/// </summary>
|
|
||||||
private Actor _prefabCreationActor;
|
|
||||||
|
|
||||||
internal PrefabsModule(Editor editor)
|
internal PrefabsModule(Editor editor)
|
||||||
: base(editor)
|
: base(editor)
|
||||||
{
|
{
|
||||||
@@ -65,13 +60,14 @@ namespace FlaxEditor.Modules
|
|||||||
/// To create prefab manually (from code) use <see cref="PrefabManager.CreatePrefab"/> method.
|
/// To create prefab manually (from code) use <see cref="PrefabManager.CreatePrefab"/> method.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="selection">The scene selection to use.</param>
|
/// <param name="selection">The scene selection to use.</param>
|
||||||
public void CreatePrefab(List<SceneGraphNode> selection)
|
/// <param name="prefabWindow">The prefab window that creates it.</param>
|
||||||
|
public void CreatePrefab(List<SceneGraphNode> selection, Windows.Assets.PrefabWindow prefabWindow = null)
|
||||||
{
|
{
|
||||||
if (selection == null)
|
if (selection == null)
|
||||||
selection = Editor.SceneEditing.Selection;
|
selection = Editor.SceneEditing.Selection;
|
||||||
if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.CanCreatePrefab)
|
if (selection.Count == 1 && selection[0] is ActorNode actorNode && actorNode.CanCreatePrefab)
|
||||||
{
|
{
|
||||||
CreatePrefab(actorNode.Actor);
|
CreatePrefab(actorNode.Actor, true, prefabWindow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +88,8 @@ namespace FlaxEditor.Modules
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="actor">The root prefab actor.</param>
|
/// <param name="actor">The root prefab actor.</param>
|
||||||
/// <param name="rename">Allow renaming or not</param>
|
/// <param name="rename">Allow renaming or not</param>
|
||||||
public void CreatePrefab(Actor actor, bool rename)
|
/// <param name="prefabWindow">The prefab window that creates it.</param>
|
||||||
|
public void CreatePrefab(Actor actor, bool rename, Windows.Assets.PrefabWindow prefabWindow = null)
|
||||||
{
|
{
|
||||||
// Skip in invalid states
|
// Skip in invalid states
|
||||||
if (!Editor.StateMachine.CurrentState.CanEditContent)
|
if (!Editor.StateMachine.CurrentState.CanEditContent)
|
||||||
@@ -105,42 +102,47 @@ namespace FlaxEditor.Modules
|
|||||||
PrefabCreating?.Invoke(actor);
|
PrefabCreating?.Invoke(actor);
|
||||||
|
|
||||||
var proxy = Editor.ContentDatabase.GetProxy<Prefab>();
|
var proxy = Editor.ContentDatabase.GetProxy<Prefab>();
|
||||||
_prefabCreationActor = actor;
|
Editor.Windows.ContentWin.NewItem(proxy, actor, contentItem => OnPrefabCreated(contentItem, actor, prefabWindow), actor.Name, rename);
|
||||||
Editor.Windows.ContentWin.NewItem(proxy, actor, OnPrefabCreated, actor.Name, rename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPrefabCreated(ContentItem contentItem)
|
private void OnPrefabCreated(ContentItem contentItem, Actor actor, Windows.Assets.PrefabWindow prefabWindow)
|
||||||
{
|
{
|
||||||
if (contentItem is PrefabItem prefabItem)
|
if (contentItem is PrefabItem prefabItem)
|
||||||
{
|
{
|
||||||
PrefabCreated?.Invoke(prefabItem);
|
PrefabCreated?.Invoke(prefabItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip in invalid states
|
Undo undo = null;
|
||||||
if (!Editor.StateMachine.CurrentState.CanEditScene)
|
if (prefabWindow != null)
|
||||||
return;
|
{
|
||||||
|
prefabWindow.MarkAsEdited();
|
||||||
|
undo = prefabWindow.Undo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Skip in invalid states
|
||||||
|
if (!Editor.StateMachine.CurrentState.CanEditScene)
|
||||||
|
return;
|
||||||
|
undo = Editor.Undo;
|
||||||
|
}
|
||||||
|
|
||||||
// Record undo for prefab creating (backend links the target instance with the prefab)
|
// Record undo for prefab creating (backend links the target instance with the prefab)
|
||||||
if (Editor.Undo.Enabled)
|
if (undo.Enabled)
|
||||||
{
|
{
|
||||||
if (!_prefabCreationActor)
|
if (!actor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var actorsList = new List<Actor>();
|
var actorsList = new List<Actor>();
|
||||||
Utilities.Utils.GetActorsTree(actorsList, _prefabCreationActor);
|
Utilities.Utils.GetActorsTree(actorsList, actor);
|
||||||
|
|
||||||
var actions = new IUndoAction[actorsList.Count];
|
var actions = new IUndoAction[actorsList.Count];
|
||||||
for (int i = 0; i < actorsList.Count; i++)
|
for (int i = 0; i < actorsList.Count; i++)
|
||||||
{
|
actions[i] = BreakPrefabLinkAction.Linked(actorsList[i]);
|
||||||
var action = BreakPrefabLinkAction.Linked(actorsList[i]);
|
undo.AddAction(new MultiUndoAction(actions));
|
||||||
actions[i] = action;
|
|
||||||
}
|
|
||||||
Undo.AddAction(new MultiUndoAction(actions));
|
|
||||||
|
|
||||||
_prefabCreationActor = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Editor.Instance.Windows.PropertiesWin.Presenter.BuildLayout();
|
Editor.Windows.PropertiesWin.Presenter.BuildLayout();
|
||||||
|
if (prefabWindow != null)
|
||||||
|
prefabWindow.Presenter.BuildLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
using FlaxEditor.Options;
|
using FlaxEditor.Options;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
@@ -330,6 +332,78 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
|||||||
Editor.Instance.CodeEditing.SelectedEditor = editor;
|
Editor.Instance.CodeEditing.SelectedEditor = editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts creating a new module
|
||||||
|
/// </summary>
|
||||||
|
internal void CreateModule(string path, string moduleName, bool editorModule, bool cpp)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
Editor.LogWarning("Failed to create module due to no name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create folder
|
||||||
|
var moduleFolderPath = Path.Combine(path, moduleName);
|
||||||
|
Directory.CreateDirectory(moduleFolderPath);
|
||||||
|
|
||||||
|
// Create module
|
||||||
|
var moduleText = "using Flax.Build;\n" +
|
||||||
|
"using Flax.Build.NativeCpp;\n" +
|
||||||
|
$"\npublic class {moduleName} : Game{(editorModule ? "Editor" : "")}Module\n" +
|
||||||
|
"{\n " +
|
||||||
|
"/// <inheritdoc />\n" +
|
||||||
|
" public override void Init()\n" +
|
||||||
|
" {\n" +
|
||||||
|
" base.Init();\n" +
|
||||||
|
"\n" +
|
||||||
|
" // C#-only scripting if false\n" +
|
||||||
|
$" BuildNativeCode = {(cpp ? "true" : "false")};\n" +
|
||||||
|
" }\n" +
|
||||||
|
"\n" +
|
||||||
|
" /// <inheritdoc />\n" +
|
||||||
|
" public override void Setup(BuildOptions options)\n" +
|
||||||
|
" {" +
|
||||||
|
"\n" +
|
||||||
|
" base.Setup(options);\n" +
|
||||||
|
"\n" +
|
||||||
|
" options.ScriptingAPI.IgnoreMissingDocumentationWarnings = true;\n" +
|
||||||
|
"\n" +
|
||||||
|
" // Here you can modify the build options for your game module\n" +
|
||||||
|
" // To reference another module use: options.PublicDependencies.Add(\"Audio\");\n" +
|
||||||
|
" // To add C++ define use: options.PublicDefinitions.Add(\"COMPILE_WITH_FLAX\");\n" +
|
||||||
|
" // To learn more see scripting documentation.\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}";
|
||||||
|
moduleText = Encoding.UTF8.GetString(Encoding.Default.GetBytes(moduleText));
|
||||||
|
var modulePath = Path.Combine(moduleFolderPath, $"{moduleName}.Build.cs");
|
||||||
|
File.WriteAllText(modulePath, moduleText);
|
||||||
|
Editor.Log($"Module created at {modulePath}");
|
||||||
|
|
||||||
|
// Get editor target and target files and add module
|
||||||
|
var files = Directory.GetFiles(path);
|
||||||
|
var targetModuleText = $"Modules.Add(\"{moduleName}\");\n ";
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (!file.Contains(".Build.cs", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
var targetText = File.ReadAllText(file);
|
||||||
|
|
||||||
|
// Skip game project if it is suppose to be an editor module
|
||||||
|
if (editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO: Handle edge case when there are no modules in a target
|
||||||
|
var index = targetText.IndexOf("Modules.Add");
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
var newText = targetText.Insert(index, targetModuleText);
|
||||||
|
File.WriteAllText(file, newText);
|
||||||
|
Editor.Log($"Module added to Target: {file}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnUpdate()
|
public override void OnUpdate()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.SceneGraph.Actors
|
namespace FlaxEditor.SceneGraph.Actors
|
||||||
@@ -22,9 +23,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnContextMenu(ContextMenu contextMenu)
|
public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
|
||||||
{
|
{
|
||||||
base.OnContextMenu(contextMenu);
|
base.OnContextMenu(contextMenu, window);
|
||||||
|
|
||||||
var actor = (AnimatedModel)Actor;
|
var actor = (AnimatedModel)Actor;
|
||||||
if (actor && actor.SkinnedModel)
|
if (actor && actor.SkinnedModel)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ using Real = System.Double;
|
|||||||
using Real = System.Single;
|
using Real = System.Single;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.SceneGraph.Actors
|
namespace FlaxEditor.SceneGraph.Actors
|
||||||
@@ -23,6 +25,25 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
|
||||||
|
{
|
||||||
|
base.OnContextMenu(contextMenu, window);
|
||||||
|
if (window is not SceneTreeWindow win)
|
||||||
|
return;
|
||||||
|
var button = new ContextMenuButton(contextMenu, "Move Camera to View");
|
||||||
|
button.Parent = contextMenu.ItemsContainer;
|
||||||
|
contextMenu.ItemsContainer.Children.Remove(button);
|
||||||
|
contextMenu.ItemsContainer.Children.Insert(4, button);
|
||||||
|
button.Clicked += () =>
|
||||||
|
{
|
||||||
|
var c = Actor as Camera;
|
||||||
|
var viewport = Editor.Instance.Windows.EditWin.Viewport;
|
||||||
|
c.Position = viewport.ViewPosition;
|
||||||
|
c.Orientation = viewport.ViewOrientation;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal)
|
public override bool RayCastSelf(ref RayCastData ray, out Real distance, out Vector3 normal)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.SceneGraph.GUI;
|
using FlaxEditor.SceneGraph.GUI;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.SceneGraph.Actors
|
namespace FlaxEditor.SceneGraph.Actors
|
||||||
@@ -65,7 +66,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
public override SceneNode ParentScene => this;
|
public override SceneNode ParentScene => this;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnContextMenu(ContextMenu contextMenu)
|
public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
|
||||||
{
|
{
|
||||||
contextMenu.AddSeparator();
|
contextMenu.AddSeparator();
|
||||||
var path = Scene.Path;
|
var path = Scene.Path;
|
||||||
@@ -80,7 +81,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
if (Level.ScenesCount > 1)
|
if (Level.ScenesCount > 1)
|
||||||
contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
|
contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
|
||||||
|
|
||||||
base.OnContextMenu(contextMenu);
|
base.OnContextMenu(contextMenu, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelect()
|
private void OnSelect()
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Real = System.Single;
|
|||||||
using System;
|
using System;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.Modules;
|
using FlaxEditor.Modules;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.Json;
|
using FlaxEngine.Json;
|
||||||
using Object = FlaxEngine.Object;
|
using Object = FlaxEngine.Object;
|
||||||
@@ -203,9 +204,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnContextMenu(ContextMenu contextMenu)
|
public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
|
||||||
{
|
{
|
||||||
ParentNode.OnContextMenu(contextMenu);
|
ParentNode.OnContextMenu(contextMenu, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SceneGraphNode Create(StateData state)
|
public static SceneGraphNode Create(StateData state)
|
||||||
@@ -272,9 +273,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
DebugDraw.DrawSphere(new BoundingSphere(pos, tangentSize), Color.YellowGreen, 0, false);
|
DebugDraw.DrawSphere(new BoundingSphere(pos, tangentSize), Color.YellowGreen, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnContextMenu(ContextMenu contextMenu)
|
public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
|
||||||
{
|
{
|
||||||
ParentNode.OnContextMenu(contextMenu);
|
ParentNode.OnContextMenu(contextMenu, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnDispose()
|
public override void OnDispose()
|
||||||
@@ -354,9 +355,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnContextMenu(ContextMenu contextMenu)
|
public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
|
||||||
{
|
{
|
||||||
base.OnContextMenu(contextMenu);
|
base.OnContextMenu(contextMenu, window);
|
||||||
|
|
||||||
contextMenu.AddButton("Add spline model", OnAddSplineModel);
|
contextMenu.AddButton("Add spline model", OnAddSplineModel);
|
||||||
contextMenu.AddButton("Add spline collider", OnAddSplineCollider);
|
contextMenu.AddButton("Add spline collider", OnAddSplineCollider);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.SceneGraph.Actors
|
namespace FlaxEditor.SceneGraph.Actors
|
||||||
@@ -21,9 +22,9 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnContextMenu(ContextMenu contextMenu)
|
public override void OnContextMenu(ContextMenu contextMenu, EditorWindow window)
|
||||||
{
|
{
|
||||||
base.OnContextMenu(contextMenu);
|
base.OnContextMenu(contextMenu, window);
|
||||||
|
|
||||||
contextMenu.AddButton("Add collider", OnAddMeshCollider).Enabled = ((StaticModel)Actor).Model != null;
|
contextMenu.AddButton("Add collider", OnAddMeshCollider).Enabled = ((StaticModel)Actor).Model != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -599,7 +599,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
// Drag scripts
|
// Drag scripts
|
||||||
else if (_dragScripts != null && _dragScripts.HasValidDrag)
|
else if (_dragScripts != null && _dragScripts.HasValidDrag)
|
||||||
{
|
{
|
||||||
foreach(var script in _dragScripts.Objects)
|
foreach (var script in _dragScripts.Objects)
|
||||||
{
|
{
|
||||||
var customAction = script.HasPrefabLink ? new ReparentAction(script) : null;
|
var customAction = script.HasPrefabLink ? new ReparentAction(script) : null;
|
||||||
using (new UndoBlock(ActorNode.Root.Undo, script, "Change script parent", customAction))
|
using (new UndoBlock(ActorNode.Root.Undo, script, "Change script parent", customAction))
|
||||||
@@ -616,7 +616,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
var spawnParent = myActor;
|
var spawnParent = myActor;
|
||||||
if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below)
|
if (DragOverMode == DragItemPositioning.Above || DragOverMode == DragItemPositioning.Below)
|
||||||
spawnParent = newParent;
|
spawnParent = newParent;
|
||||||
|
|
||||||
for (int i = 0; i < _dragAssets.Objects.Count; i++)
|
for (int i = 0; i < _dragAssets.Objects.Count; i++)
|
||||||
{
|
{
|
||||||
var item = _dragAssets.Objects[i];
|
var item = _dragAssets.Objects[i];
|
||||||
@@ -720,7 +720,7 @@ namespace FlaxEditor.SceneGraph.GUI
|
|||||||
for (var i = 0; i < tree.Selection.Count; i++)
|
for (var i = 0; i < tree.Selection.Count; i++)
|
||||||
{
|
{
|
||||||
var e = tree.Selection[i];
|
var e = tree.Selection[i];
|
||||||
|
|
||||||
// Skip if parent is already selected to keep correct parenting
|
// Skip if parent is already selected to keep correct parenting
|
||||||
if (tree.Selection.Contains(e.Parent))
|
if (tree.Selection.Contains(e.Parent))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.Modules;
|
using FlaxEditor.Modules;
|
||||||
using FlaxEditor.SceneGraph.Actors;
|
using FlaxEditor.SceneGraph.Actors;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.SceneGraph
|
namespace FlaxEditor.SceneGraph
|
||||||
@@ -339,7 +340,7 @@ namespace FlaxEditor.SceneGraph
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when scene tree window wants to show the context menu. Allows to add custom options.
|
/// Called when scene tree window wants to show the context menu. Allows to add custom options.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu)
|
public virtual void OnContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu contextMenu, EditorWindow window)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -745,12 +745,12 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
return inputType.IsVoid;
|
return inputType.IsVoid;
|
||||||
}
|
}
|
||||||
@@ -1162,7 +1162,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
if (nodeArch.Tag is not ScriptMemberInfo memberInfo)
|
if (nodeArch.Tag is not ScriptMemberInfo memberInfo)
|
||||||
return false;
|
return false;
|
||||||
@@ -1188,7 +1188,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
if (nodeArch.Tag is not ScriptMemberInfo memberInfo)
|
if (nodeArch.Tag is not ScriptMemberInfo memberInfo)
|
||||||
return false;
|
return false;
|
||||||
@@ -1836,12 +1836,12 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
return inputType.IsVoid;
|
return inputType.IsVoid;
|
||||||
}
|
}
|
||||||
@@ -1982,7 +1982,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
UpdateSignature();
|
UpdateSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||||
if (scriptType == ScriptType.Null)
|
if (scriptType == ScriptType.Null)
|
||||||
@@ -2011,7 +2011,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
var scriptType = TypeUtils.GetType((string)nodeArch.DefaultValues[0]);
|
||||||
if (scriptType == ScriptType.Null)
|
if (scriptType == ScriptType.Null)
|
||||||
@@ -2093,7 +2093,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
UpdateSignature();
|
UpdateSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
if (outputType.IsVoid)
|
if (outputType.IsVoid)
|
||||||
return true;
|
return true;
|
||||||
@@ -2130,7 +2130,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
return inputType.IsVoid;
|
return inputType.IsVoid;
|
||||||
}
|
}
|
||||||
@@ -2353,7 +2353,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
// Event based nodes always have a pulse input, so it's always compatible with void
|
// Event based nodes always have a pulse input, so it's always compatible with void
|
||||||
if (outputType.IsVoid)
|
if (outputType.IsVoid)
|
||||||
@@ -2373,7 +2373,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
// Event based nodes always have a pulse output, so it's always compatible with void
|
// Event based nodes always have a pulse output, so it's always compatible with void
|
||||||
if (inputType.IsVoid)
|
if (inputType.IsVoid)
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
var typeName = (string)nodeArch.DefaultValues[0];
|
var typeName = (string)nodeArch.DefaultValues[0];
|
||||||
var type = TypeUtils.GetType(typeName);
|
var type = TypeUtils.GetType(typeName);
|
||||||
@@ -234,7 +234,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
var typeName = (string)nodeArch.DefaultValues[0];
|
var typeName = (string)nodeArch.DefaultValues[0];
|
||||||
var type = TypeUtils.GetType(typeName);
|
var type = TypeUtils.GetType(typeName);
|
||||||
@@ -255,7 +255,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint)
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
var typeName = (string)nodeArch.DefaultValues[0];
|
var typeName = (string)nodeArch.DefaultValues[0];
|
||||||
var type = TypeUtils.GetType(typeName);
|
var type = TypeUtils.GetType(typeName);
|
||||||
@@ -267,7 +267,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint)
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
{
|
{
|
||||||
var typeName = (string)nodeArch.DefaultValues[0];
|
var typeName = (string)nodeArch.DefaultValues[0];
|
||||||
var type = TypeUtils.GetType(typeName);
|
var type = TypeUtils.GetType(typeName);
|
||||||
|
|||||||
@@ -490,6 +490,56 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_combobox.Width = Width - 30;
|
_combobox.Width = Width - 30;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
if (inputType == ScriptType.Object)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]);
|
||||||
|
ScriptType type = parameter?.Type ?? ScriptType.Null;
|
||||||
|
|
||||||
|
if (parameter == null || type == ScriptType.Null || parameter.Type.Type == null)
|
||||||
|
return false;
|
||||||
|
if (DefaultPrototypes == null || !DefaultPrototypes.TryGetValue(parameter.Type.Type, out var elements))
|
||||||
|
{
|
||||||
|
return VisjectSurface.FullCastCheck(inputType, type, hint);
|
||||||
|
}
|
||||||
|
if (elements == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (var i = 0; i < elements.Length; i++)
|
||||||
|
{
|
||||||
|
if(elements[i].Type != NodeElementType.Output)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]);
|
||||||
|
ScriptType type = parameter?.Type ?? ScriptType.Null;
|
||||||
|
|
||||||
|
if (parameter == null || type == ScriptType.Null)
|
||||||
|
return false;
|
||||||
|
if (parameter.Type.Type == null || DefaultPrototypes == null || !DefaultPrototypes.TryGetValue(parameter.Type.Type, out var elements))
|
||||||
|
return false;
|
||||||
|
if (elements == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (var i = 0; i < elements.Length; i++)
|
||||||
|
{
|
||||||
|
if(elements[i].Type != NodeElementType.Input)
|
||||||
|
continue;
|
||||||
|
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -675,6 +725,53 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override bool UseNormalMaps => false;
|
protected override bool UseNormalMaps => false;
|
||||||
|
|
||||||
|
internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
if (inputType == ScriptType.Object)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]);
|
||||||
|
ScriptType type = parameter?.Type ?? ScriptType.Null;
|
||||||
|
|
||||||
|
if (parameter == null || type == ScriptType.Null)
|
||||||
|
return false;
|
||||||
|
if (parameter.Type.Type == null || DefaultPrototypesParticleEmitter == null || !DefaultPrototypesParticleEmitter.TryGetValue(parameter.Type.Type, out var elements))
|
||||||
|
return false;
|
||||||
|
if (elements == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (var i = 0; i < elements.Length; i++)
|
||||||
|
{
|
||||||
|
if(elements[i].Type != NodeElementType.Output)
|
||||||
|
continue;
|
||||||
|
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal new static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]);
|
||||||
|
ScriptType type = parameter?.Type ?? ScriptType.Null;
|
||||||
|
|
||||||
|
if (parameter == null || type == ScriptType.Null)
|
||||||
|
return false;
|
||||||
|
if (parameter.Type.Type == null || DefaultPrototypesParticleEmitter == null || !DefaultPrototypesParticleEmitter.TryGetValue(parameter.Type.Type, out var elements))
|
||||||
|
return false;
|
||||||
|
if (elements == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (var i = 0; i < elements.Length; i++)
|
||||||
|
{
|
||||||
|
if(elements[i].Type != NodeElementType.Input)
|
||||||
|
continue;
|
||||||
|
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -692,6 +789,22 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override bool UseNormalMaps => false;
|
protected override bool UseNormalMaps => false;
|
||||||
|
|
||||||
|
internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
if (inputType == ScriptType.Object)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]);
|
||||||
|
ScriptType type = parameter?.Type ?? ScriptType.Null;
|
||||||
|
|
||||||
|
return VisjectSurface.FullCastCheck(inputType, type, hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal new static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -874,6 +987,22 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
_combobox.Width = Width - 50;
|
_combobox.Width = Width - 50;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
return inputType == ScriptType.Void;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsInputCompatible(NodeArchetype nodeArch, ScriptType outputType, ConnectionsHint hint, VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
if (outputType == ScriptType.Void)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]);
|
||||||
|
ScriptType type = parameter?.Type ?? ScriptType.Null;
|
||||||
|
|
||||||
|
return VisjectSurface.FullCastCheck(outputType, type, hint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -885,6 +1014,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
TypeID = 1,
|
TypeID = 1,
|
||||||
Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGet(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGet(id, context, arch, groupArch),
|
||||||
|
IsInputCompatible = SurfaceNodeParamsGet.IsInputCompatible,
|
||||||
|
IsOutputCompatible = SurfaceNodeParamsGet.IsOutputCompatible,
|
||||||
Title = "Get Parameter",
|
Title = "Get Parameter",
|
||||||
Description = "Parameter value getter",
|
Description = "Parameter value getter",
|
||||||
Flags = NodeFlags.MaterialGraph | NodeFlags.AnimGraph,
|
Flags = NodeFlags.MaterialGraph | NodeFlags.AnimGraph,
|
||||||
@@ -902,6 +1033,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
TypeID = 2,
|
TypeID = 2,
|
||||||
Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGetParticleEmitter(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGetParticleEmitter(id, context, arch, groupArch),
|
||||||
|
IsInputCompatible = SurfaceNodeParamsGetParticleEmitter.IsInputCompatible,
|
||||||
|
IsOutputCompatible = SurfaceNodeParamsGetParticleEmitter.IsOutputCompatible,
|
||||||
Title = "Get Parameter",
|
Title = "Get Parameter",
|
||||||
Description = "Parameter value getter",
|
Description = "Parameter value getter",
|
||||||
Flags = NodeFlags.ParticleEmitterGraph,
|
Flags = NodeFlags.ParticleEmitterGraph,
|
||||||
@@ -919,6 +1052,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
TypeID = 3,
|
TypeID = 3,
|
||||||
Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGetVisualScript(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new SurfaceNodeParamsGetVisualScript(id, context, arch, groupArch),
|
||||||
|
IsInputCompatible = SurfaceNodeParamsGetVisualScript.IsInputCompatible,
|
||||||
|
IsOutputCompatible = SurfaceNodeParamsGetVisualScript.IsOutputCompatible,
|
||||||
Title = "Get Parameter",
|
Title = "Get Parameter",
|
||||||
Description = "Parameter value getter",
|
Description = "Parameter value getter",
|
||||||
Flags = NodeFlags.VisualScriptGraph,
|
Flags = NodeFlags.VisualScriptGraph,
|
||||||
@@ -936,6 +1071,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
TypeID = 4,
|
TypeID = 4,
|
||||||
Create = (id, context, arch, groupArch) => new SurfaceNodeParamsSet(id, context, arch, groupArch),
|
Create = (id, context, arch, groupArch) => new SurfaceNodeParamsSet(id, context, arch, groupArch),
|
||||||
|
IsInputCompatible = SurfaceNodeParamsSet.IsInputCompatible,
|
||||||
|
IsOutputCompatible = SurfaceNodeParamsSet.IsOutputCompatible,
|
||||||
Title = "Set Parameter",
|
Title = "Set Parameter",
|
||||||
Description = "Parameter value setter",
|
Description = "Parameter value setter",
|
||||||
Flags = NodeFlags.VisualScriptGraph,
|
Flags = NodeFlags.VisualScriptGraph,
|
||||||
|
|||||||
@@ -787,7 +787,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AsNode : SurfaceNode
|
internal class AsNode : SurfaceNode
|
||||||
{
|
{
|
||||||
private TypePickerControl _picker;
|
private TypePickerControl _picker;
|
||||||
|
|
||||||
@@ -838,6 +838,15 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
box.CurrentType = type ? type : ScriptType.FlaxObject;
|
box.CurrentType = type ? type : ScriptType.FlaxObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the type of the picker and the type of the output box
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Target Type</param>
|
||||||
|
public void SetPickerValue(ScriptType type)
|
||||||
|
{
|
||||||
|
_picker.Value = type;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
@@ -999,6 +1008,15 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
GetBox(4).CurrentType = type ? type : _picker.Type;
|
GetBox(4).CurrentType = type ? type : _picker.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the type of the picker and the type of the output box
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">Target Type</param>
|
||||||
|
public void SetPickerValue(ScriptType type)
|
||||||
|
{
|
||||||
|
_picker.Value = type;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -623,7 +623,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
Archetypes = archetypes
|
Archetypes = archetypes
|
||||||
};
|
};
|
||||||
|
|
||||||
var group = CreateGroup(groupArchetype);
|
var group = CreateGroup(groupArchetype, false);
|
||||||
group.ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown);
|
group.ArrowImageOpened = new SpriteBrush(Style.Current.ArrowDown);
|
||||||
group.ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight);
|
group.ArrowImageClosed = new SpriteBrush(Style.Current.ArrowRight);
|
||||||
group.Close(false);
|
group.Close(false);
|
||||||
@@ -726,9 +726,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
SelectedItem = previousSelectedItem;
|
SelectedItem = previousSelectedItem;
|
||||||
|
|
||||||
// Scroll into view (without smoothing)
|
// Scroll into view (without smoothing)
|
||||||
_panel1.VScrollBar.SmoothingScale = 0;
|
_panel1.ScrollViewTo(SelectedItem, true);
|
||||||
_panel1.ScrollViewTo(SelectedItem);
|
|
||||||
_panel1.VScrollBar.SmoothingScale = 1;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,11 +110,11 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
bool isCompatible = false;
|
bool isCompatible = false;
|
||||||
if (startBox.IsOutput && _archetype.IsInputCompatible != null)
|
if (startBox.IsOutput && _archetype.IsInputCompatible != null)
|
||||||
{
|
{
|
||||||
isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints);
|
isCompatible |= _archetype.IsInputCompatible.Invoke(_archetype, startBox.CurrentType, _archetype.ConnectionsHints, startBox.ParentNode.Context);
|
||||||
}
|
}
|
||||||
else if (!startBox.IsOutput && _archetype.IsOutputCompatible != null)
|
else if (!startBox.IsOutput && _archetype.IsOutputCompatible != null)
|
||||||
{
|
{
|
||||||
isCompatible |= _archetype.IsOutputCompatible.Invoke(_archetype, startBox.CurrentType, startBox.ParentNode.Archetype.ConnectionsHints);
|
isCompatible |= _archetype.IsOutputCompatible.Invoke(_archetype, startBox.CurrentType, startBox.ParentNode.Archetype.ConnectionsHints, startBox.ParentNode.Context);
|
||||||
}
|
}
|
||||||
else if (_archetype.Elements != null)
|
else if (_archetype.Elements != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -792,8 +792,62 @@ namespace FlaxEditor.Surface.Elements
|
|||||||
if (useCaster)
|
if (useCaster)
|
||||||
{
|
{
|
||||||
// Connect via Caster
|
// Connect via Caster
|
||||||
//AddCaster(oB, iB);
|
const float casterXOffset = 250;
|
||||||
throw new NotImplementedException("AddCaster(..) function");
|
if (Surface.Undo != null && Surface.Undo.Enabled)
|
||||||
|
{
|
||||||
|
bool undoEnabled = Surface.Undo.Enabled;
|
||||||
|
Surface.Undo.Enabled = false;
|
||||||
|
SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode
|
||||||
|
Surface.Undo.Enabled = undoEnabled;
|
||||||
|
if (node is not Archetypes.Tools.AsNode castNode)
|
||||||
|
throw new Exception("Node is not a casting node!");
|
||||||
|
|
||||||
|
// Set the type of the casting node
|
||||||
|
undoEnabled = castNode.Surface.Undo.Enabled;
|
||||||
|
castNode.Surface.Undo.Enabled = false;
|
||||||
|
castNode.SetPickerValue(iB.CurrentType);
|
||||||
|
castNode.Surface.Undo.Enabled = undoEnabled;
|
||||||
|
if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox)
|
||||||
|
throw new NullReferenceException("Casting failed. Cast node is invalid!");
|
||||||
|
|
||||||
|
// We set the position of the cast node here to set it relative to the target nodes input box
|
||||||
|
undoEnabled = castNode.Surface.Undo.Enabled;
|
||||||
|
castNode.Surface.Undo.Enabled = false;
|
||||||
|
var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY));
|
||||||
|
castNode.Location = Surface.Root.PointFromParent(ref wantedOffset);
|
||||||
|
castNode.Surface.Undo.Enabled = undoEnabled;
|
||||||
|
|
||||||
|
var spawnNodeAction = new AddRemoveNodeAction(castNode, true);
|
||||||
|
|
||||||
|
var connectToCastNodeAction = new ConnectBoxesAction(castInputBox, oB, true);
|
||||||
|
castInputBox.CreateConnection(oB);
|
||||||
|
connectToCastNodeAction.End();
|
||||||
|
|
||||||
|
var connectCastToTargetNodeAction = new ConnectBoxesAction(iB, castOutputBox, true);
|
||||||
|
iB.CreateConnection(castOutputBox);
|
||||||
|
connectCastToTargetNodeAction.End();
|
||||||
|
|
||||||
|
Surface.AddBatchedUndoAction(new MultiUndoAction(spawnNodeAction, connectToCastNodeAction, connectCastToTargetNodeAction));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SurfaceNode node = Surface.Context.SpawnNode(7, 22, Float2.Zero); // 22 AsNode, 25 CastNode
|
||||||
|
if (node is not Archetypes.Tools.AsNode castNode)
|
||||||
|
throw new Exception("Node is not a casting node!");
|
||||||
|
|
||||||
|
// Set the type of the casting node
|
||||||
|
castNode.SetPickerValue(iB.CurrentType);
|
||||||
|
if (node.GetBox(0) is not OutputBox castOutputBox || node.GetBox(1) is not InputBox castInputBox)
|
||||||
|
throw new NullReferenceException("Casting failed. Cast node is invalid!");
|
||||||
|
|
||||||
|
// We set the position of the cast node here to set it relative to the target nodes input box
|
||||||
|
var wantedOffset = iB.ParentNode.Location - new Float2(casterXOffset, -(iB.LocalY - castOutputBox.LocalY));
|
||||||
|
castNode.Location = Surface.Root.PointFromParent(ref wantedOffset);
|
||||||
|
|
||||||
|
castInputBox.CreateConnection(oB);
|
||||||
|
iB.CreateConnection(castOutputBox);
|
||||||
|
}
|
||||||
|
Surface.MarkAsEdited();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ namespace FlaxEditor.Surface
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the given type is compatible with the given node archetype. Used for custom nodes
|
/// Checks if the given type is compatible with the given node archetype. Used for custom nodes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint);
|
public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint, VisjectSurfaceContext context);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique node type ID within a single group.
|
/// Unique node type ID within a single group.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using FlaxEditor.Surface.Elements;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
namespace FlaxEditor.Surface
|
namespace FlaxEditor.Surface
|
||||||
@@ -135,8 +136,25 @@ namespace FlaxEditor.Surface
|
|||||||
endPos = _lastInstigatorUnderMouse.ConnectionOrigin;
|
endPos = _lastInstigatorUnderMouse.ConnectionOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Float2 actualStartPos = startPos;
|
||||||
|
Float2 actualEndPos = endPos;
|
||||||
|
|
||||||
|
if (_connectionInstigator is Archetypes.Tools.RerouteNode)
|
||||||
|
{
|
||||||
|
if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true})
|
||||||
|
{
|
||||||
|
actualStartPos = endPos;
|
||||||
|
actualEndPos = startPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_connectionInstigator is Box { IsOutput: false })
|
||||||
|
{
|
||||||
|
actualStartPos = endPos;
|
||||||
|
actualEndPos = startPos;
|
||||||
|
}
|
||||||
|
|
||||||
// Draw connection
|
// Draw connection
|
||||||
_connectionInstigator.DrawConnectingLine(ref startPos, ref endPos, ref lineColor);
|
_connectionInstigator.DrawConnectingLine(ref actualStartPos, ref actualEndPos, ref lineColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -285,6 +285,7 @@ bool TerrainTools::GenerateTerrain(Terrain* terrain, const Int2& numberOfPatches
|
|||||||
|
|
||||||
StringAnsi TerrainTools::SerializePatch(Terrain* terrain, const Int2& patchCoord)
|
StringAnsi TerrainTools::SerializePatch(Terrain* terrain, const Int2& patchCoord)
|
||||||
{
|
{
|
||||||
|
CHECK_RETURN(terrain, StringAnsi::Empty);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK_RETURN(patch, StringAnsi::Empty);
|
CHECK_RETURN(patch, StringAnsi::Empty);
|
||||||
|
|
||||||
@@ -300,6 +301,7 @@ StringAnsi TerrainTools::SerializePatch(Terrain* terrain, const Int2& patchCoord
|
|||||||
|
|
||||||
void TerrainTools::DeserializePatch(Terrain* terrain, const Int2& patchCoord, const StringAnsiView& value)
|
void TerrainTools::DeserializePatch(Terrain* terrain, const Int2& patchCoord, const StringAnsiView& value)
|
||||||
{
|
{
|
||||||
|
CHECK(terrain);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK(patch);
|
CHECK(patch);
|
||||||
|
|
||||||
@@ -317,27 +319,31 @@ void TerrainTools::DeserializePatch(Terrain* terrain, const Int2& patchCoord, co
|
|||||||
|
|
||||||
bool TerrainTools::InitializePatch(Terrain* terrain, const Int2& patchCoord)
|
bool TerrainTools::InitializePatch(Terrain* terrain, const Int2& patchCoord)
|
||||||
{
|
{
|
||||||
|
CHECK_RETURN(terrain, true);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK_RETURN(patch, true);
|
CHECK_RETURN(patch, true);
|
||||||
return patch->InitializeHeightMap();
|
return patch->InitializeHeightMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerrainTools::ModifyHeightMap(Terrain* terrain, const Int2& patchCoord, float* samples, const Int2& offset, const Int2& size)
|
bool TerrainTools::ModifyHeightMap(Terrain* terrain, const Int2& patchCoord, const float* samples, const Int2& offset, const Int2& size)
|
||||||
{
|
{
|
||||||
|
CHECK_RETURN(terrain, true);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK_RETURN(patch, true);
|
CHECK_RETURN(patch, true);
|
||||||
return patch->ModifyHeightMap(samples, offset, size);
|
return patch->ModifyHeightMap(samples, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerrainTools::ModifyHolesMask(Terrain* terrain, const Int2& patchCoord, byte* samples, const Int2& offset, const Int2& size)
|
bool TerrainTools::ModifyHolesMask(Terrain* terrain, const Int2& patchCoord, const byte* samples, const Int2& offset, const Int2& size)
|
||||||
{
|
{
|
||||||
|
CHECK_RETURN(terrain, true);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK_RETURN(patch, true);
|
CHECK_RETURN(patch, true);
|
||||||
return patch->ModifyHolesMask(samples, offset, size);
|
return patch->ModifyHolesMask(samples, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerrainTools::ModifySplatMap(Terrain* terrain, const Int2& patchCoord, int32 index, Color32* samples, const Int2& offset, const Int2& size)
|
bool TerrainTools::ModifySplatMap(Terrain* terrain, const Int2& patchCoord, int32 index, const Color32* samples, const Int2& offset, const Int2& size)
|
||||||
{
|
{
|
||||||
|
CHECK_RETURN(terrain, true);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK_RETURN(patch, true);
|
CHECK_RETURN(patch, true);
|
||||||
CHECK_RETURN(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT, true);
|
CHECK_RETURN(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT, true);
|
||||||
@@ -346,6 +352,7 @@ bool TerrainTools::ModifySplatMap(Terrain* terrain, const Int2& patchCoord, int3
|
|||||||
|
|
||||||
float* TerrainTools::GetHeightmapData(Terrain* terrain, const Int2& patchCoord)
|
float* TerrainTools::GetHeightmapData(Terrain* terrain, const Int2& patchCoord)
|
||||||
{
|
{
|
||||||
|
CHECK_RETURN(terrain, nullptr);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK_RETURN(patch, nullptr);
|
CHECK_RETURN(patch, nullptr);
|
||||||
return patch->GetHeightmapData();
|
return patch->GetHeightmapData();
|
||||||
@@ -353,6 +360,7 @@ float* TerrainTools::GetHeightmapData(Terrain* terrain, const Int2& patchCoord)
|
|||||||
|
|
||||||
byte* TerrainTools::GetHolesMaskData(Terrain* terrain, const Int2& patchCoord)
|
byte* TerrainTools::GetHolesMaskData(Terrain* terrain, const Int2& patchCoord)
|
||||||
{
|
{
|
||||||
|
CHECK_RETURN(terrain, nullptr);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK_RETURN(patch, nullptr);
|
CHECK_RETURN(patch, nullptr);
|
||||||
return patch->GetHolesMaskData();
|
return patch->GetHolesMaskData();
|
||||||
@@ -360,6 +368,7 @@ byte* TerrainTools::GetHolesMaskData(Terrain* terrain, const Int2& patchCoord)
|
|||||||
|
|
||||||
Color32* TerrainTools::GetSplatMapData(Terrain* terrain, const Int2& patchCoord, int32 index)
|
Color32* TerrainTools::GetSplatMapData(Terrain* terrain, const Int2& patchCoord, int32 index)
|
||||||
{
|
{
|
||||||
|
CHECK_RETURN(terrain, nullptr);
|
||||||
auto patch = terrain->GetPatch(patchCoord);
|
auto patch = terrain->GetPatch(patchCoord);
|
||||||
CHECK_RETURN(patch, nullptr);
|
CHECK_RETURN(patch, nullptr);
|
||||||
return patch->GetSplatMapData(index);
|
return patch->GetSplatMapData(index);
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(TerrainTools);
|
|||||||
/// <param name="offset">The offset from the first row and column of the heightmap data (offset destination x and z start position).</param>
|
/// <param name="offset">The offset from the first row and column of the heightmap data (offset destination x and z start position).</param>
|
||||||
/// <param name="size">The size of the heightmap to modify (x and z). Amount of samples in each direction.</param>
|
/// <param name="size">The size of the heightmap to modify (x and z). Amount of samples in each direction.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
API_FUNCTION() static bool ModifyHeightMap(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, float* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size);
|
API_FUNCTION() static bool ModifyHeightMap(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, const float* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Modifies the terrain patch holes mask with the given samples.
|
/// Modifies the terrain patch holes mask with the given samples.
|
||||||
@@ -79,7 +79,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(TerrainTools);
|
|||||||
/// <param name="offset">The offset from the first row and column of the mask data (offset destination x and z start position).</param>
|
/// <param name="offset">The offset from the first row and column of the mask data (offset destination x and z start position).</param>
|
||||||
/// <param name="size">The size of the mask to modify (x and z). Amount of samples in each direction.</param>
|
/// <param name="size">The size of the mask to modify (x and z). Amount of samples in each direction.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
API_FUNCTION() static bool ModifyHolesMask(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, byte* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size);
|
API_FUNCTION() static bool ModifyHolesMask(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, const byte* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Modifies the terrain patch splat map (layers mask) with the given samples.
|
/// Modifies the terrain patch splat map (layers mask) with the given samples.
|
||||||
@@ -91,7 +91,7 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(TerrainTools);
|
|||||||
/// <param name="offset">The offset from the first row and column of the splatmap data (offset destination x and z start position).</param>
|
/// <param name="offset">The offset from the first row and column of the splatmap data (offset destination x and z start position).</param>
|
||||||
/// <param name="size">The size of the splatmap to modify (x and z). Amount of samples in each direction.</param>
|
/// <param name="size">The size of the splatmap to modify (x and z). Amount of samples in each direction.</param>
|
||||||
/// <returns>True if failed, otherwise false.</returns>
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
API_FUNCTION() static bool ModifySplatMap(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, int32 index, Color32* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size);
|
API_FUNCTION() static bool ModifySplatMap(Terrain* terrain, API_PARAM(Ref) const Int2& patchCoord, int32 index, const Color32* samples, API_PARAM(Ref) const Int2& offset, API_PARAM(Ref) const Int2& size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the raw pointer to the heightmap data (cached internally by the c++ core in editor).
|
/// Gets the raw pointer to the heightmap data (cached internally by the c++ core in editor).
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using FlaxEditor.Content.Settings;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.GUI.Input;
|
using FlaxEditor.GUI.Input;
|
||||||
using FlaxEditor.Options;
|
using FlaxEditor.Options;
|
||||||
@@ -9,6 +10,8 @@ using FlaxEditor.Viewport.Cameras;
|
|||||||
using FlaxEditor.Viewport.Widgets;
|
using FlaxEditor.Viewport.Widgets;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using JsonSerializer = FlaxEngine.Json.JsonSerializer;
|
||||||
|
|
||||||
namespace FlaxEditor.Viewport
|
namespace FlaxEditor.Viewport
|
||||||
{
|
{
|
||||||
@@ -486,10 +489,63 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// View Layers
|
||||||
|
{
|
||||||
|
var viewLayers = ViewWidgetButtonMenu.AddChildMenu("View Layers").ContextMenu;
|
||||||
|
viewLayers.AddButton("Copy layers", () => Clipboard.Text = JsonSerializer.Serialize(Task.View.RenderLayersMask));
|
||||||
|
viewLayers.AddButton("Paste layers", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Task.ViewLayersMask = JsonSerializer.Deserialize<LayersMask>(Clipboard.Text);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
});
|
||||||
|
viewLayers.AddButton("Reset layers", () => Task.ViewLayersMask = LayersMask.Default).Icon = Editor.Instance.Icons.Rotate32;
|
||||||
|
viewLayers.AddButton("Disable layers", () => Task.ViewLayersMask = new LayersMask(0)).Icon = Editor.Instance.Icons.Rotate32;
|
||||||
|
viewLayers.AddSeparator();
|
||||||
|
var layers = LayersAndTagsSettings.GetCurrentLayers();
|
||||||
|
if (layers != null && layers.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < layers.Length; i++)
|
||||||
|
{
|
||||||
|
var layer = layers[i];
|
||||||
|
var button = viewLayers.AddButton(layer);
|
||||||
|
button.CloseMenuOnClick = false;
|
||||||
|
button.Tag = 1 << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
viewLayers.ButtonClicked += button =>
|
||||||
|
{
|
||||||
|
if (button.Tag != null)
|
||||||
|
{
|
||||||
|
int layerIndex = (int)button.Tag;
|
||||||
|
LayersMask mask = new LayersMask(layerIndex);
|
||||||
|
Task.ViewLayersMask ^= mask;
|
||||||
|
button.Icon = (Task.ViewLayersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
viewLayers.VisibleChanged += WidgetViewLayersShowHide;
|
||||||
|
}
|
||||||
|
|
||||||
// View Flags
|
// View Flags
|
||||||
{
|
{
|
||||||
var viewFlags = ViewWidgetButtonMenu.AddChildMenu("View Flags").ContextMenu;
|
var viewFlags = ViewWidgetButtonMenu.AddChildMenu("View Flags").ContextMenu;
|
||||||
|
viewFlags.AddButton("Copy flags", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewFlags));
|
||||||
|
viewFlags.AddButton("Paste flags", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Task.ViewFlags = JsonSerializer.Deserialize<ViewFlags>(Clipboard.Text);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
});
|
||||||
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
|
viewFlags.AddButton("Reset flags", () => Task.ViewFlags = ViewFlags.DefaultEditor).Icon = Editor.Instance.Icons.Rotate32;
|
||||||
|
viewFlags.AddButton("Disable flags", () => Task.ViewFlags = ViewFlags.None).Icon = Editor.Instance.Icons.Rotate32;
|
||||||
viewFlags.AddSeparator();
|
viewFlags.AddSeparator();
|
||||||
for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++)
|
for (int i = 0; i < EditorViewportViewFlagsValues.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -504,7 +560,7 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
var v = (ViewFlags)button.Tag;
|
var v = (ViewFlags)button.Tag;
|
||||||
Task.ViewFlags ^= v;
|
Task.ViewFlags ^= v;
|
||||||
button.Icon = (Task.View.Flags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
button.Icon = (Task.ViewFlags & v) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
viewFlags.VisibleChanged += WidgetViewFlagsShowHide;
|
viewFlags.VisibleChanged += WidgetViewFlagsShowHide;
|
||||||
@@ -513,6 +569,18 @@ namespace FlaxEditor.Viewport
|
|||||||
// Debug View
|
// Debug View
|
||||||
{
|
{
|
||||||
var debugView = ViewWidgetButtonMenu.AddChildMenu("Debug View").ContextMenu;
|
var debugView = ViewWidgetButtonMenu.AddChildMenu("Debug View").ContextMenu;
|
||||||
|
debugView.AddButton("Copy view", () => Clipboard.Text = JsonSerializer.Serialize(Task.ViewMode));
|
||||||
|
debugView.AddButton("Paste view", () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Task.ViewMode = JsonSerializer.Deserialize<ViewMode>(Clipboard.Text);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
});
|
||||||
|
debugView.AddSeparator();
|
||||||
for (int i = 0; i < EditorViewportViewModeValues.Length; i++)
|
for (int i = 0; i < EditorViewportViewModeValues.Length; i++)
|
||||||
{
|
{
|
||||||
ref var v = ref EditorViewportViewModeValues[i];
|
ref var v = ref EditorViewportViewModeValues[i];
|
||||||
@@ -954,7 +1022,7 @@ namespace FlaxEditor.Viewport
|
|||||||
|
|
||||||
// Center mouse position if it's too close to the edge
|
// Center mouse position if it's too close to the edge
|
||||||
var size = Size;
|
var size = Size;
|
||||||
var center = size * 0.5f;
|
var center = Float2.Round(size * 0.5f);
|
||||||
if (Mathf.Abs(_viewMousePos.X - center.X) > center.X * 0.8f || Mathf.Abs(_viewMousePos.Y - center.Y) > center.Y * 0.8f)
|
if (Mathf.Abs(_viewMousePos.X - center.X) > center.X * 0.8f || Mathf.Abs(_viewMousePos.Y - center.Y) > center.Y * 0.8f)
|
||||||
{
|
{
|
||||||
_viewMousePos = center;
|
_viewMousePos = center;
|
||||||
@@ -1084,9 +1152,9 @@ namespace FlaxEditor.Viewport
|
|||||||
_isVirtualMouseRightDown = false; // Cancel when mouse right or escape is pressed
|
_isVirtualMouseRightDown = false; // Cancel when mouse right or escape is pressed
|
||||||
if (_wasVirtualMouseRightDown)
|
if (_wasVirtualMouseRightDown)
|
||||||
wasControllingMouse = true;
|
wasControllingMouse = true;
|
||||||
if (_isVirtualMouseRightDown)
|
if (_isVirtualMouseRightDown)
|
||||||
_isControllingMouse = _isVirtualMouseRightDown;
|
_isControllingMouse = _isVirtualMouseRightDown;
|
||||||
|
|
||||||
if (wasControllingMouse != _isControllingMouse)
|
if (wasControllingMouse != _isControllingMouse)
|
||||||
{
|
{
|
||||||
if (_isControllingMouse)
|
if (_isControllingMouse)
|
||||||
@@ -1591,6 +1659,24 @@ namespace FlaxEditor.Viewport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void WidgetViewLayersShowHide(Control cm)
|
||||||
|
{
|
||||||
|
if (cm.Visible == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ccm = (ContextMenu)cm;
|
||||||
|
var layersMask = Task.ViewLayersMask;
|
||||||
|
foreach (var e in ccm.Items)
|
||||||
|
{
|
||||||
|
if (e is ContextMenuButton b && b != null && b.Tag != null)
|
||||||
|
{
|
||||||
|
int layerIndex = (int)b.Tag;
|
||||||
|
LayersMask mask = new LayersMask(layerIndex);
|
||||||
|
b.Icon = (layersMask & mask) != 0 ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private float GetGamepadAxis(GamepadAxis axis)
|
private float GetGamepadAxis(GamepadAxis axis)
|
||||||
{
|
{
|
||||||
var value = FlaxEngine.Input.GetGamepadAxis(InputGamepadIndex.All, axis);
|
var value = FlaxEngine.Input.GetGamepadAxis(InputGamepadIndex.All, axis);
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_window = window;
|
_window = window;
|
||||||
|
|
||||||
// Try to restore target asset AudioClip import options (useful for fast reimport)
|
// Try to restore target asset AudioClip import options (useful for fast reimport)
|
||||||
AudioImportSettings.TryRestore(ref ImportSettings, window.Item.Path);
|
Editor.TryRestoreImportOptions(ref ImportSettings.Settings, window.Item.Path);
|
||||||
|
|
||||||
// Prepare restore data
|
// Prepare restore data
|
||||||
PeekState();
|
PeekState();
|
||||||
@@ -134,6 +134,11 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Reimport()
|
public void Reimport()
|
||||||
{
|
{
|
||||||
|
if (_window?._previewSource != null)
|
||||||
|
{
|
||||||
|
_window._previewSource.Stop();
|
||||||
|
_window.UpdateToolstrip();
|
||||||
|
}
|
||||||
Editor.Instance.ContentImporting.Reimport((BinaryAssetItem)_window.Item, ImportSettings, true);
|
Editor.Instance.ContentImporting.Reimport((BinaryAssetItem)_window.Item, ImportSettings, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,11 +80,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
{
|
{
|
||||||
if (!IsEdited)
|
if (!IsEdited)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_asset.WaitForLoaded())
|
if (_asset.WaitForLoaded())
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (Editor.SaveJsonAsset(_item.Path, _object))
|
if (Editor.SaveJsonAsset(_item.Path, _object))
|
||||||
{
|
{
|
||||||
@@ -137,7 +134,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_presenter.Select(_object);
|
_presenter.Select(_object);
|
||||||
|
|
||||||
if (_typeText != null)
|
if (_typeText != null)
|
||||||
_typeText.Dispose();
|
_typeText.Dispose();
|
||||||
var typeText = new ClickableLabel
|
var typeText = new ClickableLabel
|
||||||
@@ -152,7 +149,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
typeText.LocalY += (_toolstrip.Height - typeText.Height) * 0.5f;
|
typeText.LocalY += (_toolstrip.Height - typeText.Height) * 0.5f;
|
||||||
typeText.RightClick = () => Clipboard.Text = Asset.DataTypeName;
|
typeText.RightClick = () => Clipboard.Text = Asset.DataTypeName;
|
||||||
_typeText = typeText;
|
_typeText = typeText;
|
||||||
|
|
||||||
_undo.Clear();
|
_undo.Clear();
|
||||||
ClearEditedFlag();
|
ClearEditedFlag();
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
contextMenu.AddSeparator();
|
contextMenu.AddSeparator();
|
||||||
|
|
||||||
b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection));
|
b = contextMenu.AddButton("Create Prefab", () => Editor.Prefabs.CreatePrefab(Selection, this));
|
||||||
b.Enabled = isSingleActorSelected &&
|
b.Enabled = isSingleActorSelected &&
|
||||||
(Selection[0] as ActorNode).CanCreatePrefab &&
|
(Selection[0] as ActorNode).CanCreatePrefab &&
|
||||||
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
||||||
@@ -313,7 +313,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
if (showCustomNodeOptions)
|
if (showCustomNodeOptions)
|
||||||
{
|
{
|
||||||
Selection[0].OnContextMenu(contextMenu);
|
Selection[0].OnContextMenu(contextMenu, this);
|
||||||
}
|
}
|
||||||
ContextMenuShow?.Invoke(contextMenu);
|
ContextMenuShow?.Invoke(contextMenu);
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public PrefabWindowViewport Viewport => _viewport;
|
public PrefabWindowViewport Viewport => _viewport;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the prefab objects properties editor.
|
||||||
|
/// </summary>
|
||||||
|
public CustomEditorPresenter Presenter => _propertiesEditor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the undo system used by this window for changes tracking.
|
/// Gets the undo system used by this window for changes tracking.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -146,7 +151,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
Graph = new LocalSceneGraph(new CustomRootNode(this));
|
Graph = new LocalSceneGraph(new CustomRootNode(this));
|
||||||
_tree = new PrefabTree
|
_tree = new PrefabTree
|
||||||
{
|
{
|
||||||
Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node
|
Margin = new Margin(0.0f, 0.0f, -16.0f, _treePanel.ScrollBarsSize), // Hide root node
|
||||||
IsScrollable = true,
|
IsScrollable = true,
|
||||||
};
|
};
|
||||||
_tree.AddChild(Graph.Root.TreeNode);
|
_tree.AddChild(Graph.Root.TreeNode);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.Assertions;
|
using FlaxEngine.Assertions;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Json;
|
using FlaxEngine.Json;
|
||||||
|
|
||||||
namespace FlaxEditor.Windows
|
namespace FlaxEditor.Windows
|
||||||
@@ -145,9 +147,18 @@ namespace FlaxEditor.Windows
|
|||||||
cm.AddButton("Refresh all thumbnails", RefreshViewItemsThumbnails);
|
cm.AddButton("Refresh all thumbnails", RefreshViewItemsThumbnails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cm.AddSeparator();
|
||||||
|
|
||||||
|
// Check if is source folder to add new module
|
||||||
|
if (folder?.ParentFolder?.Node is ProjectTreeNode parentFolderNode && folder.Node == parentFolderNode.Source)
|
||||||
|
{
|
||||||
|
var button = cm.AddButton("New module");
|
||||||
|
button.CloseMenuOnClick = false;
|
||||||
|
button.Clicked += () => NewModule(button, parentFolderNode.Source.Path);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode))
|
if (!isRootFolder && !(item is ContentFolder projectFolder && projectFolder.Node is ProjectTreeNode))
|
||||||
{
|
{
|
||||||
cm.AddSeparator();
|
|
||||||
cm.AddButton("New folder", NewFolder);
|
cm.AddButton("New folder", NewFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,5 +322,138 @@ namespace FlaxEditor.Windows
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void NewModule(ContextMenuButton button, string path)
|
||||||
|
{
|
||||||
|
var popup = new ContextMenuBase
|
||||||
|
{
|
||||||
|
Size = new Float2(230, 125),
|
||||||
|
ClipChildren = false,
|
||||||
|
CullChildren = false,
|
||||||
|
};
|
||||||
|
popup.Show(button, new Float2(button.Width, 0));
|
||||||
|
|
||||||
|
var nameLabel = new Label
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Name",
|
||||||
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
|
};
|
||||||
|
nameLabel.LocalX += 10;
|
||||||
|
nameLabel.LocalY += 10;
|
||||||
|
|
||||||
|
var nameTextBox = new TextBox
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
WatermarkText = "Module Name",
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
IsMultiline = false,
|
||||||
|
};
|
||||||
|
nameTextBox.LocalX += 100;
|
||||||
|
nameTextBox.LocalY += 10;
|
||||||
|
var defaultTextBoxBorderColor = nameTextBox.BorderColor;
|
||||||
|
var defaultTextBoxBorderSelectedColor = nameTextBox.BorderSelectedColor;
|
||||||
|
nameTextBox.TextChanged += () =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(nameTextBox.Text))
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = defaultTextBoxBorderColor;
|
||||||
|
nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluginPath = Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text);
|
||||||
|
if (Directory.Exists(pluginPath))
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = Color.Red;
|
||||||
|
nameTextBox.BorderSelectedColor = Color.Red;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nameTextBox.BorderColor = defaultTextBoxBorderColor;
|
||||||
|
nameTextBox.BorderSelectedColor = defaultTextBoxBorderSelectedColor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var editorLabel = new Label
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Editor",
|
||||||
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
|
};
|
||||||
|
editorLabel.LocalX += 10;
|
||||||
|
editorLabel.LocalY += 35;
|
||||||
|
|
||||||
|
var editorCheckBox = new CheckBox
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
};
|
||||||
|
editorCheckBox.LocalY += 35;
|
||||||
|
editorCheckBox.LocalX += 100;
|
||||||
|
|
||||||
|
var cppLabel = new Label
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "C++",
|
||||||
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
|
};
|
||||||
|
cppLabel.LocalX += 10;
|
||||||
|
cppLabel.LocalY += 60;
|
||||||
|
|
||||||
|
var cppCheckBox = new CheckBox
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
};
|
||||||
|
cppCheckBox.LocalY += 60;
|
||||||
|
cppCheckBox.LocalX += 100;
|
||||||
|
|
||||||
|
var submitButton = new Button
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Create",
|
||||||
|
Width = 70,
|
||||||
|
};
|
||||||
|
submitButton.LocalX += 40;
|
||||||
|
submitButton.LocalY += 90;
|
||||||
|
submitButton.Clicked += () =>
|
||||||
|
{
|
||||||
|
// TODO: Check all modules in project including plugins
|
||||||
|
if (Directory.Exists(Path.Combine(Globals.ProjectFolder, "Source", nameTextBox.Text)))
|
||||||
|
{
|
||||||
|
Editor.LogWarning("Cannot create module due to name conflict.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Editor.CodeEditing.CreateModule(path, nameTextBox.Text, editorCheckBox.Checked, cppCheckBox.Checked);
|
||||||
|
nameTextBox.Clear();
|
||||||
|
editorCheckBox.Checked = false;
|
||||||
|
cppCheckBox.Checked = false;
|
||||||
|
popup.Hide();
|
||||||
|
button.ParentContextMenu.Hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
var cancelButton = new Button
|
||||||
|
{
|
||||||
|
Parent = popup,
|
||||||
|
AnchorPreset = AnchorPresets.TopLeft,
|
||||||
|
Text = "Cancel",
|
||||||
|
Width = 70,
|
||||||
|
};
|
||||||
|
cancelButton.LocalX += 120;
|
||||||
|
cancelButton.LocalY += 90;
|
||||||
|
cancelButton.Clicked += () =>
|
||||||
|
{
|
||||||
|
nameTextBox.Clear();
|
||||||
|
editorCheckBox.Checked = false;
|
||||||
|
cppCheckBox.Checked = false;
|
||||||
|
popup.Hide();
|
||||||
|
button.ParentContextMenu.Hide();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ namespace FlaxEditor.Windows
|
|||||||
b.Checked = ShowPluginsFiles;
|
b.Checked = ShowPluginsFiles;
|
||||||
b.CloseMenuOnClick = false;
|
b.CloseMenuOnClick = false;
|
||||||
b.AutoCheck = true;
|
b.AutoCheck = true;
|
||||||
|
|
||||||
b = show.ContextMenu.AddButton("Generated files", () => ShowGeneratedFiles = !ShowGeneratedFiles);
|
b = show.ContextMenu.AddButton("Generated files", () => ShowGeneratedFiles = !ShowGeneratedFiles);
|
||||||
b.TooltipText = "Shows generated files";
|
b.TooltipText = "Shows generated files";
|
||||||
b.Checked = ShowGeneratedFiles;
|
b.Checked = ShowGeneratedFiles;
|
||||||
@@ -755,7 +755,7 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stars creating the folder.
|
/// Starts creating the folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void NewFolder()
|
public void NewFolder()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
if (showCustomNodeOptions)
|
if (showCustomNodeOptions)
|
||||||
{
|
{
|
||||||
Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu);
|
Editor.SceneEditing.Selection[0].OnContextMenu(contextMenu, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextMenuShow?.Invoke(contextMenu);
|
ContextMenuShow?.Invoke(contextMenu);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace FlaxEditor.Windows
|
|||||||
root.TreeNode.Expand();
|
root.TreeNode.Expand();
|
||||||
_tree = new Tree(true)
|
_tree = new Tree(true)
|
||||||
{
|
{
|
||||||
Margin = new Margin(0.0f, 0.0f, -16.0f, 0.0f), // Hide root node
|
Margin = new Margin(0.0f, 0.0f, -16.0f, _sceneTreePanel.ScrollBarsSize), // Hide root node
|
||||||
IsScrollable = true,
|
IsScrollable = true,
|
||||||
};
|
};
|
||||||
_tree.AddChild(root.TreeNode);
|
_tree.AddChild(root.TreeNode);
|
||||||
|
|||||||
@@ -54,9 +54,7 @@ namespace FlaxEditor.Windows.Search
|
|||||||
_selectedItem.BackgroundColor = Style.Current.BackgroundSelected;
|
_selectedItem.BackgroundColor = Style.Current.BackgroundSelected;
|
||||||
if (_matchedItems.Count > VisibleItemCount)
|
if (_matchedItems.Count > VisibleItemCount)
|
||||||
{
|
{
|
||||||
_resultPanel.VScrollBar.SmoothingScale = 0;
|
_resultPanel.ScrollViewTo(_selectedItem, true);
|
||||||
_resultPanel.ScrollViewTo(_selectedItem);
|
|
||||||
_resultPanel.VScrollBar.SmoothingScale = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,14 @@ const Char* SplashScreenQuotes[] =
|
|||||||
TEXT("Who is signing all these integers?!"),
|
TEXT("Who is signing all these integers?!"),
|
||||||
TEXT("Flax fact: Flax was called Celelej once."),
|
TEXT("Flax fact: Flax was called Celelej once."),
|
||||||
TEXT("Changing text overflow setti-"),
|
TEXT("Changing text overflow setti-"),
|
||||||
|
TEXT("Testing tests"),
|
||||||
|
TEXT("Free hugs"),
|
||||||
|
TEXT("Think outside the box"),
|
||||||
|
TEXT("Let's make something fantastic"),
|
||||||
|
TEXT("Be brave"),
|
||||||
|
TEXT("Drum roll please"),
|
||||||
|
TEXT("Good Luck Have Fun"),
|
||||||
|
TEXT("GG Well Played"),
|
||||||
};
|
};
|
||||||
|
|
||||||
SplashScreen::~SplashScreen()
|
SplashScreen::~SplashScreen()
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ Asset::LoadResult AudioClip::load()
|
|||||||
|
|
||||||
#if !BUILD_RELEASE
|
#if !BUILD_RELEASE
|
||||||
// Validate buffer start times
|
// Validate buffer start times
|
||||||
if (Math::NotNearEqual(_buffersStartTimes[_totalChunks], GetLength()))
|
if (!Math::NearEqual(_buffersStartTimes[_totalChunks], GetLength(), 1.0f / 60.0f))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Invalid audio buffers data size. Expected length: {0}s", GetLength());
|
LOG(Warning, "Invalid audio buffers data size. Expected length: {0}s", GetLength());
|
||||||
for (int32 i = 0; i < _totalChunks + 1; i++)
|
for (int32 i = 0; i < _totalChunks + 1; i++)
|
||||||
@@ -454,14 +454,12 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AudioFormat::Raw:
|
case AudioFormat::Raw:
|
||||||
{
|
|
||||||
data = Span<byte>(chunk->Get(), chunk->Size());
|
data = Span<byte>(chunk->Get(), chunk->Size());
|
||||||
}
|
break;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
info.NumSamples = data.Length() / bytesPerSample;
|
info.NumSamples = Math::AlignDown(data.Length() / bytesPerSample, info.NumChannels * bytesPerSample);
|
||||||
|
|
||||||
// Convert to Mono if used as 3D source and backend doesn't support it
|
// Convert to Mono if used as 3D source and backend doesn't support it
|
||||||
if (Is3D() && info.NumChannels > 1 && EnumHasNoneFlags(AudioBackend::Features(), AudioBackend::FeatureFlags::SpatialMultiChannel))
|
if (Is3D() && info.NumChannels > 1 && EnumHasNoneFlags(AudioBackend::Features(), AudioBackend::FeatureFlags::SpatialMultiChannel))
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ void AudioSource::Stop()
|
|||||||
|
|
||||||
float AudioSource::GetTime() const
|
float AudioSource::GetTime() const
|
||||||
{
|
{
|
||||||
if (_state == States::Stopped || SourceIDs.IsEmpty())
|
if (_state == States::Stopped || SourceIDs.IsEmpty() || !Clip->IsLoaded())
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
float time = AudioBackend::Source::GetCurrentBufferTime(this);
|
float time = AudioBackend::Source::GetCurrentBufferTime(this);
|
||||||
@@ -265,6 +265,7 @@ void AudioSource::Cleanup()
|
|||||||
void AudioSource::OnClipChanged()
|
void AudioSource::OnClipChanged()
|
||||||
{
|
{
|
||||||
Stop();
|
Stop();
|
||||||
|
_clipChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioSource::OnClipLoaded()
|
void AudioSource::OnClipLoaded()
|
||||||
@@ -318,6 +319,12 @@ void AudioSource::SetNonStreamingBuffer()
|
|||||||
|
|
||||||
void AudioSource::PlayInternal()
|
void AudioSource::PlayInternal()
|
||||||
{
|
{
|
||||||
|
if (_clipChanged && SourceIDs.HasItems())
|
||||||
|
{
|
||||||
|
// If clip was changed between source setup (OnEnable) and actual playback start then ensure to flush any runtime properties with the audio backend
|
||||||
|
_clipChanged = false;
|
||||||
|
AudioBackend::Source::SpatialSetupChanged(this);
|
||||||
|
}
|
||||||
AudioBackend::Source::Play(this);
|
AudioBackend::Source::Play(this);
|
||||||
|
|
||||||
_isActuallyPlayingSth = true;
|
_isActuallyPlayingSth = true;
|
||||||
@@ -482,6 +489,7 @@ void AudioSource::OnEnable()
|
|||||||
{
|
{
|
||||||
_prevPos = GetPosition();
|
_prevPos = GetPosition();
|
||||||
_velocity = Vector3::Zero;
|
_velocity = Vector3::Zero;
|
||||||
|
_clipChanged = false;
|
||||||
|
|
||||||
Audio::OnAddSource(this);
|
Audio::OnAddSource(this);
|
||||||
GetScene()->Ticking.Update.AddTick<AudioSource, &AudioSource::Update>(this);
|
GetScene()->Ticking.Update.AddTick<AudioSource, &AudioSource::Update>(this);
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ private:
|
|||||||
bool _loop;
|
bool _loop;
|
||||||
bool _playOnStart;
|
bool _playOnStart;
|
||||||
bool _allowSpatialization;
|
bool _allowSpatialization;
|
||||||
|
bool _clipChanged = false;
|
||||||
|
|
||||||
bool _isActuallyPlayingSth = false;
|
bool _isActuallyPlayingSth = false;
|
||||||
bool _needToUpdateStreamingBuffers = false;
|
bool _needToUpdateStreamingBuffers = false;
|
||||||
|
|||||||
@@ -228,11 +228,9 @@ namespace ALC
|
|||||||
ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
|
ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
|
||||||
{
|
{
|
||||||
// TODO: cache enum values in Init()??
|
// TODO: cache enum values in Init()??
|
||||||
|
|
||||||
switch (bitDepth)
|
switch (bitDepth)
|
||||||
{
|
{
|
||||||
case 8:
|
case 8:
|
||||||
{
|
|
||||||
switch (numChannels)
|
switch (numChannels)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
@@ -247,13 +245,8 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
|
|||||||
return alGetEnumValue("AL_FORMAT_61CHN8");
|
return alGetEnumValue("AL_FORMAT_61CHN8");
|
||||||
case 8:
|
case 8:
|
||||||
return alGetEnumValue("AL_FORMAT_71CHN8");
|
return alGetEnumValue("AL_FORMAT_71CHN8");
|
||||||
default:
|
|
||||||
CRASH;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case 16:
|
case 16:
|
||||||
{
|
|
||||||
switch (numChannels)
|
switch (numChannels)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
@@ -268,19 +261,22 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
|
|||||||
return alGetEnumValue("AL_FORMAT_61CHN16");
|
return alGetEnumValue("AL_FORMAT_61CHN16");
|
||||||
case 8:
|
case 8:
|
||||||
return alGetEnumValue("AL_FORMAT_71CHN16");
|
return alGetEnumValue("AL_FORMAT_71CHN16");
|
||||||
default:
|
|
||||||
CRASH;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case 32:
|
case 32:
|
||||||
{
|
|
||||||
switch (numChannels)
|
switch (numChannels)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
|
#ifdef AL_FORMAT_MONO_FLOAT32
|
||||||
|
return AL_FORMAT_MONO_FLOAT32;
|
||||||
|
#else
|
||||||
return alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
|
return alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
|
||||||
|
#endif
|
||||||
case 2:
|
case 2:
|
||||||
|
#ifdef AL_FORMAT_STEREO_FLOAT32
|
||||||
|
return AL_FORMAT_STEREO_FLOAT32;
|
||||||
|
#else
|
||||||
return alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
|
return alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
|
||||||
|
#endif
|
||||||
case 4:
|
case 4:
|
||||||
return alGetEnumValue("AL_FORMAT_QUAD32");
|
return alGetEnumValue("AL_FORMAT_QUAD32");
|
||||||
case 6:
|
case 6:
|
||||||
@@ -289,15 +285,9 @@ ALenum GetOpenALBufferFormat(uint32 numChannels, uint32 bitDepth)
|
|||||||
return alGetEnumValue("AL_FORMAT_61CHN32");
|
return alGetEnumValue("AL_FORMAT_61CHN32");
|
||||||
case 8:
|
case 8:
|
||||||
return alGetEnumValue("AL_FORMAT_71CHN32");
|
return alGetEnumValue("AL_FORMAT_71CHN32");
|
||||||
default:
|
|
||||||
CRASH;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
return 0;
|
||||||
CRASH;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBackendOAL::Listener_OnAdd(AudioListener* listener)
|
void AudioBackendOAL::Listener_OnAdd(AudioListener* listener)
|
||||||
@@ -607,7 +597,8 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
|
|||||||
{
|
{
|
||||||
PROFILE_CPU();
|
PROFILE_CPU();
|
||||||
|
|
||||||
// TODO: maybe use temporary buffers per thread to reduce dynamic allocations when uploading data to OpenAL?
|
// Pick the format for the audio data (it might not be supported natively)
|
||||||
|
ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
|
||||||
|
|
||||||
// Mono or stereo
|
// Mono or stereo
|
||||||
if (info.NumChannels <= 2)
|
if (info.NumChannels <= 2)
|
||||||
@@ -618,28 +609,23 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
|
|||||||
{
|
{
|
||||||
const uint32 bufferSize = info.NumSamples * sizeof(float);
|
const uint32 bufferSize = info.NumSamples * sizeof(float);
|
||||||
float* sampleBufferFloat = (float*)Allocator::Allocate(bufferSize);
|
float* sampleBufferFloat = (float*)Allocator::Allocate(bufferSize);
|
||||||
|
|
||||||
AudioTool::ConvertToFloat(samples, info.BitDepth, sampleBufferFloat, info.NumSamples);
|
AudioTool::ConvertToFloat(samples, info.BitDepth, sampleBufferFloat, info.NumSamples);
|
||||||
|
|
||||||
const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
|
format = GetOpenALBufferFormat(info.NumChannels, 32);
|
||||||
alBufferData(bufferId, format, sampleBufferFloat, bufferSize, info.SampleRate);
|
alBufferData(bufferId, format, sampleBufferFloat, bufferSize, info.SampleRate);
|
||||||
ALC_CHECK_ERROR(alBufferData);
|
ALC_CHECK_ERROR(alBufferData);
|
||||||
|
|
||||||
Allocator::Free(sampleBufferFloat);
|
Allocator::Free(sampleBufferFloat);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(Warning, "OpenAL doesn't support bit depth larger than 16. Your audio data will be truncated.");
|
LOG(Warning, "OpenAL doesn't support bit depth larger than 16. Your audio data will be truncated.");
|
||||||
|
|
||||||
const uint32 bufferSize = info.NumSamples * 2;
|
const uint32 bufferSize = info.NumSamples * 2;
|
||||||
byte* sampleBuffer16 = (byte*)Allocator::Allocate(bufferSize);
|
byte* sampleBuffer16 = (byte*)Allocator::Allocate(bufferSize);
|
||||||
|
|
||||||
AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer16, 16, info.NumSamples);
|
AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer16, 16, info.NumSamples);
|
||||||
|
|
||||||
const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16);
|
format = GetOpenALBufferFormat(info.NumChannels, 16);
|
||||||
alBufferData(bufferId, format, sampleBuffer16, bufferSize, info.SampleRate);
|
alBufferData(bufferId, format, sampleBuffer16, bufferSize, info.SampleRate);
|
||||||
ALC_CHECK_ERROR(alBufferData);
|
ALC_CHECK_ERROR(alBufferData);
|
||||||
|
|
||||||
Allocator::Free(sampleBuffer16);
|
Allocator::Free(sampleBuffer16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -648,19 +634,15 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
|
|||||||
// OpenAL expects unsigned 8-bit data, but engine stores it as signed, so convert
|
// OpenAL expects unsigned 8-bit data, but engine stores it as signed, so convert
|
||||||
const uint32 bufferSize = info.NumSamples * (info.BitDepth / 8);
|
const uint32 bufferSize = info.NumSamples * (info.BitDepth / 8);
|
||||||
byte* sampleBuffer = (byte*)Allocator::Allocate(bufferSize);
|
byte* sampleBuffer = (byte*)Allocator::Allocate(bufferSize);
|
||||||
|
|
||||||
for (uint32 i = 0; i < info.NumSamples; i++)
|
for (uint32 i = 0; i < info.NumSamples; i++)
|
||||||
sampleBuffer[i] = ((int8*)samples)[i] + 128;
|
sampleBuffer[i] = ((int8*)samples)[i] + 128;
|
||||||
|
|
||||||
const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16);
|
|
||||||
alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate);
|
alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate);
|
||||||
ALC_CHECK_ERROR(alBufferData);
|
ALC_CHECK_ERROR(alBufferData);
|
||||||
|
|
||||||
Allocator::Free(sampleBuffer);
|
Allocator::Free(sampleBuffer);
|
||||||
}
|
}
|
||||||
else
|
else if (format)
|
||||||
{
|
{
|
||||||
const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
|
|
||||||
alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
|
alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
|
||||||
ALC_CHECK_ERROR(alBufferData);
|
ALC_CHECK_ERROR(alBufferData);
|
||||||
}
|
}
|
||||||
@@ -675,10 +657,9 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
|
|||||||
{
|
{
|
||||||
const uint32 bufferSize = info.NumChannels * sizeof(int32);
|
const uint32 bufferSize = info.NumChannels * sizeof(int32);
|
||||||
byte* sampleBuffer32 = (byte*)Allocator::Allocate(bufferSize);
|
byte* sampleBuffer32 = (byte*)Allocator::Allocate(bufferSize);
|
||||||
|
|
||||||
AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer32, 32, info.NumSamples);
|
AudioTool::ConvertBitDepth(samples, info.BitDepth, sampleBuffer32, 32, info.NumSamples);
|
||||||
|
|
||||||
const ALenum format = GetOpenALBufferFormat(info.NumChannels, 32);
|
format = GetOpenALBufferFormat(info.NumChannels, 32);
|
||||||
alBufferData(bufferId, format, sampleBuffer32, bufferSize, info.SampleRate);
|
alBufferData(bufferId, format, sampleBuffer32, bufferSize, info.SampleRate);
|
||||||
ALC_CHECK_ERROR(alBufferData);
|
ALC_CHECK_ERROR(alBufferData);
|
||||||
|
|
||||||
@@ -693,19 +674,23 @@ void AudioBackendOAL::Buffer_Write(uint32 bufferId, byte* samples, const AudioDa
|
|||||||
for (uint32 i = 0; i < info.NumSamples; i++)
|
for (uint32 i = 0; i < info.NumSamples; i++)
|
||||||
sampleBuffer[i] = ((int8*)samples)[i] + 128;
|
sampleBuffer[i] = ((int8*)samples)[i] + 128;
|
||||||
|
|
||||||
const ALenum format = GetOpenALBufferFormat(info.NumChannels, 16);
|
format = GetOpenALBufferFormat(info.NumChannels, 16);
|
||||||
alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate);
|
alBufferData(bufferId, format, sampleBuffer, bufferSize, info.SampleRate);
|
||||||
ALC_CHECK_ERROR(alBufferData);
|
ALC_CHECK_ERROR(alBufferData);
|
||||||
|
|
||||||
Allocator::Free(sampleBuffer);
|
Allocator::Free(sampleBuffer);
|
||||||
}
|
}
|
||||||
else
|
else if (format)
|
||||||
{
|
{
|
||||||
const ALenum format = GetOpenALBufferFormat(info.NumChannels, info.BitDepth);
|
|
||||||
alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
|
alBufferData(bufferId, format, samples, info.NumSamples * (info.BitDepth / 8), info.SampleRate);
|
||||||
ALC_CHECK_ERROR(alBufferData);
|
ALC_CHECK_ERROR(alBufferData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!format)
|
||||||
|
{
|
||||||
|
LOG(Error, "Not suppported audio data format for OpenAL device: BitDepth={}, NumChannels={}", info.BitDepth, info.NumChannels);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Char* AudioBackendOAL::Base_Name()
|
const Char* AudioBackendOAL::Base_Name()
|
||||||
|
|||||||
@@ -15,15 +15,8 @@
|
|||||||
#include "Engine/Engine/Globals.h"
|
#include "Engine/Engine/Globals.h"
|
||||||
#include "FlaxEngine.Gen.h"
|
#include "FlaxEngine.Gen.h"
|
||||||
|
|
||||||
AssetsCache::AssetsCache()
|
|
||||||
: _isDirty(false)
|
|
||||||
, _registry(4096)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssetsCache::Init()
|
void AssetsCache::Init()
|
||||||
{
|
{
|
||||||
// Cache data
|
|
||||||
Entry e;
|
Entry e;
|
||||||
int32 count;
|
int32 count;
|
||||||
const DateTime loadStartTime = DateTime::Now();
|
const DateTime loadStartTime = DateTime::Now();
|
||||||
@@ -32,13 +25,11 @@ void AssetsCache::Init()
|
|||||||
#else
|
#else
|
||||||
_path = Globals::ProjectContentFolder / TEXT("AssetsCache.dat");
|
_path = Globals::ProjectContentFolder / TEXT("AssetsCache.dat");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG(Info, "Loading Asset Cache {0}...", _path);
|
LOG(Info, "Loading Asset Cache {0}...", _path);
|
||||||
|
|
||||||
// Check if assets registry exists
|
// Check if assets registry exists
|
||||||
if (!FileSystem::FileExists(_path))
|
if (!FileSystem::FileExists(_path))
|
||||||
{
|
{
|
||||||
// Back
|
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
LOG(Warning, "Cannot find assets cache file");
|
LOG(Warning, "Cannot find assets cache file");
|
||||||
return;
|
return;
|
||||||
@@ -49,7 +40,6 @@ void AssetsCache::Init()
|
|||||||
DeleteMe<FileReadStream> deleteStream(stream);
|
DeleteMe<FileReadStream> deleteStream(stream);
|
||||||
|
|
||||||
// Load version
|
// Load version
|
||||||
// Note: every Engine build is using different assets cache
|
|
||||||
int32 version;
|
int32 version;
|
||||||
stream->ReadInt32(&version);
|
stream->ReadInt32(&version);
|
||||||
if (version != FLAXENGINE_VERSION_BUILD)
|
if (version != FLAXENGINE_VERSION_BUILD)
|
||||||
@@ -58,24 +48,28 @@ void AssetsCache::Init()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load Engine workspace path
|
// Load paths
|
||||||
String workspacePath;
|
String enginePath, projectPath;
|
||||||
stream->ReadString(&workspacePath, -410);
|
stream->ReadString(&enginePath, -410);
|
||||||
|
stream->ReadString(&projectPath, -410);
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
AssetsCacheFlags flags;
|
AssetsCacheFlags flags;
|
||||||
stream->ReadInt32((int32*)&flags);
|
stream->ReadInt32((int32*)&flags);
|
||||||
|
|
||||||
// Check if other engine instance used this cache (cache depends on engine build and install location)
|
// Check if other workspace instance used this cache
|
||||||
// Skip it for relative paths mode
|
if (EnumHasNoneFlags(flags, AssetsCacheFlags::RelativePaths) && enginePath != Globals::StartupFolder)
|
||||||
if (!(flags & AssetsCacheFlags::RelativePaths) && workspacePath != Globals::StartupFolder)
|
|
||||||
{
|
{
|
||||||
LOG(Warning, "Assets cache generated by the different engine installation in \'{0}\'", workspacePath);
|
LOG(Warning, "Assets cache generated by the different {1} installation in \'{0}\'", enginePath, TEXT("engine"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (EnumHasNoneFlags(flags, AssetsCacheFlags::RelativePaths) && projectPath != Globals::ProjectFolder)
|
||||||
|
{
|
||||||
|
LOG(Warning, "Assets cache generated by the different {1} installation in \'{0}\'", projectPath, TEXT("project"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopeLock lock(_locker);
|
ScopeLock lock(_locker);
|
||||||
|
|
||||||
_isDirty = false;
|
_isDirty = false;
|
||||||
|
|
||||||
// Load elements count
|
// Load elements count
|
||||||
@@ -103,15 +97,11 @@ void AssetsCache::Init()
|
|||||||
e.Info.Path = Globals::StartupFolder / e.Info.Path;
|
e.Info.Path = Globals::StartupFolder / e.Info.Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate entry
|
// Use only valid entries
|
||||||
if (!IsEntryValid(e))
|
if (IsEntryValid(e))
|
||||||
{
|
_registry.Add(e.Info.ID, e);
|
||||||
// Reject
|
else
|
||||||
rejectedCount++;
|
rejectedCount++;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_registry.Add(e.Info.ID, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Paths mapping
|
// Paths mapping
|
||||||
@@ -148,7 +138,6 @@ void AssetsCache::Init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// End
|
|
||||||
const int32 loadTimeInMs = static_cast<int32>((DateTime::Now() - loadStartTime).GetTotalMilliseconds());
|
const int32 loadTimeInMs = static_cast<int32>((DateTime::Now() - loadStartTime).GetTotalMilliseconds());
|
||||||
LOG(Info, "Asset Cache loaded {0} entries in {1} ms ({2} rejected)", _registry.Count(), loadTimeInMs, rejectedCount);
|
LOG(Info, "Asset Cache loaded {0} entries in {1} ms ({2} rejected)", _registry.Count(), loadTimeInMs, rejectedCount);
|
||||||
}
|
}
|
||||||
@@ -188,8 +177,9 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa
|
|||||||
// Version
|
// Version
|
||||||
stream->WriteInt32(FLAXENGINE_VERSION_BUILD);
|
stream->WriteInt32(FLAXENGINE_VERSION_BUILD);
|
||||||
|
|
||||||
// Engine workspace path
|
// Paths
|
||||||
stream->WriteString(Globals::StartupFolder, -410);
|
stream->WriteString(Globals::StartupFolder, -410);
|
||||||
|
stream->WriteString(Globals::ProjectFolder, -410);
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
stream->WriteInt32((int32)flags);
|
stream->WriteInt32((int32)flags);
|
||||||
@@ -202,7 +192,6 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa
|
|||||||
for (auto i = entries.Begin(); i.IsNotEnd(); ++i)
|
for (auto i = entries.Begin(); i.IsNotEnd(); ++i)
|
||||||
{
|
{
|
||||||
auto& e = i->Value;
|
auto& e = i->Value;
|
||||||
|
|
||||||
stream->Write(e.Info.ID);
|
stream->Write(e.Info.ID);
|
||||||
stream->WriteString(e.Info.TypeName, index - 13);
|
stream->WriteString(e.Info.TypeName, index - 13);
|
||||||
stream->WriteString(e.Info.Path, index);
|
stream->WriteString(e.Info.Path, index);
|
||||||
@@ -211,7 +200,6 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa
|
|||||||
#else
|
#else
|
||||||
stream->WriteInt64(0);
|
stream->WriteInt64(0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +210,6 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa
|
|||||||
{
|
{
|
||||||
stream->Write(i->Value);
|
stream->Write(i->Value);
|
||||||
stream->WriteString(i->Key, index + 73);
|
stream->WriteString(i->Key, index + 73);
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public:
|
|||||||
typedef Dictionary<String, Guid> PathsMapping;
|
typedef Dictionary<String, Guid> PathsMapping;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _isDirty;
|
bool _isDirty = false;
|
||||||
CriticalSection _locker;
|
CriticalSection _locker;
|
||||||
Registry _registry;
|
Registry _registry;
|
||||||
PathsMapping _pathsMapping;
|
PathsMapping _pathsMapping;
|
||||||
@@ -82,15 +82,8 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AssetsCache"/> class.
|
/// Gets amount of registered assets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AssetsCache();
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// <summary>
|
|
||||||
/// Gets amount of registered assets
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Registry size</returns>
|
|
||||||
int32 Size() const
|
int32 Size() const
|
||||||
{
|
{
|
||||||
_locker.Lock();
|
_locker.Lock();
|
||||||
|
|||||||
@@ -125,16 +125,12 @@ void ContentService::LateUpdate()
|
|||||||
if (timeNow - LastUnloadCheckTime < Content::AssetsUpdateInterval)
|
if (timeNow - LastUnloadCheckTime < Content::AssetsUpdateInterval)
|
||||||
return;
|
return;
|
||||||
LastUnloadCheckTime = timeNow;
|
LastUnloadCheckTime = timeNow;
|
||||||
|
AssetsLocker.Lock();
|
||||||
Asset* asset;
|
|
||||||
ScopeLock lock(AssetsLocker);
|
|
||||||
|
|
||||||
// TODO: maybe it would be better to link for asset remove ref event and cache only assets with no references - test it with millions of assets?
|
|
||||||
|
|
||||||
// Verify all assets
|
// Verify all assets
|
||||||
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
|
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
|
||||||
{
|
{
|
||||||
asset = i->Value;
|
Asset* asset = i->Value;
|
||||||
|
|
||||||
// Check if has no references and is not during unloading
|
// Check if has no references and is not during unloading
|
||||||
if (asset->GetReferencesCount() <= 0 && !UnloadQueue.ContainsKey(asset))
|
if (asset->GetReferencesCount() <= 0 && !UnloadQueue.ContainsKey(asset))
|
||||||
@@ -158,7 +154,7 @@ void ContentService::LateUpdate()
|
|||||||
// Unload marked assets
|
// Unload marked assets
|
||||||
for (int32 i = 0; i < ToUnload.Count(); i++)
|
for (int32 i = 0; i < ToUnload.Count(); i++)
|
||||||
{
|
{
|
||||||
asset = ToUnload[i];
|
Asset* asset = ToUnload[i];
|
||||||
|
|
||||||
// Check if has no references
|
// Check if has no references
|
||||||
if (asset->GetReferencesCount() <= 0)
|
if (asset->GetReferencesCount() <= 0)
|
||||||
@@ -170,6 +166,8 @@ void ContentService::LateUpdate()
|
|||||||
UnloadQueue.Remove(asset);
|
UnloadQueue.Remove(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AssetsLocker.Unlock();
|
||||||
|
|
||||||
// Update cache (for longer sessions it will help to reduce cache misses)
|
// Update cache (for longer sessions it will help to reduce cache misses)
|
||||||
Cache.Save();
|
Cache.Save();
|
||||||
}
|
}
|
||||||
@@ -212,7 +210,6 @@ bool FindAssets(const ProjectInfo* project, HashSet<const ProjectInfo*>& project
|
|||||||
{
|
{
|
||||||
if (projects.Contains(project))
|
if (projects.Contains(project))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
projects.Add(project);
|
projects.Add(project);
|
||||||
bool found = findAsset(id, project->ProjectFolderPath / TEXT("Content"), tmpCache, info);
|
bool found = findAsset(id, project->ProjectFolderPath / TEXT("Content"), tmpCache, info);
|
||||||
for (const auto& reference : project->References)
|
for (const auto& reference : project->References)
|
||||||
@@ -220,7 +217,6 @@ bool FindAssets(const ProjectInfo* project, HashSet<const ProjectInfo*>& project
|
|||||||
if (reference.Project)
|
if (reference.Project)
|
||||||
found |= FindAssets(reference.Project, projects, id, tmpCache, info);
|
found |= FindAssets(reference.Project, projects, id, tmpCache, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +228,6 @@ bool Content::GetAssetInfo(const Guid& id, AssetInfo& info)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
#if ENABLE_ASSETS_DISCOVERY
|
#if ENABLE_ASSETS_DISCOVERY
|
||||||
|
|
||||||
// Find asset in registry
|
// Find asset in registry
|
||||||
if (Cache.FindAsset(id, info))
|
if (Cache.FindAsset(id, info))
|
||||||
return true;
|
return true;
|
||||||
@@ -270,19 +265,15 @@ bool Content::GetAssetInfo(const Guid& id, AssetInfo& info)
|
|||||||
|
|
||||||
//LOG(Warning, "Cannot find {0}.", id);
|
//LOG(Warning, "Cannot find {0}.", id);
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Find asset in registry
|
// Find asset in registry
|
||||||
return Cache.FindAsset(id, info);
|
return Cache.FindAsset(id, info);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
|
bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
|
||||||
{
|
{
|
||||||
#if ENABLE_ASSETS_DISCOVERY
|
#if ENABLE_ASSETS_DISCOVERY
|
||||||
|
|
||||||
// Find asset in registry
|
// Find asset in registry
|
||||||
if (Cache.FindAsset(path, info))
|
if (Cache.FindAsset(path, info))
|
||||||
return true;
|
return true;
|
||||||
@@ -326,12 +317,9 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Find asset in registry
|
// Find asset in registry
|
||||||
return Cache.FindAsset(path, info);
|
return Cache.FindAsset(path, info);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,32 +18,6 @@
|
|||||||
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
|
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
|
||||||
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
|
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
|
||||||
#include "Engine/Serialization/JsonWriters.h"
|
#include "Engine/Serialization/JsonWriters.h"
|
||||||
#include "Engine/Scripting/Enums.h"
|
|
||||||
|
|
||||||
String ImportAudio::Options::ToString() const
|
|
||||||
{
|
|
||||||
return String::Format(TEXT("Format:{}, DisableStreaming:{}, Is3D:{}, Quality:{}, BitDepth:{}"), ScriptingEnum::ToString(Format), DisableStreaming, Is3D, Quality, BitDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImportAudio::Options::Serialize(SerializeStream& stream, const void* otherObj)
|
|
||||||
{
|
|
||||||
SERIALIZE_GET_OTHER_OBJ(ImportAudio::Options);
|
|
||||||
|
|
||||||
SERIALIZE(Format);
|
|
||||||
SERIALIZE(DisableStreaming);
|
|
||||||
SERIALIZE(Is3D);
|
|
||||||
SERIALIZE(Quality);
|
|
||||||
SERIALIZE(BitDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImportAudio::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
|
||||||
{
|
|
||||||
DESERIALIZE(Format);
|
|
||||||
DESERIALIZE(DisableStreaming);
|
|
||||||
DESERIALIZE(Is3D);
|
|
||||||
DESERIALIZE(Quality);
|
|
||||||
DESERIALIZE(BitDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options)
|
bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options)
|
||||||
{
|
{
|
||||||
@@ -90,6 +64,10 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vorbis uses fixed 16-bit depth
|
||||||
|
if (options.Format == AudioFormat::Vorbis)
|
||||||
|
options.BitDepth = AudioTool::BitDepth::_16;
|
||||||
|
|
||||||
LOG_STR(Info, options.ToString());
|
LOG_STR(Info, options.ToString());
|
||||||
|
|
||||||
// Open the file
|
// Open the file
|
||||||
@@ -112,16 +90,14 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
|||||||
sampleBuffer.Link(audioData.Get());
|
sampleBuffer.Link(audioData.Get());
|
||||||
|
|
||||||
// Convert bit depth if need to
|
// Convert bit depth if need to
|
||||||
if (options.BitDepth != static_cast<int32>(info.BitDepth))
|
uint32 outputBitDepth = (uint32)options.BitDepth;
|
||||||
|
if (outputBitDepth != info.BitDepth)
|
||||||
{
|
{
|
||||||
const uint32 outBufferSize = info.NumSamples * (options.BitDepth / 8);
|
const uint32 outBufferSize = info.NumSamples * (outputBitDepth / 8);
|
||||||
sampleBuffer.Allocate(outBufferSize);
|
sampleBuffer.Allocate(outBufferSize);
|
||||||
|
AudioTool::ConvertBitDepth(audioData.Get(), info.BitDepth, sampleBuffer.Get(), outputBitDepth, info.NumSamples);
|
||||||
AudioTool::ConvertBitDepth(audioData.Get(), info.BitDepth, sampleBuffer.Get(), options.BitDepth, info.NumSamples);
|
info.BitDepth = outputBitDepth;
|
||||||
|
|
||||||
info.BitDepth = options.BitDepth;
|
|
||||||
bytesPerSample = info.BitDepth / 8;
|
bytesPerSample = info.BitDepth / 8;
|
||||||
|
|
||||||
bufferSize = outBufferSize;
|
bufferSize = outBufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +125,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
|||||||
context.Data.Header.Chunks[chunkIndex]->Data.Copy(dataPtr, dataSize);
|
context.Data.Header.Chunks[chunkIndex]->Data.Copy(dataPtr, dataSize);
|
||||||
|
|
||||||
#define WRITE_DATA(chunkIndex, dataPtr, dataSize) \
|
#define WRITE_DATA(chunkIndex, dataPtr, dataSize) \
|
||||||
samplesPerChunk[chunkIndex] = (dataSize) / (options.BitDepth / 8); \
|
samplesPerChunk[chunkIndex] = (dataSize) / (outputBitDepth / 8); \
|
||||||
switch (options.Format) \
|
switch (options.Format) \
|
||||||
{ \
|
{ \
|
||||||
case AudioFormat::Raw: \
|
case AudioFormat::Raw: \
|
||||||
@@ -183,8 +159,9 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Split audio data into a several chunks (uniform data spread)
|
// Split audio data into a several chunks (uniform data spread)
|
||||||
const int32 MinChunkSize = 1 * 1024 * 1024; // 1 MB
|
const int32 minChunkSize = 1 * 1024 * 1024; // 1 MB
|
||||||
const int32 chunkSize = Math::Max<int32>(MinChunkSize, (int32)Math::AlignUp<uint32>(bufferSize / ASSET_FILE_DATA_CHUNKS, 256));
|
const int32 dataAlignment = info.NumChannels * bytesPerSample; // Ensure to never split samples in-between (eg. 24-bit that uses 3 bytes)
|
||||||
|
const int32 chunkSize = Math::Max<int32>(minChunkSize, (int32)Math::AlignUp<uint32>(bufferSize / ASSET_FILE_DATA_CHUNKS, dataAlignment));
|
||||||
const int32 chunksCount = Math::CeilToInt((float)bufferSize / chunkSize);
|
const int32 chunksCount = Math::CeilToInt((float)bufferSize / chunkSize);
|
||||||
ASSERT(chunksCount > 0 && chunksCount <= ASSET_FILE_DATA_CHUNKS);
|
ASSERT(chunksCount > 0 && chunksCount <= ASSET_FILE_DATA_CHUNKS);
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
#include "Engine/Tools/AudioTool/AudioDecoder.h"
|
|
||||||
#include "Engine/Core/ISerializable.h"
|
|
||||||
#include "Engine/Audio/Config.h"
|
|
||||||
|
|
||||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||||
|
|
||||||
|
#include "Engine/Tools/AudioTool/AudioTool.h"
|
||||||
|
#include "Engine/Tools/AudioTool/AudioDecoder.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enable/disable caching audio import options
|
/// Enable/disable caching audio import options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -20,23 +20,7 @@
|
|||||||
class ImportAudio
|
class ImportAudio
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
typedef AudioTool::Options Options;
|
||||||
/// Importing audio options
|
|
||||||
/// </summary>
|
|
||||||
struct Options : public ISerializable
|
|
||||||
{
|
|
||||||
AudioFormat Format = AudioFormat::Vorbis;
|
|
||||||
bool DisableStreaming = false;
|
|
||||||
bool Is3D = false;
|
|
||||||
int32 BitDepth = 16;
|
|
||||||
float Quality = 0.4f;
|
|
||||||
|
|
||||||
String ToString() const;
|
|
||||||
|
|
||||||
// [ISerializable]
|
|
||||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
|
||||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -136,13 +136,11 @@ public:
|
|||||||
public:
|
public:
|
||||||
bool IsEnd() const
|
bool IsEnd() const
|
||||||
{
|
{
|
||||||
ASSERT(_collection);
|
|
||||||
return Index() == _collection->Count();
|
return Index() == _collection->Count();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNotEnd() const
|
bool IsNotEnd() const
|
||||||
{
|
{
|
||||||
ASSERT(_collection);
|
|
||||||
return Index() != _collection->Count();
|
return Index() != _collection->Count();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,8 +169,6 @@ public:
|
|||||||
public:
|
public:
|
||||||
Iterator& operator++()
|
Iterator& operator++()
|
||||||
{
|
{
|
||||||
ASSERT(_collection);
|
|
||||||
|
|
||||||
// Check if it is not at end
|
// Check if it is not at end
|
||||||
const int32 end = _collection->Count();
|
const int32 end = _collection->Count();
|
||||||
if (Index() != end)
|
if (Index() != end)
|
||||||
@@ -188,38 +184,18 @@ public:
|
|||||||
_index = 0;
|
_index = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator operator++(int)
|
Iterator operator++(int)
|
||||||
{
|
{
|
||||||
ASSERT(_collection);
|
|
||||||
Iterator temp = *this;
|
Iterator temp = *this;
|
||||||
|
++temp;
|
||||||
// Check if it is not at end
|
|
||||||
const int32 end = _collection->Count();
|
|
||||||
if (Index() != end)
|
|
||||||
{
|
|
||||||
// Move forward within chunk
|
|
||||||
_index++;
|
|
||||||
|
|
||||||
// Check if need to change chunk
|
|
||||||
if (_index == ChunkSize && _chunkIndex < _collection->_chunks.Count() - 1)
|
|
||||||
{
|
|
||||||
// Move to next chunk
|
|
||||||
_chunkIndex++;
|
|
||||||
_index = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator& operator--()
|
Iterator& operator--()
|
||||||
{
|
{
|
||||||
ASSERT(_collection);
|
|
||||||
|
|
||||||
// Check if it's not at beginning
|
// Check if it's not at beginning
|
||||||
if (_index != 0 || _chunkIndex != 0)
|
if (_index != 0 || _chunkIndex != 0)
|
||||||
{
|
{
|
||||||
@@ -236,32 +212,13 @@ public:
|
|||||||
_index--;
|
_index--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator operator--(int)
|
Iterator operator--(int)
|
||||||
{
|
{
|
||||||
ASSERT(_collection);
|
|
||||||
Iterator temp = *this;
|
Iterator temp = *this;
|
||||||
|
--temp;
|
||||||
// Check if it's not at beginning
|
|
||||||
if (_index != 0 || _chunkIndex != 0)
|
|
||||||
{
|
|
||||||
// Check if need to change chunk
|
|
||||||
if (_index == 0)
|
|
||||||
{
|
|
||||||
// Move to previous chunk
|
|
||||||
_chunkIndex--;
|
|
||||||
_index = ChunkSize - 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Move backward within chunk
|
|
||||||
_index--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ public:
|
|||||||
|
|
||||||
FORCE_INLINE bool operator==(const Iterator& v) const
|
FORCE_INLINE bool operator==(const Iterator& v) const
|
||||||
{
|
{
|
||||||
return _index == v._index && &_collection == &v._collection;
|
return _index == v._index && _collection == v._collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE bool operator!=(const Iterator& v) const
|
FORCE_INLINE bool operator!=(const Iterator& v) const
|
||||||
|
|||||||
@@ -73,28 +73,22 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta)
|
|||||||
|
|
||||||
PoolLocker.Lock();
|
PoolLocker.Lock();
|
||||||
|
|
||||||
int32 itemsLeft;
|
// Update timeouts and delete objects that timed out
|
||||||
do
|
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
||||||
{
|
{
|
||||||
// Update timeouts and delete objects that timed out
|
auto& bucket = *i;
|
||||||
itemsLeft = Pool.Count();
|
Object* obj = bucket.Key;
|
||||||
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
|
const float ttl = bucket.Value - ((obj->Flags & ObjectFlags::UseGameTimeForDelete) != ObjectFlags::None ? gameDelta : dt);
|
||||||
|
if (ttl <= 0.0f)
|
||||||
{
|
{
|
||||||
Object* obj = i->Key;
|
Pool.Remove(i);
|
||||||
const float ttl = i->Value - ((obj->Flags & ObjectFlags::UseGameTimeForDelete) != ObjectFlags::None ? gameDelta : dt);
|
obj->OnDeleteObject();
|
||||||
if (ttl <= 0.0f)
|
}
|
||||||
{
|
else
|
||||||
Pool.Remove(i);
|
{
|
||||||
obj->OnDeleteObject();
|
bucket.Value = ttl;
|
||||||
itemsLeft--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
i->Value = ttl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (itemsLeft != Pool.Count()); // Continue removing if any new item was added during removing (eg. sub-object delete with 0 timeout)
|
|
||||||
|
|
||||||
PoolLocker.Unlock();
|
PoolLocker.Unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3
|
|||||||
const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero;
|
const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero;
|
||||||
const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero;
|
const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero;
|
||||||
const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero;
|
const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero;
|
||||||
|
const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color
|
||||||
|
|
||||||
const int32 end = startIndex + searchRange;
|
const int32 end = startIndex + searchRange;
|
||||||
|
|
||||||
for (size_t i = 0; i < sparialSortCache.size(); i++)
|
for (size_t i = 0; i < sparialSortCache.size(); i++)
|
||||||
@@ -184,6 +186,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3
|
|||||||
const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero;
|
const Float3 vNormal = mesh.Normals.HasItems() ? mesh.Normals[vertexIndex] : Float3::Zero;
|
||||||
const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero;
|
const Float3 vTangent = mesh.Tangents.HasItems() ? mesh.Tangents[vertexIndex] : Float3::Zero;
|
||||||
const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero;
|
const Float2 vLightmapUV = mesh.LightmapUVs.HasItems() ? mesh.LightmapUVs[vertexIndex] : Float2::Zero;
|
||||||
|
const Color vColor = mesh.Colors.HasItems() ? mesh.Colors[vertexIndex] : Color::Black; // Assuming Color::Black as a default color
|
||||||
|
|
||||||
const int32 end = startIndex + searchRange;
|
const int32 end = startIndex + searchRange;
|
||||||
|
|
||||||
for (int32 v = startIndex; v < end; v++)
|
for (int32 v = startIndex; v < end; v++)
|
||||||
@@ -201,6 +205,8 @@ int32 FindVertex(const MeshData& mesh, int32 vertexIndex, int32 startIndex, int3
|
|||||||
continue;
|
continue;
|
||||||
if (mesh.LightmapUVs.HasItems() && (vLightmapUV - mesh.LightmapUVs[v]).LengthSquared() > uvEpsSqr)
|
if (mesh.LightmapUVs.HasItems() && (vLightmapUV - mesh.LightmapUVs[v]).LengthSquared() > uvEpsSqr)
|
||||||
continue;
|
continue;
|
||||||
|
if (mesh.Colors.HasItems() && vColor != mesh.Colors[v])
|
||||||
|
continue;
|
||||||
// TODO: check more components?
|
// TODO: check more components?
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
|
|||||||
@@ -39,5 +39,19 @@ namespace FlaxEngine
|
|||||||
View = view;
|
View = view;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rendering mask for layers. Used to exclude objects from rendering (via <see cref="View"/> property).
|
||||||
|
/// </summary>
|
||||||
|
public LayersMask ViewLayersMask
|
||||||
|
{
|
||||||
|
get => View.RenderLayersMask;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var view = View;
|
||||||
|
view.RenderLayersMask = value;
|
||||||
|
View = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ void RenderView::SetProjector(float nearPlane, float farPlane, const Float3& pos
|
|||||||
CullingFrustum = Frustum;
|
CullingFrustum = Frustum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
|
void RenderView::CopyFrom(const Camera* camera, const Viewport* viewport)
|
||||||
{
|
{
|
||||||
const Vector3 cameraPos = camera->GetPosition();
|
const Vector3 cameraPos = camera->GetPosition();
|
||||||
LargeWorlds::UpdateOrigin(Origin, cameraPos);
|
LargeWorlds::UpdateOrigin(Origin, cameraPos);
|
||||||
@@ -192,6 +192,8 @@ void RenderView::CopyFrom(Camera* camera, Viewport* viewport)
|
|||||||
Frustum.GetInvMatrix(IVP);
|
Frustum.GetInvMatrix(IVP);
|
||||||
CullingFrustum = Frustum;
|
CullingFrustum = Frustum;
|
||||||
RenderLayersMask = camera->RenderLayersMask;
|
RenderLayersMask = camera->RenderLayersMask;
|
||||||
|
Flags = camera->RenderFlags;
|
||||||
|
Mode = camera->RenderMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const
|
void RenderView::GetWorldMatrix(const Transform& transform, Matrix& world) const
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ namespace FlaxEngine
|
|||||||
NonJitteredProjection = Projection;
|
NonJitteredProjection = Projection;
|
||||||
TemporalAAJitter = Float4.Zero;
|
TemporalAAJitter = Float4.Zero;
|
||||||
RenderLayersMask = camera.RenderLayersMask;
|
RenderLayersMask = camera.RenderLayersMask;
|
||||||
|
Flags = camera.RenderFlags;
|
||||||
|
Mode = camera.RenderMode;
|
||||||
|
|
||||||
UpdateCachedData();
|
UpdateCachedData();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,10 +295,12 @@ public:
|
|||||||
/// <param name="angle">Camera's FOV angle (in degrees)</param>
|
/// <param name="angle">Camera's FOV angle (in degrees)</param>
|
||||||
void SetProjector(float nearPlane, float farPlane, const Float3& position, const Float3& direction, const Float3& up, float angle);
|
void SetProjector(float nearPlane, float farPlane, const Float3& position, const Float3& direction, const Float3& up, float angle);
|
||||||
|
|
||||||
// Copy view data from camera
|
/// <summary>
|
||||||
// @param camera Camera to copy its data
|
/// Copies view data from camera to the view.
|
||||||
// @param camera The custom viewport to use for view/projection matrices override.
|
/// </summary>
|
||||||
void CopyFrom(Camera* camera, Viewport* viewport = nullptr);
|
/// <param name="camera">The camera to copy its data.</param>
|
||||||
|
/// <param name="viewport">The custom viewport to use for view/projection matrices override.</param>
|
||||||
|
void CopyFrom(const Camera* camera, const Viewport* viewport = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FORCE_INLINE DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const
|
FORCE_INLINE DrawPass GetShadowsDrawPassMask(ShadowsCastingMode shadowsMode) const
|
||||||
|
|||||||
@@ -649,22 +649,6 @@ void GPUContextVulkan::UpdateDescriptorSets(ComputePipelineStateVulkan* pipeline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUContextVulkan::BindPipeline()
|
|
||||||
{
|
|
||||||
if (_psDirtyFlag && _currentState && (_rtDepth || _rtCount))
|
|
||||||
{
|
|
||||||
// Clear flag
|
|
||||||
_psDirtyFlag = false;
|
|
||||||
|
|
||||||
// Change state
|
|
||||||
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
|
||||||
const auto pipeline = _currentState->GetState(_renderPass);
|
|
||||||
vkCmdBindPipeline(cmdBuffer->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
||||||
|
|
||||||
RENDER_STAT_PS_STATE_CHANGE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUContextVulkan::OnDrawCall()
|
void GPUContextVulkan::OnDrawCall()
|
||||||
{
|
{
|
||||||
GPUPipelineStateVulkan* pipelineState = _currentState;
|
GPUPipelineStateVulkan* pipelineState = _currentState;
|
||||||
@@ -704,7 +688,15 @@ void GPUContextVulkan::OnDrawCall()
|
|||||||
BeginRenderPass();
|
BeginRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
BindPipeline();
|
// Bind pipeline
|
||||||
|
if (_psDirtyFlag && _currentState && (_rtDepth || _rtCount))
|
||||||
|
{
|
||||||
|
_psDirtyFlag = false;
|
||||||
|
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
|
||||||
|
const auto pipeline = _currentState->GetState(_renderPass);
|
||||||
|
vkCmdBindPipeline(cmdBuffer->GetHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
RENDER_STAT_PS_STATE_CHANGE();
|
||||||
|
}
|
||||||
|
|
||||||
//UpdateDynamicStates();
|
//UpdateDynamicStates();
|
||||||
|
|
||||||
@@ -1115,9 +1107,7 @@ void GPUContextVulkan::Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCo
|
|||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
auto pipelineState = shaderVulkan->GetOrCreateState();
|
auto pipelineState = shaderVulkan->GetOrCreateState();
|
||||||
|
|
||||||
UpdateDescriptorSets(pipelineState);
|
UpdateDescriptorSets(pipelineState);
|
||||||
|
|
||||||
FlushBarriers();
|
FlushBarriers();
|
||||||
|
|
||||||
// Bind pipeline
|
// Bind pipeline
|
||||||
@@ -1152,10 +1142,8 @@ void GPUContextVulkan::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* b
|
|||||||
EndRenderPass();
|
EndRenderPass();
|
||||||
|
|
||||||
auto pipelineState = shaderVulkan->GetOrCreateState();
|
auto pipelineState = shaderVulkan->GetOrCreateState();
|
||||||
|
|
||||||
UpdateDescriptorSets(pipelineState);
|
UpdateDescriptorSets(pipelineState);
|
||||||
AddBufferBarrier(bufferForArgsVulkan, VK_ACCESS_INDIRECT_COMMAND_READ_BIT);
|
AddBufferBarrier(bufferForArgsVulkan, VK_ACCESS_INDIRECT_COMMAND_READ_BIT);
|
||||||
|
|
||||||
FlushBarriers();
|
FlushBarriers();
|
||||||
|
|
||||||
// Bind pipeline
|
// Bind pipeline
|
||||||
|
|||||||
@@ -161,7 +161,6 @@ private:
|
|||||||
void UpdateDescriptorSets(const struct SpirvShaderDescriptorInfo& descriptorInfo, class DescriptorSetWriterVulkan& dsWriter, bool& needsWrite);
|
void UpdateDescriptorSets(const struct SpirvShaderDescriptorInfo& descriptorInfo, class DescriptorSetWriterVulkan& dsWriter, bool& needsWrite);
|
||||||
void UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineState);
|
void UpdateDescriptorSets(GPUPipelineStateVulkan* pipelineState);
|
||||||
void UpdateDescriptorSets(ComputePipelineStateVulkan* pipelineState);
|
void UpdateDescriptorSets(ComputePipelineStateVulkan* pipelineState);
|
||||||
void BindPipeline();
|
|
||||||
void OnDrawCall();
|
void OnDrawCall();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -886,6 +886,14 @@ void Actor::EndPlay()
|
|||||||
OnDisable();
|
OnDisable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto* script : Scripts)
|
||||||
|
{
|
||||||
|
CHECK_EXECUTE_IN_EDITOR
|
||||||
|
{
|
||||||
|
script->OnDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
OnEndPlay();
|
OnEndPlay();
|
||||||
|
|
||||||
// Clear flag
|
// Clear flag
|
||||||
@@ -899,15 +907,6 @@ void Actor::EndPlay()
|
|||||||
e->EndPlay();
|
e->EndPlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire event for scripting
|
|
||||||
for (auto* script : Scripts)
|
|
||||||
{
|
|
||||||
CHECK_EXECUTE_IN_EDITOR
|
|
||||||
{
|
|
||||||
script->OnDestroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inform attached scripts
|
// Inform attached scripts
|
||||||
for (int32 i = 0; i < Scripts.Count(); i++)
|
for (int32 i = 0; i < Scripts.Count(); i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -416,6 +416,8 @@ void Camera::Serialize(SerializeStream& stream, const void* otherObj)
|
|||||||
SERIALIZE_MEMBER(Far, _far);
|
SERIALIZE_MEMBER(Far, _far);
|
||||||
SERIALIZE_MEMBER(OrthoScale, _orthoScale);
|
SERIALIZE_MEMBER(OrthoScale, _orthoScale);
|
||||||
SERIALIZE(RenderLayersMask);
|
SERIALIZE(RenderLayersMask);
|
||||||
|
SERIALIZE(RenderFlags);
|
||||||
|
SERIALIZE(RenderMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||||
@@ -430,6 +432,8 @@ void Camera::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier
|
|||||||
DESERIALIZE_MEMBER(Far, _far);
|
DESERIALIZE_MEMBER(Far, _far);
|
||||||
DESERIALIZE_MEMBER(OrthoScale, _orthoScale);
|
DESERIALIZE_MEMBER(OrthoScale, _orthoScale);
|
||||||
DESERIALIZE(RenderLayersMask);
|
DESERIALIZE(RenderLayersMask);
|
||||||
|
DESERIALIZE(RenderFlags);
|
||||||
|
DESERIALIZE(RenderMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::OnEnable()
|
void Camera::OnEnable()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Engine/Core/Math/Viewport.h"
|
#include "Engine/Core/Math/Viewport.h"
|
||||||
#include "Engine/Core/Math/Ray.h"
|
#include "Engine/Core/Math/Ray.h"
|
||||||
#include "Engine/Core/Types/LayersMask.h"
|
#include "Engine/Core/Types/LayersMask.h"
|
||||||
|
#include "Engine/Graphics/Enums.h"
|
||||||
#include "Engine/Scripting/ScriptingObjectReference.h"
|
#include "Engine/Scripting/ScriptingObjectReference.h"
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
#include "Engine/Content/AssetReference.h"
|
#include "Engine/Content/AssetReference.h"
|
||||||
@@ -134,6 +135,18 @@ public:
|
|||||||
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")")
|
API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Camera\")")
|
||||||
LayersMask RenderLayersMask;
|
LayersMask RenderLayersMask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Frame rendering flags used to switch between graphics features for this camera.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes = "EditorOrder(110), EditorDisplay(\"Camera\")")
|
||||||
|
ViewFlags RenderFlags = ViewFlags::DefaultGame;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes frame rendering modes for this camera.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes = "EditorOrder(120), EditorDisplay(\"Camera\")")
|
||||||
|
ViewMode RenderMode = ViewMode::Default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Projects the point from 3D world-space to game window coordinates (in screen pixels for default viewport calculated from <see cref="Viewport"/>).
|
/// Projects the point from 3D world-space to game window coordinates (in screen pixels for default viewport calculated from <see cref="Viewport"/>).
|
||||||
|
|||||||
@@ -30,18 +30,18 @@ public:
|
|||||||
API_FIELD(Attributes="EditorOrder(3), DefaultValue(0.0f), EditorDisplay(\"Light\"), Limit(0, 1000, 0.01f)")
|
API_FIELD(Attributes="EditorOrder(3), DefaultValue(0.0f), EditorDisplay(\"Light\"), Limit(0, 1000, 0.01f)")
|
||||||
float SourceLength = 0.0f;
|
float SourceLength = 0.0f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled.
|
|
||||||
/// </summary>
|
|
||||||
API_FIELD(Attributes="EditorOrder(13), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)")
|
|
||||||
float FallOffExponent = 8.0f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to use physically based inverse squared distance falloff, where Radius is only clamping the light's contribution.
|
/// Whether to use physically based inverse squared distance falloff, where Radius is only clamping the light's contribution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_FIELD(Attributes="EditorOrder(14), DefaultValue(false), EditorDisplay(\"Light\")")
|
API_FIELD(Attributes = "EditorOrder(13), DefaultValue(false), EditorDisplay(\"Light\")")
|
||||||
bool UseInverseSquaredFalloff = false;
|
bool UseInverseSquaredFalloff = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes="EditorOrder(14), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)")
|
||||||
|
float FallOffExponent = 8.0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IES texture (light profiles from real world measured data)
|
/// IES texture (light profiles from real world measured data)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -29,18 +29,18 @@ public:
|
|||||||
API_FIELD(Attributes="EditorOrder(2), DefaultValue(0.0f), EditorDisplay(\"Light\"), Limit(0, 1000, 0.01f)")
|
API_FIELD(Attributes="EditorOrder(2), DefaultValue(0.0f), EditorDisplay(\"Light\"), Limit(0, 1000, 0.01f)")
|
||||||
float SourceRadius = 0.0f;
|
float SourceRadius = 0.0f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled.
|
|
||||||
/// </summary>
|
|
||||||
API_FIELD(Attributes="EditorOrder(13), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)")
|
|
||||||
float FallOffExponent = 8.0f;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to use physically based inverse squared distance falloff, where Radius is only clamping the light's contribution.
|
/// Whether to use physically based inverse squared distance falloff, where Radius is only clamping the light's contribution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_FIELD(Attributes="EditorOrder(14), DefaultValue(false), EditorDisplay(\"Light\")")
|
API_FIELD(Attributes = "EditorOrder(13), DefaultValue(false), EditorDisplay(\"Light\")")
|
||||||
bool UseInverseSquaredFalloff = false;
|
bool UseInverseSquaredFalloff = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes="EditorOrder(14), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)")
|
||||||
|
float FallOffExponent = 8.0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IES texture (light profiles from real world measured data)
|
/// IES texture (light profiles from real world measured data)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -332,6 +332,8 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
|
|||||||
// Serialize to json data
|
// Serialize to json data
|
||||||
ASSERT(!IsCreatingPrefab);
|
ASSERT(!IsCreatingPrefab);
|
||||||
IsCreatingPrefab = true;
|
IsCreatingPrefab = true;
|
||||||
|
const Guid targetPrefabId = targetActor->GetPrefabID();
|
||||||
|
const bool hasTargetPrefabId = targetPrefabId.IsValid();
|
||||||
rapidjson_flax::StringBuffer actorsDataBuffer;
|
rapidjson_flax::StringBuffer actorsDataBuffer;
|
||||||
{
|
{
|
||||||
CompactJsonWriter writerObj(actorsDataBuffer);
|
CompactJsonWriter writerObj(actorsDataBuffer);
|
||||||
@@ -340,7 +342,27 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
|
|||||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||||
{
|
{
|
||||||
SceneObject* obj = sceneObjects->At(i);
|
SceneObject* obj = sceneObjects->At(i);
|
||||||
|
|
||||||
|
// Detect when creating prefab from object that is already part of prefab then serialize it as unlinked
|
||||||
|
const Guid prefabId = obj->GetPrefabID();
|
||||||
|
const Guid prefabObjectId = obj->GetPrefabObjectID();
|
||||||
|
bool isObjectFromPrefab = targetPrefabId == prefabId && prefabId.IsValid(); // Allow to use other nested prefabs properly (ignore only root object's prefab link)
|
||||||
|
if (isObjectFromPrefab)
|
||||||
|
{
|
||||||
|
//obj->BreakPrefabLink();
|
||||||
|
obj->_prefabID = Guid::Empty;
|
||||||
|
obj->_prefabObjectID = Guid::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
writer.SceneObject(obj);
|
writer.SceneObject(obj);
|
||||||
|
|
||||||
|
// Restore broken link
|
||||||
|
if (hasTargetPrefabId)
|
||||||
|
{
|
||||||
|
//obj->LinkPrefab(prefabId, prefabObjectId);
|
||||||
|
obj->_prefabID = prefabId;
|
||||||
|
obj->_prefabObjectID = prefabObjectId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
writer.EndArray();
|
writer.EndArray();
|
||||||
}
|
}
|
||||||
@@ -406,7 +428,6 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
|
|||||||
{
|
{
|
||||||
SceneObject* obj = sceneObjects->At(i);
|
SceneObject* obj = sceneObjects->At(i);
|
||||||
Guid prefabObjectId;
|
Guid prefabObjectId;
|
||||||
|
|
||||||
if (objectInstanceIdToPrefabObjectId.TryGet(obj->GetSceneObjectId(), prefabObjectId))
|
if (objectInstanceIdToPrefabObjectId.TryGet(obj->GetSceneObjectId(), prefabObjectId))
|
||||||
{
|
{
|
||||||
obj->LinkPrefab(assetInfo.ID, prefabObjectId);
|
obj->LinkPrefab(assetInfo.ID, prefabObjectId);
|
||||||
|
|||||||
@@ -945,7 +945,7 @@ const char* ButtonCodeToKeyName(KeyboardKeys code)
|
|||||||
// Row #6
|
// Row #6
|
||||||
case KeyboardKeys::Control: return "LCTL"; // Left Control
|
case KeyboardKeys::Control: return "LCTL"; // Left Control
|
||||||
case KeyboardKeys::LeftWindows: return "LWIN";
|
case KeyboardKeys::LeftWindows: return "LWIN";
|
||||||
case KeyboardKeys::LeftMenu: return "LALT";
|
case KeyboardKeys::Alt: return "LALT";
|
||||||
case KeyboardKeys::Spacebar: return "SPCE";
|
case KeyboardKeys::Spacebar: return "SPCE";
|
||||||
case KeyboardKeys::RightMenu: return "RALT";
|
case KeyboardKeys::RightMenu: return "RALT";
|
||||||
case KeyboardKeys::RightWindows: return "RWIN";
|
case KeyboardKeys::RightWindows: return "RWIN";
|
||||||
@@ -2700,6 +2700,8 @@ Float2 LinuxPlatform::GetDesktopSize()
|
|||||||
if (screenIdx >= count)
|
if (screenIdx >= count)
|
||||||
return Float2::Zero;
|
return Float2::Zero;
|
||||||
|
|
||||||
|
// this function is used as a fallback to place a window at the center of
|
||||||
|
// a screen so we report only one screen instead of the real desktop
|
||||||
Float2 size((float)xsi[screenIdx].width, (float)xsi[screenIdx].height);
|
Float2 size((float)xsi[screenIdx].width, (float)xsi[screenIdx].height);
|
||||||
X11::XFree(xsi);
|
X11::XFree(xsi);
|
||||||
return size;
|
return size;
|
||||||
@@ -2707,14 +2709,72 @@ Float2 LinuxPlatform::GetDesktopSize()
|
|||||||
|
|
||||||
Rectangle LinuxPlatform::GetMonitorBounds(const Float2& screenPos)
|
Rectangle LinuxPlatform::GetMonitorBounds(const Float2& screenPos)
|
||||||
{
|
{
|
||||||
// TODO: do it in a proper way
|
if (!xDisplay)
|
||||||
return Rectangle(Float2::Zero, GetDesktopSize());
|
return Rectangle::Empty;
|
||||||
|
|
||||||
|
int event, err;
|
||||||
|
const bool ok = X11::XineramaQueryExtension(xDisplay, &event, &err);
|
||||||
|
if (!ok)
|
||||||
|
return Rectangle::Empty;
|
||||||
|
|
||||||
|
int count;
|
||||||
|
int screenIdx = 0;
|
||||||
|
X11::XineramaScreenInfo* xsi = X11::XineramaQueryScreens(xDisplay, &count);
|
||||||
|
if (screenIdx >= count)
|
||||||
|
return Rectangle::Empty;
|
||||||
|
// find the screen for this screenPos
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (screenPos.X >= xsi[i].x_org && screenPos.X < xsi[i].x_org+xsi[i].width
|
||||||
|
&& screenPos.Y >= xsi[i].y_org && screenPos.Y < xsi[i].y_org+xsi[i].height)
|
||||||
|
{
|
||||||
|
screenIdx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Float2 org((float)xsi[screenIdx].x_org, (float)xsi[screenIdx].y_org);
|
||||||
|
Float2 size((float)xsi[screenIdx].width, (float)xsi[screenIdx].height);
|
||||||
|
X11::XFree(xsi);
|
||||||
|
return Rectangle(org, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle LinuxPlatform::GetVirtualDesktopBounds()
|
Rectangle LinuxPlatform::GetVirtualDesktopBounds()
|
||||||
{
|
{
|
||||||
// TODO: do it in a proper way
|
if (!xDisplay)
|
||||||
return Rectangle(Float2::Zero, GetDesktopSize());
|
return Rectangle::Empty;
|
||||||
|
|
||||||
|
int event, err;
|
||||||
|
const bool ok = X11::XineramaQueryExtension(xDisplay, &event, &err);
|
||||||
|
if (!ok)
|
||||||
|
return Rectangle::Empty;
|
||||||
|
|
||||||
|
int count;
|
||||||
|
X11::XineramaScreenInfo* xsi = X11::XineramaQueryScreens(xDisplay, &count);
|
||||||
|
if (count <= 0)
|
||||||
|
return Rectangle::Empty;
|
||||||
|
// get all screen dimensions and assume the monitors form a rectangle
|
||||||
|
// as you can arrange monitors to your liking this is not necessarily the case
|
||||||
|
int minX = INT32_MAX, minY = INT32_MAX;
|
||||||
|
int maxX = 0, maxY = 0;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int maxScreenX = xsi[i].x_org + xsi[i].width;
|
||||||
|
int maxScreenY = xsi[i].y_org + xsi[i].height;
|
||||||
|
if (maxScreenX > maxX)
|
||||||
|
maxX = maxScreenX;
|
||||||
|
if (maxScreenY > maxY)
|
||||||
|
maxY = maxScreenY;
|
||||||
|
if (minX > xsi[i].x_org)
|
||||||
|
minX = xsi[i].x_org;
|
||||||
|
if (minY > xsi[i].y_org)
|
||||||
|
minY = xsi[i].y_org;
|
||||||
|
}
|
||||||
|
|
||||||
|
Float2 org(static_cast<float>(minX), static_cast<float>(minY));
|
||||||
|
Float2 size(static_cast<float>(maxX - minX), static_cast<float>(maxY - minY));
|
||||||
|
X11::XFree(xsi);
|
||||||
|
return Rectangle(org, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
String LinuxPlatform::GetMainDirectory()
|
String LinuxPlatform::GetMainDirectory()
|
||||||
|
|||||||
@@ -41,8 +41,10 @@ extern Dictionary<StringAnsi, X11::KeyCode> KeyNameMap;
|
|||||||
extern Array<KeyboardKeys> KeyCodeMap;
|
extern Array<KeyboardKeys> KeyCodeMap;
|
||||||
extern X11::Cursor Cursors[(int32)CursorType::MAX];
|
extern X11::Cursor Cursors[(int32)CursorType::MAX];
|
||||||
|
|
||||||
static const uint32 MouseDoubleClickTime = 500;
|
static constexpr uint32 MouseDoubleClickTime = 500;
|
||||||
|
static constexpr uint32 MaxDoubleClickDistanceSquared = 10;
|
||||||
static X11::Time MouseLastButtonPressTime = 0;
|
static X11::Time MouseLastButtonPressTime = 0;
|
||||||
|
static Float2 OldMouseClickPosition;
|
||||||
|
|
||||||
LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||||
: WindowBase(settings)
|
: WindowBase(settings)
|
||||||
@@ -153,7 +155,11 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
|||||||
hints.max_height = (int)settings.MaximumSize.Y;
|
hints.max_height = (int)settings.MaximumSize.Y;
|
||||||
hints.flags |= USSize;
|
hints.flags |= USSize;
|
||||||
}
|
}
|
||||||
X11::XSetNormalHints(display, window, &hints);
|
// honor the WM placement except for manual (overriding) placements
|
||||||
|
if (settings.StartPosition == WindowStartPosition::Manual)
|
||||||
|
{
|
||||||
|
X11::XSetNormalHints(display, window, &hints);
|
||||||
|
}
|
||||||
|
|
||||||
// Ensures the child window is always on top of the parent window
|
// Ensures the child window is always on top of the parent window
|
||||||
if (settings.Parent)
|
if (settings.Parent)
|
||||||
@@ -595,15 +601,19 @@ void LinuxWindow::OnButtonPress(void* event)
|
|||||||
// Handle double-click
|
// Handle double-click
|
||||||
if (buttonEvent->button == Button1)
|
if (buttonEvent->button == Button1)
|
||||||
{
|
{
|
||||||
if (buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime))
|
if (
|
||||||
|
buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) &&
|
||||||
|
Float2::DistanceSquared(mousePos, OldMouseClickPosition) < MaxDoubleClickDistanceSquared)
|
||||||
{
|
{
|
||||||
Input::Mouse->OnMouseDoubleClick(ClientToScreen(mousePos), mouseButton, this);
|
Input::Mouse->OnMouseDoubleClick(ClientToScreen(mousePos), mouseButton, this);
|
||||||
MouseLastButtonPressTime = 0;
|
MouseLastButtonPressTime = 0;
|
||||||
|
OldMouseClickPosition = mousePos;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MouseLastButtonPressTime = buttonEvent->time;
|
MouseLastButtonPressTime = buttonEvent->time;
|
||||||
|
OldMouseClickPosition = mousePos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ void JsonTools::ChangeIds(Document& doc, const Dictionary<Guid, Guid>& mapping)
|
|||||||
::ChangeIds(doc, doc, mapping);
|
::ChangeIds(doc, doc, mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Float2 JsonTools::GetFloat2(const Value& value)
|
Float2 JsonTools::GetFloat2(const Value& value)
|
||||||
{
|
{
|
||||||
Float2 result;
|
Float2 result;
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ void ShadersCompilation::RegisterForShaderReloads(Asset* asset, const String& in
|
|||||||
{
|
{
|
||||||
// Create a directory watcher to track the included file changes
|
// Create a directory watcher to track the included file changes
|
||||||
const String directory = StringUtils::GetDirectoryName(includedPath);
|
const String directory = StringUtils::GetDirectoryName(includedPath);
|
||||||
if (!ShaderIncludesWatcher.ContainsKey(directory))
|
if (FileSystem::DirectoryExists(directory) && !ShaderIncludesWatcher.ContainsKey(directory))
|
||||||
{
|
{
|
||||||
auto watcher = New<FileSystemWatcher>(directory, false);
|
auto watcher = New<FileSystemWatcher>(directory, false);
|
||||||
watcher->OnEvent.Bind<OnShaderIncludesWatcherEvent>();
|
watcher->OnEvent.Bind<OnShaderIncludesWatcherEvent>();
|
||||||
|
|||||||
@@ -1126,12 +1126,9 @@ bool TerrainPatch::InitializeHeightMap()
|
|||||||
float* TerrainPatch::GetHeightmapData()
|
float* TerrainPatch::GetHeightmapData()
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Terrain.GetHeightmapData");
|
PROFILE_CPU_NAMED("Terrain.GetHeightmapData");
|
||||||
|
|
||||||
if (_cachedHeightMap.HasItems())
|
if (_cachedHeightMap.HasItems())
|
||||||
return _cachedHeightMap.Get();
|
return _cachedHeightMap.Get();
|
||||||
|
|
||||||
CacheHeightData();
|
CacheHeightData();
|
||||||
|
|
||||||
return _cachedHeightMap.Get();
|
return _cachedHeightMap.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1144,12 +1141,9 @@ void TerrainPatch::ClearHeightmapCache()
|
|||||||
byte* TerrainPatch::GetHolesMaskData()
|
byte* TerrainPatch::GetHolesMaskData()
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Terrain.GetHolesMaskData");
|
PROFILE_CPU_NAMED("Terrain.GetHolesMaskData");
|
||||||
|
|
||||||
if (_cachedHolesMask.HasItems())
|
if (_cachedHolesMask.HasItems())
|
||||||
return _cachedHolesMask.Get();
|
return _cachedHolesMask.Get();
|
||||||
|
|
||||||
CacheHeightData();
|
CacheHeightData();
|
||||||
|
|
||||||
return _cachedHolesMask.Get();
|
return _cachedHolesMask.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1161,15 +1155,11 @@ void TerrainPatch::ClearHolesMaskCache()
|
|||||||
|
|
||||||
Color32* TerrainPatch::GetSplatMapData(int32 index)
|
Color32* TerrainPatch::GetSplatMapData(int32 index)
|
||||||
{
|
{
|
||||||
ASSERT(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT);
|
CHECK_RETURN(index >= 0 && index < TERRAIN_MAX_SPLATMAPS_COUNT, nullptr);
|
||||||
|
|
||||||
PROFILE_CPU_NAMED("Terrain.GetSplatMapData");
|
PROFILE_CPU_NAMED("Terrain.GetSplatMapData");
|
||||||
|
|
||||||
if (_cachedSplatMap[index].HasItems())
|
if (_cachedSplatMap[index].HasItems())
|
||||||
return _cachedSplatMap[index].Get();
|
return _cachedSplatMap[index].Get();
|
||||||
|
|
||||||
CacheSplatData();
|
CacheSplatData();
|
||||||
|
|
||||||
return _cachedSplatMap[index].Get();
|
return _cachedSplatMap[index].Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,49 @@
|
|||||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#if COMPILE_WITH_AUDIO_TOOL
|
||||||
|
|
||||||
#include "AudioTool.h"
|
#include "AudioTool.h"
|
||||||
#include "Engine/Core/Core.h"
|
#include "Engine/Core/Core.h"
|
||||||
#include "Engine/Core/Memory/Allocation.h"
|
#include "Engine/Core/Memory/Allocation.h"
|
||||||
|
#if USE_EDITOR
|
||||||
|
#include "Engine/Serialization/Serialization.h"
|
||||||
|
#include "Engine/Scripting/Enums.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define CONVERT_TO_MONO_AVG 1
|
#define CONVERT_TO_MONO_AVG 1
|
||||||
#if !CONVERT_TO_MONO_AVG
|
#if !CONVERT_TO_MONO_AVG
|
||||||
#include "Engine/Core/Math/Math.h"
|
#include "Engine/Core/Math/Math.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
String AudioTool::Options::ToString() const
|
||||||
|
{
|
||||||
|
return String::Format(TEXT("Format:{}, DisableStreaming:{}, Is3D:{}, Quality:{}, BitDepth:{}"), ScriptingEnum::ToString(Format), DisableStreaming, Is3D, Quality, (int32)BitDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTool::Options::Serialize(SerializeStream& stream, const void* otherObj)
|
||||||
|
{
|
||||||
|
SERIALIZE_GET_OTHER_OBJ(AudioTool::Options);
|
||||||
|
|
||||||
|
SERIALIZE(Format);
|
||||||
|
SERIALIZE(DisableStreaming);
|
||||||
|
SERIALIZE(Is3D);
|
||||||
|
SERIALIZE(Quality);
|
||||||
|
SERIALIZE(BitDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||||
|
{
|
||||||
|
DESERIALIZE(Format);
|
||||||
|
DESERIALIZE(DisableStreaming);
|
||||||
|
DESERIALIZE(Is3D);
|
||||||
|
DESERIALIZE(Quality);
|
||||||
|
DESERIALIZE(BitDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void ConvertToMono8(const int8* input, uint8* output, uint32 numSamples, uint32 numChannels)
|
void ConvertToMono8(const int8* input, uint8* output, uint32 numSamples, uint32 numChannels)
|
||||||
{
|
{
|
||||||
for (uint32 i = 0; i < numSamples; i++)
|
for (uint32 i = 0; i < numSamples; i++)
|
||||||
@@ -231,8 +266,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp
|
|||||||
for (uint32 i = 0; i < numSamples; i++)
|
for (uint32 i = 0; i < numSamples; i++)
|
||||||
{
|
{
|
||||||
const int8 sample = *(int8*)input;
|
const int8 sample = *(int8*)input;
|
||||||
output[i] = sample / 127.0f;
|
output[i] = sample * (1.0f / 127.0f);
|
||||||
|
|
||||||
input++;
|
input++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,8 +275,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp
|
|||||||
for (uint32 i = 0; i < numSamples; i++)
|
for (uint32 i = 0; i < numSamples; i++)
|
||||||
{
|
{
|
||||||
const int16 sample = *(int16*)input;
|
const int16 sample = *(int16*)input;
|
||||||
output[i] = sample / 32767.0f;
|
output[i] = sample * (1.0f / 32767.0f);
|
||||||
|
|
||||||
input += 2;
|
input += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,8 +284,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp
|
|||||||
for (uint32 i = 0; i < numSamples; i++)
|
for (uint32 i = 0; i < numSamples; i++)
|
||||||
{
|
{
|
||||||
const int32 sample = Convert24To32Bits(input);
|
const int32 sample = Convert24To32Bits(input);
|
||||||
output[i] = sample / 2147483647.0f;
|
output[i] = sample * (1.0f / 2147483647.0f);
|
||||||
|
|
||||||
input += 3;
|
input += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,8 +293,7 @@ void AudioTool::ConvertToFloat(const byte* input, uint32 inBitDepth, float* outp
|
|||||||
for (uint32 i = 0; i < numSamples; i++)
|
for (uint32 i = 0; i < numSamples; i++)
|
||||||
{
|
{
|
||||||
const int32 sample = *(int32*)input;
|
const int32 sample = *(int32*)input;
|
||||||
output[i] = sample / 2147483647.0f;
|
output[i] = sample * (1.0f / 2147483647.0f);
|
||||||
|
|
||||||
input += 4;
|
input += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,7 +309,8 @@ void AudioTool::ConvertFromFloat(const float* input, int32* output, uint32 numSa
|
|||||||
{
|
{
|
||||||
const float sample = *(float*)input;
|
const float sample = *(float*)input;
|
||||||
output[i] = static_cast<int32>(sample * 2147483647.0f);
|
output[i] = static_cast<int32>(sample * 2147483647.0f);
|
||||||
|
|
||||||
input++;
|
input++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -2,16 +2,86 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if COMPILE_WITH_AUDIO_TOOL
|
||||||
|
|
||||||
#include "Engine/Core/Config.h"
|
#include "Engine/Core/Config.h"
|
||||||
#include "Engine/Core/Types/BaseTypes.h"
|
#include "Engine/Core/Types/BaseTypes.h"
|
||||||
|
#if USE_EDITOR
|
||||||
|
#include "Engine/Core/ISerializable.h"
|
||||||
|
#endif
|
||||||
|
#include "Engine/Audio/Types.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Audio data importing and processing utilities.
|
/// Audio data importing and processing utilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class FLAXENGINE_API AudioTool
|
API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API AudioTool
|
||||||
{
|
{
|
||||||
public:
|
DECLARE_SCRIPTING_TYPE_MINIMAL(AudioTool);
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// <summary>
|
||||||
|
/// Declares the imported audio clip bit depth.
|
||||||
|
/// </summary>
|
||||||
|
API_ENUM(Attributes="HideInEditor") enum class BitDepth : int32
|
||||||
|
{
|
||||||
|
// 8-bits per sample.
|
||||||
|
_8 = 8,
|
||||||
|
// 16-bits per sample.
|
||||||
|
_16 = 16,
|
||||||
|
// 24-bits per sample.
|
||||||
|
_24 = 24,
|
||||||
|
// 32-bits per sample.
|
||||||
|
_32 = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Audio import options.
|
||||||
|
/// </summary>
|
||||||
|
API_STRUCT(Attributes="HideInEditor") struct FLAXENGINE_API Options : public ISerializable
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(Options);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The audio data format to import the audio clip as.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes="EditorOrder(10)")
|
||||||
|
AudioFormat Format = AudioFormat::Vorbis;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Compression Quality\"), Limit(0, 1, 0.01f)")
|
||||||
|
float Quality = 0.4f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes="EditorOrder(30)")
|
||||||
|
bool DisableStreaming = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes="EditorOrder(40), EditorDisplay(null, \"Is 3D\")")
|
||||||
|
bool Is3D = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of a single sample in bits. The clip will be converted to this bit depth on import.
|
||||||
|
/// </summary>
|
||||||
|
API_FIELD(Attributes="EditorOrder(50), VisibleIf(nameof(ShowBtiDepth))")
|
||||||
|
BitDepth BitDepth = BitDepth::_16;
|
||||||
|
|
||||||
|
String ToString() const;
|
||||||
|
|
||||||
|
// [ISerializable]
|
||||||
|
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||||
|
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a set of audio samples using multiple channels into a set of mono samples.
|
/// Converts a set of audio samples using multiple channels into a set of mono samples.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -59,3 +129,5 @@ public:
|
|||||||
return (input[2] << 24) | (input[1] << 16) | (input[0] << 8);
|
return (input[2] << 24) | (input[1] << 16) | (input[0] << 8);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -314,6 +314,28 @@ namespace FlaxEngine.GUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDoubleClick(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (button == MouseButton.Left && _isPressed)
|
||||||
|
{
|
||||||
|
OnPressEnd();
|
||||||
|
OnClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button == MouseButton.Left && !_isPressed)
|
||||||
|
{
|
||||||
|
OnPressBegin();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnTouchDown(Float2 location, int pointerId)
|
public override bool OnTouchDown(Float2 location, int pointerId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -277,6 +277,27 @@ namespace FlaxEngine.GUI
|
|||||||
return base.OnMouseDown(location, button);
|
return base.OnMouseDown(location, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (button == MouseButton.Left && !_isPressed)
|
||||||
|
{
|
||||||
|
OnPressBegin();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button == MouseButton.Left && _isPressed)
|
||||||
|
{
|
||||||
|
OnPressEnd();
|
||||||
|
if (_box.Contains(ref location))
|
||||||
|
{
|
||||||
|
OnClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base.OnMouseDoubleClick(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -666,6 +666,30 @@ namespace FlaxEngine.GUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDoubleClick(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (_touchDown && button == MouseButton.Left)
|
||||||
|
{
|
||||||
|
_touchDown = false;
|
||||||
|
ShowPopup();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button == MouseButton.Left)
|
||||||
|
{
|
||||||
|
_touchDown = true;
|
||||||
|
if (!IsPopupOpened)
|
||||||
|
Focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnTouchDown(Float2 location, int pointerId)
|
public override bool OnTouchDown(Float2 location, int pointerId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -769,13 +769,13 @@ namespace FlaxEngine.GUI
|
|||||||
{
|
{
|
||||||
SetSelection(SelectionLeft);
|
SetSelection(SelectionLeft);
|
||||||
}
|
}
|
||||||
else if (SelectionLeft > 0)
|
else if (SelectionLeft >= 0)
|
||||||
{
|
{
|
||||||
int position;
|
int position;
|
||||||
if (ctrl)
|
if (ctrl)
|
||||||
position = FindPrevWordBegin();
|
position = FindPrevWordBegin();
|
||||||
else
|
else
|
||||||
position = _selectionEnd - 1;
|
position = Mathf.Max(_selectionEnd - 1, 0);
|
||||||
|
|
||||||
if (shift)
|
if (shift)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -501,6 +501,29 @@ namespace FlaxEngine.GUI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDoubleClick(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
_mouseOverHeader = HeaderRectangle.Contains(location);
|
||||||
|
if (button == MouseButton.Left && _mouseOverHeader)
|
||||||
|
{
|
||||||
|
_mouseButtonLeftDown = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button == MouseButton.Left && _mouseButtonLeftDown)
|
||||||
|
{
|
||||||
|
_mouseButtonLeftDown = false;
|
||||||
|
if (_mouseOverHeader)
|
||||||
|
Toggle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnMouseLeave()
|
public override void OnMouseLeave()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ namespace FlaxEngine.GUI
|
|||||||
|
|
||||||
// Scrolling
|
// Scrolling
|
||||||
|
|
||||||
private float _clickChange = 20, _scrollChange = 100;
|
private float _clickChange = 20, _scrollChange = 50;
|
||||||
private float _minimum, _maximum = 100;
|
private float _minimum, _maximum = 100;
|
||||||
private float _value, _targetValue;
|
private float _startValue, _value, _targetValue;
|
||||||
private readonly Orientation _orientation;
|
private readonly Orientation _orientation;
|
||||||
private RootControl.UpdateDelegate _update;
|
private RootControl.UpdateDelegate _update;
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ namespace FlaxEngine.GUI
|
|||||||
// Smoothing
|
// Smoothing
|
||||||
|
|
||||||
private float _thumbOpacity = DefaultMinimumOpacity;
|
private float _thumbOpacity = DefaultMinimumOpacity;
|
||||||
|
private float _scrollAnimationProgress = 0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the orientation.
|
/// Gets the orientation.
|
||||||
@@ -59,14 +60,19 @@ namespace FlaxEngine.GUI
|
|||||||
public float TrackThickness { get; set; } = 2.0f;
|
public float TrackThickness { get; set; } = 2.0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the value smoothing scale (0 to not use it).
|
/// The maximum time it takes to animate from current to target scroll position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float SmoothingScale { get; set; } = 0.6f;
|
public float ScrollAnimationDuration { get; set; } = 0.18f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether use scroll value smoothing.
|
/// Gets a value indicating whether use scroll value smoothing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseSmoothing => !Mathf.IsZero(SmoothingScale);
|
public bool UseSmoothing => EnableSmoothing && !Mathf.IsZero(ScrollAnimationDuration);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables scroll smoothing
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableSmoothing { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the minimum value.
|
/// Gets or sets the minimum value.
|
||||||
@@ -112,11 +118,15 @@ namespace FlaxEngine.GUI
|
|||||||
if (!Mathf.NearEqual(value, _targetValue))
|
if (!Mathf.NearEqual(value, _targetValue))
|
||||||
{
|
{
|
||||||
_targetValue = value;
|
_targetValue = value;
|
||||||
|
_startValue = _value;
|
||||||
|
_scrollAnimationProgress = 0f;
|
||||||
|
|
||||||
// Check if skip smoothing
|
// Check if skip smoothing
|
||||||
if (!UseSmoothing)
|
if (!UseSmoothing)
|
||||||
{
|
{
|
||||||
_value = value;
|
_value = value;
|
||||||
|
_startValue = value;
|
||||||
|
_scrollAnimationProgress = 1f;
|
||||||
OnValueChanged();
|
OnValueChanged();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -208,7 +218,8 @@ namespace FlaxEngine.GUI
|
|||||||
{
|
{
|
||||||
if (!Mathf.NearEqual(_value, _targetValue))
|
if (!Mathf.NearEqual(_value, _targetValue))
|
||||||
{
|
{
|
||||||
_value = _targetValue;
|
_value = _targetValue = _startValue;
|
||||||
|
_scrollAnimationProgress = 0f;
|
||||||
SetUpdate(ref _update, null);
|
SetUpdate(ref _update, null);
|
||||||
OnValueChanged();
|
OnValueChanged();
|
||||||
}
|
}
|
||||||
@@ -274,7 +285,8 @@ namespace FlaxEngine.GUI
|
|||||||
|
|
||||||
internal void Reset()
|
internal void Reset()
|
||||||
{
|
{
|
||||||
_value = _targetValue = 0;
|
_value = _targetValue = _startValue = 0;
|
||||||
|
_scrollAnimationProgress = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -296,22 +308,39 @@ namespace FlaxEngine.GUI
|
|||||||
_thumbOpacity = isDeltaSlow ? targetOpacity : Mathf.Lerp(_thumbOpacity, targetOpacity, deltaTime * 10.0f);
|
_thumbOpacity = isDeltaSlow ? targetOpacity : Mathf.Lerp(_thumbOpacity, targetOpacity, deltaTime * 10.0f);
|
||||||
bool needUpdate = Mathf.Abs(_thumbOpacity - targetOpacity) > 0.001f;
|
bool needUpdate = Mathf.Abs(_thumbOpacity - targetOpacity) > 0.001f;
|
||||||
|
|
||||||
// Ensure scroll bar is visible
|
// Ensure scroll bar is visible and smoothing is required
|
||||||
if (Visible)
|
if (Visible && Mathf.Abs(_targetValue - _value) > 0.01f)
|
||||||
{
|
{
|
||||||
// Value smoothing
|
// Interpolate or not if running slow
|
||||||
if (Mathf.Abs(_targetValue - _value) > 0.01f)
|
float value;
|
||||||
|
if (!isDeltaSlow && UseSmoothing)
|
||||||
{
|
{
|
||||||
// Interpolate or not if running slow
|
// percentage of scroll from 0 to _scrollChange, ex. 0.5 at _scrollChange / 2
|
||||||
float value;
|
var minScrollChangeRatio = Mathf.Clamp(Mathf.Abs(_targetValue - _startValue) / _scrollChange, 0, 1);
|
||||||
if (!isDeltaSlow && UseSmoothing)
|
|
||||||
value = Mathf.Lerp(_value, _targetValue, deltaTime * 20.0f * SmoothingScale);
|
// shorten the duration if we scrolled less than _scrollChange
|
||||||
else
|
var actualDuration = ScrollAnimationDuration * minScrollChangeRatio;
|
||||||
value = _targetValue;
|
var step = deltaTime / actualDuration;
|
||||||
_value = value;
|
|
||||||
OnValueChanged();
|
var progress = _scrollAnimationProgress;
|
||||||
needUpdate = true;
|
progress = Mathf.Clamp(progress + step, 0, 1);
|
||||||
|
|
||||||
|
// https://easings.net/#easeOutSine
|
||||||
|
var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2);
|
||||||
|
value = Mathf.Lerp(_startValue, _targetValue, easedProgress);
|
||||||
|
|
||||||
|
_scrollAnimationProgress = progress;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = _targetValue;
|
||||||
|
_startValue = _targetValue;
|
||||||
|
_scrollAnimationProgress = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
_value = value;
|
||||||
|
OnValueChanged();
|
||||||
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// End updating if all animations are done
|
// End updating if all animations are done
|
||||||
@@ -371,7 +400,7 @@ namespace FlaxEngine.GUI
|
|||||||
float mousePosition = _orientation == Orientation.Vertical ? slidePosition.Y : slidePosition.X;
|
float mousePosition = _orientation == Orientation.Vertical ? slidePosition.Y : slidePosition.X;
|
||||||
|
|
||||||
float percentage = (mousePosition - _mouseOffset - _thumbSize / 2) / (TrackSize - _thumbSize);
|
float percentage = (mousePosition - _mouseOffset - _thumbSize / 2) / (TrackSize - _thumbSize);
|
||||||
Value = _minimum + percentage * (_maximum - _minimum);
|
TargetValue = _minimum + percentage * (_maximum - _minimum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +410,7 @@ namespace FlaxEngine.GUI
|
|||||||
if (ThumbEnabled)
|
if (ThumbEnabled)
|
||||||
{
|
{
|
||||||
// Scroll
|
// Scroll
|
||||||
Value = _value - delta * _scrollChange;
|
Value = _targetValue - delta * _scrollChange;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
float ComputeLightProfileMultiplier(Texture2D tex, float3 worldPosition, float3 lightPosition, float3 lightDirection)
|
float ComputeLightProfileMultiplier(Texture2D tex, float3 worldPosition, float3 lightPosition, float3 lightDirection)
|
||||||
{
|
{
|
||||||
float3 l = normalize(worldPosition - lightPosition);
|
float3 l = normalize(worldPosition - lightPosition);
|
||||||
float d = dot(lightPosition, lightDirection);
|
float d = dot(l, lightDirection);
|
||||||
float angle = asin(d) / PI + 0.5f;
|
float angle = asin(d) / PI + 0.5f;
|
||||||
return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r;
|
return tex.SampleLevel(SamplerLinearClamp, float2(angle, 0), 0).r;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ namespace Flax.Build
|
|||||||
|
|
||||||
// Run the compilation
|
// Run the compilation
|
||||||
using var memoryStream = new MemoryStream();
|
using var memoryStream = new MemoryStream();
|
||||||
CSharpParseOptions parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9).WithPreprocessorSymbols(PreprocessorSymbols);
|
CSharpParseOptions parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest).WithPreprocessorSymbols(PreprocessorSymbols);
|
||||||
var syntaxTrees = new List<SyntaxTree>();
|
var syntaxTrees = new List<SyntaxTree>();
|
||||||
foreach (var sourceFile in SourceFiles)
|
foreach (var sourceFile in SourceFiles)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -641,6 +641,44 @@ namespace Flax.Build.Projects.VisualStudio
|
|||||||
File.WriteAllText(e.Key, profile.ToString(), Encoding.UTF8);
|
File.WriteAllText(e.Key, profile.ToString(), Encoding.UTF8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate Rider-specific configuration files
|
||||||
|
{
|
||||||
|
StringBuilder dotSettingsFileContent = new StringBuilder();
|
||||||
|
string dotSettingsUserFilePath = solution.Path + ".DotSettings.user";
|
||||||
|
|
||||||
|
// Solution settings (user layer)
|
||||||
|
bool useResharperBuild = false; // This needs to be disabled for custom build steps to run properly
|
||||||
|
|
||||||
|
if (File.Exists(dotSettingsUserFilePath))
|
||||||
|
{
|
||||||
|
foreach (var line in File.ReadAllLines(dotSettingsUserFilePath))
|
||||||
|
{
|
||||||
|
if (line.Contains(@"/UseMsbuildSolutionBuilder/@EntryValue"))
|
||||||
|
{
|
||||||
|
if (!useResharperBuild)
|
||||||
|
{
|
||||||
|
dotSettingsFileContent.Append("\t").Append(@"<s:String x:Key=""/Default/Environment/Hierarchy/Build/SolBuilderDuo/UseMsbuildSolutionBuilder/@EntryValue"">No</s:String>");
|
||||||
|
if (line.Contains("</wpf:ResourceDictionary>"))
|
||||||
|
dotSettingsFileContent.Append("</wpf:ResourceDictionary>\n");
|
||||||
|
else
|
||||||
|
dotSettingsFileContent.Append("\n");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dotSettingsFileContent.Append(line).Append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dotSettingsFileContent.Append(@"<wpf:ResourceDictionary xml:space=""preserve"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:s=""clr-namespace:System;assembly=mscorlib"" xmlns:ss=""urn:shemas-jetbrains-com:settings-storage-xaml"" xmlns:wpf=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">").Append("\n");
|
||||||
|
if (!useResharperBuild)
|
||||||
|
dotSettingsFileContent.Append("\t").Append(@"<s:String x:Key=""/Default/Environment/Hierarchy/Build/SolBuilderDuo/UseMsbuildSolutionBuilder/@EntryValue"">No</s:String>");
|
||||||
|
dotSettingsFileContent.Append("</wpf:ResourceDictionary>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Utilities.WriteFileIfChanged(dotSettingsUserFilePath, dotSettingsFileContent.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
Reference in New Issue
Block a user