diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 000000000..2369cf79e --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,100 @@ +name: Continuous Deployment +on: + schedule: + - cron: '15 4 * * *' + workflow_dispatch: + +jobs: + + # Windows + package-windows-editor: + name: Editor (Windows) + runs-on: "windows-latest" + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Build + run: | + .\PackageEditor.bat -arch=x64 -platform=Windows -deployOutput=Output + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: Windows-Editor + path: Output/Editor.zip + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: Windows-EditorDebugSymbols + path: Output/EditorDebugSymbols.zip + package-windows-game: + name: Game (Windows) + runs-on: "windows-latest" + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Build + run: | + .\PackagePlatforms.bat -arch=x64 -platform=Windows -deployOutput=Output + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: Windows-Game + path: Output/Windows.zip + + # Linux + package-linux-editor: + name: Editor (Linux) + runs-on: "ubuntu-20.04" + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Install dependencies + run: | + sudo rm -f /etc/apt/sources.list.d/* + sudo cp -f .github/workflows/build_linux_sources.list /etc/apt/sources.list + sudo apt-get update + sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + - name: Build + run: | + ./PackageEditor.sh -arch=x64 -platform=Linux -deployOutput=Output + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: Linux-Editor + path: Output/FlaxEditorLinux.zip + package-linux-game: + name: Game (Linux) + runs-on: "ubuntu-20.04" + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Install dependencies + run: | + sudo rm -f /etc/apt/sources.list.d/* + sudo cp -f .github/workflows/build_linux_sources.list /etc/apt/sources.list + sudo apt-get update + sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + - name: Build + run: | + ./PackagePlatforms.sh -arch=x64 -platform=Linux -deployOutput=Output + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: Linux-Game + path: Output/Linux.zip diff --git a/Source/Editor/Content/Items/AssetItem.cs b/Source/Editor/Content/Items/AssetItem.cs index afb68d42a..f534e93eb 100644 --- a/Source/Editor/Content/Items/AssetItem.cs +++ b/Source/Editor/Content/Items/AssetItem.cs @@ -108,6 +108,26 @@ namespace FlaxEditor.Content return false; } + /// + /// Called when user dags this item into editor viewport or scene tree node. + /// + /// The editor context (eg. editor viewport or scene tree node). + /// True if item can be dropped in, otherwise false. + public virtual bool OnEditorDrag(object context) + { + return false; + } + + /// + /// Called when user drops the item into editor viewport or scene tree node. + /// + /// The editor context (eg. editor viewport or scene tree node). + /// The spawned object. + public virtual Actor OnEditorDrop(object context) + { + throw new NotSupportedException($"Asset {GetType()} doesn't support dropping into viewport."); + } + /// protected override bool DrawShadow => true; diff --git a/Source/Editor/Content/Items/BinaryAssetItem.cs b/Source/Editor/Content/Items/BinaryAssetItem.cs index 417bd23e4..2503e9a38 100644 --- a/Source/Editor/Content/Items/BinaryAssetItem.cs +++ b/Source/Editor/Content/Items/BinaryAssetItem.cs @@ -103,14 +103,26 @@ namespace FlaxEditor.Content /// Implementation of for assets. /// /// - public class ModelAssetItem : BinaryAssetItem + public class ModelItem : BinaryAssetItem { /// - public ModelAssetItem(string path, ref Guid id, string typeName, Type type) + public ModelItem(string path, ref Guid id, string typeName, Type type) : base(path, ref id, typeName, type, ContentItemSearchFilter.Model) { } + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new StaticModel { Model = FlaxEngine.Content.LoadAsync(ID) }; + } + /// protected override void OnBuildTooltipText(StringBuilder sb) { @@ -142,14 +154,26 @@ namespace FlaxEditor.Content /// Implementation of for assets. /// /// - public class SkinnedModelAssetItem : BinaryAssetItem + public class SkinnedModeItem : BinaryAssetItem { /// - public SkinnedModelAssetItem(string path, ref Guid id, string typeName, Type type) + public SkinnedModeItem(string path, ref Guid id, string typeName, Type type) : base(path, ref id, typeName, type, ContentItemSearchFilter.Model) { } + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new AnimatedModel { SkinnedModel = FlaxEngine.Content.LoadAsync(ID) }; + } + /// protected override void OnBuildTooltipText(StringBuilder sb) { diff --git a/Source/Editor/Content/Items/PrefabItem.cs b/Source/Editor/Content/Items/PrefabItem.cs index aee264d55..e758595ae 100644 --- a/Source/Editor/Content/Items/PrefabItem.cs +++ b/Source/Editor/Content/Items/PrefabItem.cs @@ -21,6 +21,18 @@ namespace FlaxEditor.Content { } + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return PrefabManager.SpawnPrefab(FlaxEngine.Content.LoadAsync(ID), null); + } + /// public override ContentItemType ItemType => ContentItemType.Asset; diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs index 04311644b..c1ccf5d73 100644 --- a/Source/Editor/Content/Items/VisualScriptItem.cs +++ b/Source/Editor/Content/Items/VisualScriptItem.cs @@ -538,6 +538,18 @@ namespace FlaxEditor.Content Editor.Instance.CodeEditing.ClearTypes(); } + /// + public override bool OnEditorDrag(object context) + { + return new ScriptType(typeof(Actor)).IsAssignableFrom(ScriptType) && ScriptType.CanCreateInstance; + } + + /// + public override Actor OnEditorDrop(object context) + { + return (Actor)ScriptType.CreateInstance(); + } + /// public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.VisualScript128; diff --git a/Source/Editor/Content/Proxy/AudioClipProxy.cs b/Source/Editor/Content/Proxy/AudioClipProxy.cs index ba60d6ef9..ceff22dd1 100644 --- a/Source/Editor/Content/Proxy/AudioClipProxy.cs +++ b/Source/Editor/Content/Proxy/AudioClipProxy.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Text; using FlaxEditor.Content.Thumbnails; using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows; @@ -11,11 +12,51 @@ using FlaxEngine.GUI; namespace FlaxEditor.Content { + /// + /// Implementation of for assets. + /// + /// + class AudioClipItem : BinaryAssetItem + { + /// + public AudioClipItem(string path, ref Guid id, string typeName, Type type) + : base(path, ref id, typeName, type, ContentItemSearchFilter.Audio) + { + } + + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new AudioSource { Clip = FlaxEngine.Content.LoadAsync(ID) }; + } + + /// + protected override void OnBuildTooltipText(StringBuilder sb) + { + base.OnBuildTooltipText(sb); + + var asset = FlaxEngine.Content.Load(ID, 100); + if (asset) + { + var info = asset.Info; + sb.Append("Duration: ").Append(asset.Length).AppendLine(); + sb.Append("Channels: ").Append(info.NumChannels).AppendLine(); + sb.Append("Bit Depth: ").Append(info.BitDepth).AppendLine(); + } + } + } + /// /// A asset proxy object. /// /// - public class AudioClipProxy : BinaryAssetProxy + class AudioClipProxy : BinaryAssetProxy { private List _previews; @@ -34,6 +75,12 @@ namespace FlaxEditor.Content return new AudioClipWindow(editor, (AssetItem)item); } + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return new AudioClipItem(path, ref id, typeName, AssetType); + } + /// public override Color AccentColor => Color.FromRGB(0xB3452B); diff --git a/Source/Editor/Content/Proxy/BinaryAssetProxy.cs b/Source/Editor/Content/Proxy/BinaryAssetProxy.cs index 4d27f325b..d19f8fd63 100644 --- a/Source/Editor/Content/Proxy/BinaryAssetProxy.cs +++ b/Source/Editor/Content/Proxy/BinaryAssetProxy.cs @@ -47,9 +47,9 @@ namespace FlaxEditor.Content if (typeof(TextureBase).IsAssignableFrom(type)) return new TextureAssetItem(path, ref id, typeName, type); if (typeof(Model).IsAssignableFrom(type)) - return new ModelAssetItem(path, ref id, typeName, type); + return new ModelItem(path, ref id, typeName, type); if (typeof(SkinnedModel).IsAssignableFrom(type)) - return new SkinnedModelAssetItem(path, ref id, typeName, type); + return new SkinnedModeItem(path, ref id, typeName, type); ContentItemSearchFilter searchFilter; if (typeof(MaterialBase).IsAssignableFrom(type)) @@ -58,11 +58,9 @@ namespace FlaxEditor.Content searchFilter = ContentItemSearchFilter.Prefab; else if (typeof(SceneAsset).IsAssignableFrom(type)) searchFilter = ContentItemSearchFilter.Scene; - else if (typeof(AudioClip).IsAssignableFrom(type)) - searchFilter = ContentItemSearchFilter.Audio; else if (typeof(Animation).IsAssignableFrom(type)) searchFilter = ContentItemSearchFilter.Animation; - else if (typeof(ParticleEmitter).IsAssignableFrom(type) || typeof(ParticleSystem).IsAssignableFrom(type)) + else if (typeof(ParticleEmitter).IsAssignableFrom(type)) searchFilter = ContentItemSearchFilter.Particles; else searchFilter = ContentItemSearchFilter.Other; diff --git a/Source/Editor/Content/Proxy/CollisionDataProxy.cs b/Source/Editor/Content/Proxy/CollisionDataProxy.cs index c23730375..e7d437adc 100644 --- a/Source/Editor/Content/Proxy/CollisionDataProxy.cs +++ b/Source/Editor/Content/Proxy/CollisionDataProxy.cs @@ -8,11 +8,36 @@ using FlaxEngine; namespace FlaxEditor.Content { + /// + /// Implementation of for assets. + /// + /// + class CollisionDataItem : BinaryAssetItem + { + /// + public CollisionDataItem(string path, ref Guid id, string typeName, Type type) + : base(path, ref id, typeName, type, ContentItemSearchFilter.Other) + { + } + + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new MeshCollider { CollisionData = FlaxEngine.Content.LoadAsync(ID) }; + } + } + /// /// A asset proxy object. /// /// - public class CollisionDataProxy : BinaryAssetProxy + class CollisionDataProxy : BinaryAssetProxy { /// public override string Name => "Collision Data"; @@ -23,6 +48,12 @@ namespace FlaxEditor.Content return new CollisionDataWindow(editor, item as AssetItem); } + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return new CollisionDataItem(path, ref id, typeName, AssetType); + } + /// public override Color AccentColor => Color.FromRGB(0x2c3e50); diff --git a/Source/Editor/Content/Proxy/ModelProxy.cs b/Source/Editor/Content/Proxy/ModelProxy.cs index a28a022a8..2baf717e1 100644 --- a/Source/Editor/Content/Proxy/ModelProxy.cs +++ b/Source/Editor/Content/Proxy/ModelProxy.cs @@ -47,7 +47,7 @@ namespace FlaxEditor.Content menu.AddButton("Create collision data", () => { - var model = FlaxEngine.Content.LoadAsync(((ModelAssetItem)item).ID); + var model = FlaxEngine.Content.LoadAsync(((ModelItem)item).ID); var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy(); collisionDataProxy.CreateCollisionDataFromModel(model); }); diff --git a/Source/Editor/Content/Proxy/ParticleSystemProxy.cs b/Source/Editor/Content/Proxy/ParticleSystemProxy.cs index f3b2eb67a..7e206fba9 100644 --- a/Source/Editor/Content/Proxy/ParticleSystemProxy.cs +++ b/Source/Editor/Content/Proxy/ParticleSystemProxy.cs @@ -10,6 +10,31 @@ using FlaxEngine.GUI; namespace FlaxEditor.Content { + /// + /// Implementation of for assets. + /// + /// + class ParticleSystemItem : BinaryAssetItem + { + /// + public ParticleSystemItem(string path, ref Guid id, string typeName, Type type) + : base(path, ref id, typeName, type, ContentItemSearchFilter.Particles) + { + } + + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new ParticleEffect { ParticleSystem = FlaxEngine.Content.LoadAsync(ID) }; + } + } + /// /// A asset proxy object. /// @@ -28,6 +53,12 @@ namespace FlaxEditor.Content return new ParticleSystemWindow(editor, item as AssetItem); } + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return new ParticleSystemItem(path, ref id, typeName, AssetType); + } + /// public override Color AccentColor => Color.FromRGB(0xFF790200); diff --git a/Source/Editor/Content/Proxy/SceneAnimationProxy.cs b/Source/Editor/Content/Proxy/SceneAnimationProxy.cs index 83d9247d8..66c1bfff2 100644 --- a/Source/Editor/Content/Proxy/SceneAnimationProxy.cs +++ b/Source/Editor/Content/Proxy/SceneAnimationProxy.cs @@ -7,6 +7,31 @@ using FlaxEngine; namespace FlaxEditor.Content { + /// + /// Implementation of for assets. + /// + /// + class SceneAnimationItem : BinaryAssetItem + { + /// + public SceneAnimationItem(string path, ref Guid id, string typeName, Type type) + : base(path, ref id, typeName, type, ContentItemSearchFilter.Other) + { + } + + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new SceneAnimationPlayer { Animation = FlaxEngine.Content.LoadAsync(ID) }; + } + } + /// /// A asset proxy object. /// @@ -22,6 +47,12 @@ namespace FlaxEditor.Content return new SceneAnimationWindow(editor, item as AssetItem); } + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return new SceneAnimationItem(path, ref id, typeName, AssetType); + } + /// public override Color AccentColor => Color.FromRGB(0xff5c4a87); diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index 2284478dd..1b18034c4 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -518,7 +518,7 @@ bool Editor::Init() exit(failed ? 1 : 0); return true; } - + // If during last lightmaps baking engine crashed we could try to restore the progress ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState(); @@ -534,6 +534,12 @@ bool Editor::Init() // Initialize managed editor Managed->Init(); + // Start play if requested by cmd line + if (CommandLine::Options.Play.HasValue()) + { + Managed->RequestStartPlayOnEditMode(); + } + return false; } diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index adf4d566b..e7b33852b 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -46,6 +46,7 @@ namespace FlaxEditor private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode; private string _projectToOpen; private float _lastAutoSaveTimer; + private Guid _startupSceneCmdLine; private const string ProjectDataLastScene = "LastScene"; private const string ProjectDataLastSceneSpawn = "LastSceneSpawn"; @@ -271,10 +272,11 @@ namespace FlaxEditor module.OnEndInit(); } - internal void Init(bool isHeadless, bool skipCompile) + internal void Init(bool isHeadless, bool skipCompile, Guid startupScene) { EnsureState(); _isHeadlessMode = isHeadless; + _startupSceneCmdLine = startupScene; Log("Editor init"); if (isHeadless) Log("Running in headless mode"); @@ -332,6 +334,17 @@ namespace FlaxEditor } // Load scene + + // scene cmd line argument + var scene = ContentDatabase.Find(_startupSceneCmdLine); + if (scene is SceneItem) + { + Editor.Log("Loading scene specified in command line"); + Scene.OpenScene(_startupSceneCmdLine); + return; + } + + // if no scene cmd line argument is provided var startupSceneMode = Options.Options.General.StartupSceneMode; if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene)) { @@ -1309,6 +1322,19 @@ namespace FlaxEditor AnimGraphDebugFlow?.Invoke(debugFlow); } + private static void RequestStartPlayOnEditMode() + { + if (Instance.StateMachine.IsEditMode) + Instance.Simulation.RequestStartPlay(); + if (Instance.StateMachine.IsPlayMode) + Instance.StateMachine.StateChanged -= RequestStartPlayOnEditMode; + } + + internal static void Internal_RequestStartPlayOnEditMode() + { + Instance.StateMachine.StateChanged += RequestStartPlayOnEditMode; + } + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int Internal_ReadOutputLogs(string[] outMessages, byte[] outLogTypes, long[] outLogTimes); diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 35219a906..18a905d4b 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -35,6 +35,7 @@ MMethod* Internal_GetGameWindowSize = nullptr; MMethod* Internal_OnAppExit = nullptr; MMethod* Internal_OnVisualScriptingDebugFlow = nullptr; MMethod* Internal_OnAnimGraphDebugFlow = nullptr; +MMethod* Internal_RequestStartPlayOnEditMode = nullptr; void OnLightmapsBake(ShadowsOfMordor::BuildProgressStep step, float stepProgress, float totalProgress, bool isProgressEvent) { @@ -209,7 +210,7 @@ ManagedEditor::~ManagedEditor() void ManagedEditor::Init() { // Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time - void* args[2]; + void* args[3]; MClass* mclass = GetClass(); if (mclass == nullptr) { @@ -230,6 +231,12 @@ void ManagedEditor::Init() bool skipCompile = CommandLine::Options.SkipCompile.IsTrue(); args[0] = &isHeadless; args[1] = &skipCompile; + Guid sceneId; + if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId))) + { + sceneId = Guid::Empty; + } + args[2] = &sceneId; initMethod->Invoke(instance, args, &exception); if (exception) { @@ -481,6 +488,18 @@ bool ManagedEditor::OnAppExit() return MUtils::Unbox(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr)); } +void ManagedEditor::RequestStartPlayOnEditMode() +{ + if (!HasManagedInstance()) + return; + if (Internal_RequestStartPlayOnEditMode == nullptr) + { + Internal_RequestStartPlayOnEditMode = GetClass()->GetMethod("Internal_RequestStartPlayOnEditMode"); + ASSERT(Internal_RequestStartPlayOnEditMode); + } + Internal_RequestStartPlayOnEditMode->Invoke(GetManagedInstance(), nullptr, nullptr); +} + void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly) { ASSERT(!HasManagedInstance()); diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h index 5e0b612b5..df05e87bd 100644 --- a/Source/Editor/Managed/ManagedEditor.h +++ b/Source/Editor/Managed/ManagedEditor.h @@ -133,6 +133,11 @@ public: /// True if exit engine, otherwise false. bool OnAppExit(); + /// + /// Requests play mode when the editor is in edit mode ( once ). + /// + void RequestStartPlayOnEditMode(); + private: void OnEditorAssemblyLoaded(MAssembly* assembly); diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs index c1f2a3e7b..3faf5cd7e 100644 --- a/Source/Editor/Modules/SimulationModule.cs +++ b/Source/Editor/Modules/SimulationModule.cs @@ -203,6 +203,7 @@ namespace FlaxEditor.Modules else if (!gameWin.IsSelected) { gameWin.SelectTab(false); + gameWin.RootWindow?.Window?.Focus(); FlaxEngine.GUI.RootControl.GameRoot.Focus(); } } diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 5e2792dc4..9dc0b1719 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -50,6 +50,7 @@ namespace FlaxEditor.SceneGraph.GUI public ActorTreeNode() : base(true) { + ChildrenIndent = 16.0f; } internal virtual void LinkNode(ActorNode node) @@ -567,121 +568,12 @@ namespace FlaxEditor.SceneGraph.GUI { for (int i = 0; i < _dragAssets.Objects.Count; i++) { - var assetItem = _dragAssets.Objects[i]; - - if (assetItem.IsOfType()) - { - // Create actor - var model = FlaxEngine.Content.LoadAsync(assetItem.ID); - - var actor = new AnimatedModel - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - SkinnedModel = model, - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var model = FlaxEngine.Content.LoadAsync(assetItem.ID); - - var actor = new StaticModel - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - Model = model, - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var actor = new MeshCollider - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - CollisionData = FlaxEngine.Content.LoadAsync(assetItem.ID), - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var actor = new ParticleEffect - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - ParticleSystem = FlaxEngine.Content.LoadAsync(assetItem.ID), - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var actor = new SceneAnimationPlayer - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - Animation = FlaxEngine.Content.LoadAsync(assetItem.ID), - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var actor = new AudioSource - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - Clip = FlaxEngine.Content.LoadAsync(assetItem.ID), - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - - break; - } - else if (assetItem.IsOfType()) - { - // Create prefab instance - var prefab = FlaxEngine.Content.LoadAsync(assetItem.ID); - var actor = PrefabManager.SpawnPrefab(prefab, null); - actor.StaticFlags = Actor.StaticFlags; - actor.Name = assetItem.ShortName; - actor.Transform = Actor.Transform; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - { - // Create actor - var actor = (Actor)visualScriptItem.ScriptType.CreateInstance(); - actor.StaticFlags = Actor.StaticFlags; - actor.Name = assetItem.ShortName; - actor.Transform = Actor.Transform; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } + var item = _dragAssets.Objects[i]; + var actor = item.OnEditorDrop(this); + actor.StaticFlags = Actor.StaticFlags; + actor.Name = item.ShortName; + actor.Transform = Actor.Transform; + ActorNode.Root.Spawn(actor, Actor); } result = DragDropEffect.Move; @@ -736,46 +628,12 @@ namespace FlaxEditor.SceneGraph.GUI return actorNode.Actor != null && actorNode != ActorNode && actorNode.Find(Actor) == null; } - /// - /// Validates the asset for drag and drop into one of the scene tree nodes. - /// - /// The item. - /// True if can drag and drop it, otherwise false. - public static bool ValidateDragAsset(AssetItem assetItem) + private bool ValidateDragAsset(AssetItem assetItem) { - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - return true; - - return false; + return assetItem.OnEditorDrag(this); } - /// - /// Validates the type of the actor for drag and drop into one of the scene tree nodes. - /// - /// Type of the actor. - /// True if can drag and drop it, otherwise false. - public static bool ValidateDragActorType(ScriptType actorType) + private static bool ValidateDragActorType(ScriptType actorType) { return true; } diff --git a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp index 54ba119f2..ffb4ff7b3 100644 --- a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp +++ b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp @@ -152,34 +152,10 @@ public: namespace VisualStudio { - bool SameFile(HANDLE h1, HANDLE h2) - { - BY_HANDLE_FILE_INFORMATION bhfi1 = { 0 }; - BY_HANDLE_FILE_INFORMATION bhfi2 = { 0 }; - - if (::GetFileInformationByHandle(h1, &bhfi1) && ::GetFileInformationByHandle(h2, &bhfi2)) - { - return ((bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh) && (bhfi1.nFileIndexLow == bhfi2.nFileIndexLow) && (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber)); - } - - return false; - } - bool AreFilePathsEqual(const wchar_t* path1, const wchar_t* path2) - { - if (wcscmp(path1, path2) == 0) - return true; - - HANDLE file1 = CreateFileW(path1, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - HANDLE file2 = CreateFileW(path2, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - - bool result = SameFile(file1, file2); - - CloseHandle(file1); - CloseHandle(file2); - - return result; - } + { + return _wcsicmp(path1, path2) == 0; + } class ConnectionInternal { diff --git a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp index dde2455a7..e8b221a5d 100644 --- a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp @@ -44,6 +44,7 @@ VisualStudioEditor::VisualStudioEditor(VisualStudioVersion version, const String break; } _solutionPath = Globals::ProjectFolder / Editor::Project->Name + TEXT(".sln"); + _solutionPath.Replace('/', '\\'); // Use Windows-style path separators } void VisualStudioEditor::FindEditors(Array* output) @@ -145,7 +146,9 @@ void VisualStudioEditor::OpenFile(const String& path, int32 line) // Open file const VisualStudio::Connection connection(*_CLSID, *_solutionPath); - const auto result = connection.OpenFile(*path, line); + String tmp = path; + tmp.Replace('/', '\\'); // Use Windows-style path separators + const auto result = connection.OpenFile(*tmp, line); if (result.Failed()) { LOG(Warning, "Cannot open file \'{0}\':{1}. {2}.", path, line, String(result.Message.c_str())); diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index 870d68eba..d5a064546 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -354,6 +354,10 @@ namespace FlaxEditor.Scripting if (Type.Type == typeof(float)) sb.Append("Float"); + else if (Type.Type == typeof(short)) + sb.Append("Int16"); + else if (Type.Type == typeof(ushort)) + sb.Append("Uint16"); else if (Type.Type == typeof(int)) sb.Append("Int"); else if (Type.Type == typeof(uint)) @@ -389,6 +393,24 @@ namespace FlaxEditor.Scripting else sb.Append(asUint); } + else if (DefaultValue is short asInt16) + { + if (asInt16 == short.MaxValue) + sb.Append("Int16.MaxValue"); + else if (asInt16 == short.MinValue) + sb.Append("Int16.MinValue"); + else + sb.Append(asInt16); + } + else if (DefaultValue is ushort asUint16) + { + if (asUint16 == ushort.MaxValue) + sb.Append("Uint16.MaxValue"); + else if (asUint16 == ushort.MinValue) + sb.Append("Uint16.MinValue"); + else + sb.Append(asUint16); + } else if (DefaultValue is float asFloat) { if (asFloat == float.MaxValue) @@ -703,6 +725,10 @@ namespace FlaxEditor.Scripting return "Int"; if (_managed == typeof(uint)) return "Uint"; + if (_managed == typeof(short)) + return "Int16"; + if (_managed == typeof(ushort)) + return "Uint16"; if (_managed == typeof(bool)) return "Bool"; return _managed.Name; diff --git a/Source/Editor/Utilities/VariantUtils.cs b/Source/Editor/Utilities/VariantUtils.cs index eb044c9e9..d30cb26af 100644 --- a/Source/Editor/Utilities/VariantUtils.cs +++ b/Source/Editor/Utilities/VariantUtils.cs @@ -55,7 +55,10 @@ namespace FlaxEditor.Utilities Int2, Int3, - Int4 + Int4, + + Int16, + Uint16, } internal static VariantType ToVariantType(this Type type) @@ -67,6 +70,10 @@ namespace FlaxEditor.Utilities variantType = VariantType.Void; else if (type == typeof(bool)) variantType = VariantType.Bool; + else if (type == typeof(short)) + variantType = VariantType.Int16; + else if (type == typeof(ushort)) + variantType = VariantType.Uint16; else if (type == typeof(int)) variantType = VariantType.Int; else if (type == typeof(uint)) @@ -242,6 +249,8 @@ namespace FlaxEditor.Utilities case VariantType.Null: return ScriptType.Null; case VariantType.Void: return new ScriptType(typeof(void)); case VariantType.Bool: return new ScriptType(typeof(bool)); + case VariantType.Int16: return new ScriptType(typeof(short)); + case VariantType.Uint16: return new ScriptType(typeof(ushort)); case VariantType.Int: return new ScriptType(typeof(int)); case VariantType.Uint: return new ScriptType(typeof(uint)); case VariantType.Int64: return new ScriptType(typeof(long)); @@ -309,6 +318,8 @@ namespace FlaxEditor.Utilities case VariantType.Null: return null; case VariantType.Void: return typeof(void); case VariantType.Bool: return typeof(bool); + case VariantType.Int16: return typeof(short); + case VariantType.Uint16: return typeof(ushort); case VariantType.Int: return typeof(int); case VariantType.Uint: return typeof(uint); case VariantType.Int64: return typeof(long); @@ -378,6 +389,8 @@ namespace FlaxEditor.Utilities case VariantType.ManagedObject: case VariantType.Void: return null; case VariantType.Bool: return stream.ReadByte() != 0; + case VariantType.Int16: return stream.ReadInt16(); + case VariantType.Uint16: return stream.ReadUInt16(); case VariantType.Int: return stream.ReadInt32(); case VariantType.Uint: return stream.ReadUInt32(); case VariantType.Int64: return stream.ReadInt64(); @@ -513,6 +526,12 @@ namespace FlaxEditor.Utilities case VariantType.Bool: stream.Write((byte)((bool)value ? 1 : 0)); break; + case VariantType.Int16: + stream.Write((short)value); + break; + case VariantType.Uint16: + stream.Write((ushort)value); + break; case VariantType.Int: stream.Write((int)value); break; @@ -687,6 +706,12 @@ namespace FlaxEditor.Utilities case VariantType.Bool: stream.WriteValue((bool)value); break; + case VariantType.Int16: + stream.WriteValue((short)value); + break; + case VariantType.Uint16: + stream.WriteValue((ushort)value); + break; case VariantType.Int: stream.WriteValue((int)value); break; diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 0c895640d..ea63a598b 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -36,7 +36,7 @@ namespace FlaxEditor.Viewport private readonly ViewportWidgetButton _rotateSnapping; private readonly ViewportWidgetButton _scaleSnapping; - private readonly DragAssets _dragAssets = new DragAssets(ValidateDragItem); + private readonly DragAssets _dragAssets; private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType); private SelectionOutline _customSelectionOutline; @@ -187,6 +187,7 @@ namespace FlaxEditor.Viewport : base(Object.New(), editor.Undo) { _editor = editor; + _dragAssets = new DragAssets(ValidateDragItem); // Prepare rendering task Task.ActorsSource = ActorsSources.Scenes; @@ -800,31 +801,19 @@ namespace FlaxEditor.Viewport return result; } - private static bool ValidateDragItem(ContentItem contentItem) + private bool ValidateDragItem(ContentItem contentItem) { if (!Level.IsAnySceneLoaded) return false; if (contentItem is AssetItem assetItem) { - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) + if (assetItem.OnEditorDrag(this)) return true; if (assetItem.IsOfType()) return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; if (assetItem.IsOfType()) return true; - if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - return true; } return false; @@ -891,119 +880,40 @@ namespace FlaxEditor.Viewport private void Spawn(AssetItem item, SceneGraphNode hit, ref Vector2 location, ref Vector3 hitLocation) { - if (item is AssetItem assetItem) + if (item.IsOfType()) { - if (assetItem.IsOfType()) + if (hit is StaticModelNode staticModelNode) { - var asset = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new ParticleEffect - { - Name = item.ShortName, - ParticleSystem = asset - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var asset = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new SceneAnimationPlayer - { - Name = item.ShortName, - Animation = asset - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - if (hit is StaticModelNode staticModelNode) - { - var staticModel = (StaticModel)staticModelNode.Actor; - var ray = ConvertMouseToRay(ref location); - if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) - { - var material = FlaxEngine.Content.LoadAsync(item.ID); - using (new UndoBlock(Undo, staticModel, "Change material")) - staticModel.SetMaterial(entryIndex, material); - } - } - else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) + var staticModel = (StaticModel)staticModelNode.Actor; + var ray = ConvertMouseToRay(ref location); + if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) { var material = FlaxEngine.Content.LoadAsync(item.ID); - using (new UndoBlock(Undo, brushSurfaceNode.Brush, "Change material")) - { - var surface = brushSurfaceNode.Surface; - surface.Material = material; - brushSurfaceNode.Surface = surface; - } + using (new UndoBlock(Undo, staticModel, "Change material")) + staticModel.SetMaterial(entryIndex, material); } - return; } - if (assetItem.IsOfType()) + else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) { - var model = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new AnimatedModel + var material = FlaxEngine.Content.LoadAsync(item.ID); + using (new UndoBlock(Undo, brushSurfaceNode.Brush, "Change material")) { - Name = item.ShortName, - SkinnedModel = model - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var model = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new StaticModel - { - Name = item.ShortName, - Model = model - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var collisionData = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new MeshCollider - { - Name = item.ShortName, - CollisionData = collisionData - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var clip = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new AudioSource - { - Name = item.ShortName, - Clip = clip - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var prefab = FlaxEngine.Content.LoadAsync(item.ID); - var actor = PrefabManager.SpawnPrefab(prefab, null); - actor.Name = item.ShortName; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - Editor.Instance.Scene.OpenScene(item.ID, true); - return; - } - if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - { - var actor = (Actor)visualScriptItem.ScriptType.CreateInstance(); - actor.Name = item.ShortName; - Spawn(actor, ref hitLocation); - return; + var surface = brushSurfaceNode.Surface; + surface.Material = material; + brushSurfaceNode.Surface = surface; + } } + return; + } + if (item.IsOfType()) + { + Editor.Instance.Scene.OpenScene(item.ID, true); + return; + } + { + var actor = item.OnEditorDrop(this); + actor.Name = item.ShortName; + Spawn(actor, ref hitLocation); } } diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 6483700b6..e189a8e62 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Security.Policy; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -53,7 +54,7 @@ namespace FlaxEditor.Viewport private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private IntPtr _debugDrawContext; private PrefabSpritesRenderer _spritesRenderer; - private readonly DragAssets _dragAssets = new DragAssets(ValidateDragItem); + private readonly DragAssets _dragAssets; private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType); private readonly DragHandlers _dragHandlers = new DragHandlers(); @@ -89,6 +90,7 @@ namespace FlaxEditor.Viewport Undo = window.Undo; ViewportCamera = new FPSCamera(); _debugDrawContext = DebugDraw.AllocateContext(); + _dragAssets = new DragAssets(ValidateDragItem); // Prepare rendering task Task.ActorsSource = ActorsSources.CustomActors; @@ -673,24 +675,14 @@ namespace FlaxEditor.Viewport return _dragHandlers.OnDragEnter(data); } - private static bool ValidateDragItem(ContentItem contentItem) + private bool ValidateDragItem(ContentItem contentItem) { if (contentItem is AssetItem assetItem) { - if (assetItem.IsOfType()) + if (assetItem.OnEditorDrag(this)) return true; if (assetItem.IsOfType()) return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - return true; } return false; @@ -743,17 +735,6 @@ namespace FlaxEditor.Viewport { if (item is BinaryAssetItem binaryAssetItem) { - if (binaryAssetItem.Type == typeof(ParticleSystem)) - { - var particleSystem = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new ParticleEffect - { - Name = item.ShortName, - ParticleSystem = particleSystem - }; - Spawn(actor, ref hitLocation); - return; - } if (typeof(MaterialBase).IsAssignableFrom(binaryAssetItem.Type)) { if (hit is StaticModelNode staticModelNode) @@ -769,65 +750,11 @@ namespace FlaxEditor.Viewport } return; } - if (typeof(SkinnedModel).IsAssignableFrom(binaryAssetItem.Type)) - { - var model = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new AnimatedModel - { - Name = item.ShortName, - SkinnedModel = model - }; - Spawn(actor, ref hitLocation); - return; - } - if (typeof(Model).IsAssignableFrom(binaryAssetItem.Type)) - { - var model = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new StaticModel - { - Name = item.ShortName, - Model = model - }; - Spawn(actor, ref hitLocation); - return; - } - if (binaryAssetItem.IsOfType()) - { - var collisionData = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new MeshCollider - { - Name = item.ShortName, - CollisionData = collisionData - }; - Spawn(actor, ref hitLocation); - return; - } - if (typeof(AudioClip).IsAssignableFrom(binaryAssetItem.Type)) - { - var clip = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new AudioSource - { - Name = item.ShortName, - Clip = clip - }; - Spawn(actor, ref hitLocation); - return; - } - if (typeof(Prefab).IsAssignableFrom(binaryAssetItem.Type)) - { - var prefab = FlaxEngine.Content.LoadAsync(item.ID); - var actor = PrefabManager.SpawnPrefab(prefab, null); - actor.Name = item.ShortName; - Spawn(actor, ref hitLocation); - return; - } } - if (item is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) { - var actor = (Actor)visualScriptItem.ScriptType.CreateInstance(); + var actor = item.OnEditorDrop(this); actor.Name = item.ShortName; Spawn(actor, ref hitLocation); - return; } } diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 97bcfc412..ac150d44d 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -160,6 +160,10 @@ const char* VariantType::GetTypeName() const return "System.Void"; case Bool: return "System.Boolean"; + case Int16: + return "System.Int16"; + case Uint16: + return "System.UInt16"; case Int: return "System.Int32"; case Uint: @@ -225,6 +229,12 @@ const char* VariantType::GetTypeName() const case Bool: result = TEXT("Bool"); break; + case Int16: + result = TEXT("Int16"); + break; + case Uint16: + result = TEXT("Uint16"); + break; case Int: result = TEXT("Int"); break; @@ -417,6 +427,18 @@ Variant::Variant(bool v) AsBool = v; } +Variant::Variant(int16 v) + : Type(VariantType::Int16) +{ + AsInt16 = v; +} + +Variant::Variant(uint16 v) + : Type(VariantType::Uint16) +{ + AsUint16 = v; +} + Variant::Variant(int32 v) : Type(VariantType::Int) { @@ -896,6 +918,10 @@ bool Variant::operator==(const Variant& other) const return true; case VariantType::Bool: return AsBool == other.AsBool; + case VariantType::Int16: + return AsInt16 == other.AsInt16; + case VariantType::Uint16: + return AsUint16 == other.AsUint16; case VariantType::Int: return AsInt == other.AsInt; case VariantType::Uint: @@ -1004,6 +1030,10 @@ bool Variant::operator<(const Variant& other) const return true; case VariantType::Bool: return AsBool < other.AsBool; + case VariantType::Int16: + return AsInt16 < other.AsInt16; + case VariantType::Uint16: + return AsUint16 < other.AsUint16; case VariantType::Int: return AsInt < other.AsInt; case VariantType::Uint: @@ -1040,6 +1070,10 @@ Variant::operator bool() const { case VariantType::Bool: return AsBool; + case VariantType::Int16: + return AsInt16 != 0; + case VariantType::Uint16: + return AsUint16 != 0; case VariantType::Int: return AsInt != 0; case VariantType::Uint: @@ -1075,6 +1109,10 @@ Variant::operator Char() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return (Char)AsInt16; + case VariantType::Uint16: + return (Char)AsUint16; case VariantType::Int: return (Char)AsInt; case VariantType::Uint: @@ -1101,6 +1139,10 @@ Variant::operator int8() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return (int8)AsInt16; + case VariantType::Uint16: + return (int8)AsUint16; case VariantType::Int: return (int8)AsInt; case VariantType::Uint: @@ -1127,6 +1169,10 @@ Variant::operator int16() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return AsInt16; + case VariantType::Uint16: + return (int16)AsUint16; case VariantType::Int: return (int16)AsInt; case VariantType::Uint: @@ -1153,6 +1199,10 @@ Variant::operator int32() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return (int32)AsInt16; + case VariantType::Uint16: + return (int32)AsUint16; case VariantType::Int: return AsInt; case VariantType::Uint: @@ -1179,6 +1229,10 @@ Variant::operator int64() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return (int64)AsInt16; + case VariantType::Uint16: + return (int64)AsUint16; case VariantType::Int: return (int64)AsInt; case VariantType::Uint: @@ -1205,6 +1259,10 @@ Variant::operator uint8() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return (uint8)AsInt16; + case VariantType::Uint16: + return (uint8)AsUint16; case VariantType::Int: return (uint8)AsInt; case VariantType::Uint: @@ -1231,6 +1289,10 @@ Variant::operator uint16() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return (uint16)AsInt16; + case VariantType::Uint16: + return AsUint16; case VariantType::Int: return (uint16)AsInt; case VariantType::Uint: @@ -1257,6 +1319,10 @@ Variant::operator uint32() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return (uint32)AsInt16; + case VariantType::Uint16: + return (uint32)AsUint16; case VariantType::Int: return (uint32)AsInt; case VariantType::Uint: @@ -1283,6 +1349,10 @@ Variant::operator uint64() const { case VariantType::Bool: return AsBool ? 1 : 0; + case VariantType::Int16: + return (uint64)AsInt16; + case VariantType::Uint16: + return (uint64)AsUint16; case VariantType::Int: return (uint64)AsInt; case VariantType::Uint: @@ -1309,6 +1379,10 @@ Variant::operator float() const { case VariantType::Bool: return AsBool ? 1.0f : 0.0f; + case VariantType::Int16: + return (float)AsInt16; + case VariantType::Uint16: + return (float)AsUint16; case VariantType::Int: return (float)AsInt; case VariantType::Uint: @@ -1333,6 +1407,10 @@ Variant::operator double() const { case VariantType::Bool: return AsBool ? 1.0 : 0.0; + case VariantType::Int16: + return (double)AsInt16; + case VariantType::Uint16: + return (double)AsUint16; case VariantType::Int: return (double)AsInt; case VariantType::Uint: @@ -1423,6 +1501,10 @@ Variant::operator Vector2() const { case VariantType::Bool: return Vector2(AsBool ? 1.0f : 0.0f); + case VariantType::Int16: + return Vector2((float)AsInt16); + case VariantType::Uint16: + return Vector2((float)AsUint16); case VariantType::Int: return Vector2((float)AsInt); case VariantType::Uint: @@ -1459,6 +1541,10 @@ Variant::operator Vector3() const { case VariantType::Bool: return Vector3(AsBool ? 1.0f : 0.0f); + case VariantType::Int16: + return Vector3((float)AsInt16); + case VariantType::Uint16: + return Vector3((float)AsUint16); case VariantType::Int: return Vector3((float)AsInt); case VariantType::Uint: @@ -1495,6 +1581,10 @@ Variant::operator Vector4() const { case VariantType::Bool: return Vector4(AsBool ? 1.0f : 0.0f); + case VariantType::Int16: + return Vector4((float)AsInt16); + case VariantType::Uint16: + return Vector4((float)AsUint16); case VariantType::Int: return Vector4((float)AsInt); case VariantType::Uint: @@ -1531,6 +1621,10 @@ Variant::operator Int2() const { case VariantType::Bool: return Int2((int32)(AsBool ? 1.0f : 0.0f)); + case VariantType::Int16: + return Int2((int32)AsInt16); + case VariantType::Uint16: + return Int2((int32)AsUint16); case VariantType::Int: return Int2((int32)AsInt); case VariantType::Uint: @@ -1573,6 +1667,10 @@ Variant::operator Int3() const { case VariantType::Bool: return Int3((int32)(AsBool ? 1 : 0)); + case VariantType::Int16: + return Int3((int32)AsInt16); + case VariantType::Uint16: + return Int3((int32)AsUint16); case VariantType::Int: return Int3((int32)AsInt); case VariantType::Uint: @@ -1615,6 +1713,10 @@ Variant::operator Int4() const { case VariantType::Bool: return Int4((int32)(AsBool ? 1 : 0)); + case VariantType::Int16: + return Int4(AsInt16); + case VariantType::Uint16: + return Int4((int32)AsUint16); case VariantType::Int: return Int4(AsInt); case VariantType::Uint: @@ -1657,6 +1759,10 @@ Variant::operator Color() const { case VariantType::Bool: return Color(AsBool ? 1.0f : 0.0f); + case VariantType::Int16: + return Color((float)AsInt16); + case VariantType::Uint16: + return Color((float)AsUint16); case VariantType::Int: return Color((float)AsInt); case VariantType::Uint: @@ -2139,6 +2245,10 @@ String Variant::ToString() const return TEXT("null"); case VariantType::Bool: return AsBool ? TEXT("true") : TEXT("false"); + case VariantType::Int16: + return StringUtils::ToString(AsInt16); + case VariantType::Uint16: + return StringUtils::ToString(AsUint16); case VariantType::Int: return StringUtils::ToString(AsInt); case VariantType::Uint: @@ -2207,6 +2317,8 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) case VariantType::Bool: switch (to.Type) { + case VariantType::Int16: + case VariantType::Uint16: case VariantType::Int: case VariantType::Uint: case VariantType::Int64: @@ -2221,10 +2333,50 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) default: return false; } + case VariantType::Int16: + switch (to.Type) + { + case VariantType::Bool: + case VariantType::Int: + case VariantType::Int64: + case VariantType::Uint16: + case VariantType::Uint: + case VariantType::Uint64: + case VariantType::Float: + case VariantType::Double: + case VariantType::Vector2: + case VariantType::Vector3: + case VariantType::Vector4: + case VariantType::Color: + return true; + default: + return false; + } case VariantType::Int: switch (to.Type) { case VariantType::Bool: + case VariantType::Int16: + case VariantType::Int64: + case VariantType::Uint16: + case VariantType::Uint: + case VariantType::Uint64: + case VariantType::Float: + case VariantType::Double: + case VariantType::Vector2: + case VariantType::Vector3: + case VariantType::Vector4: + case VariantType::Color: + return true; + default: + return false; + } + case VariantType::Uint16: + switch (to.Type) + { + case VariantType::Bool: + case VariantType::Int16: + case VariantType::Int: case VariantType::Int64: case VariantType::Uint: case VariantType::Uint64: @@ -2242,7 +2394,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Uint16: case VariantType::Uint64: + case VariantType::Int16: case VariantType::Int: case VariantType::Int64: case VariantType::Float: @@ -2259,7 +2413,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Int16: case VariantType::Int: + case VariantType::Uint16: case VariantType::Uint: case VariantType::Uint64: case VariantType::Float: @@ -2276,7 +2432,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Uint16: case VariantType::Uint: + case VariantType::Int16: case VariantType::Int: case VariantType::Int64: case VariantType::Float: @@ -2293,7 +2451,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Int16: case VariantType::Int: + case VariantType::Uint16: case VariantType::Uint: case VariantType::Int64: case VariantType::Uint64: @@ -2310,8 +2470,10 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Int16: case VariantType::Int: case VariantType::Uint: + case VariantType::Uint16: case VariantType::Int64: case VariantType::Uint64: case VariantType::Float: @@ -2327,7 +2489,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Uint16: case VariantType::Uint: + case VariantType::Int16: case VariantType::Int: case VariantType::Int64: case VariantType::Float: @@ -2343,7 +2507,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Uint16: case VariantType::Uint: + case VariantType::Int16: case VariantType::Int: case VariantType::Int64: case VariantType::Float: @@ -2359,7 +2525,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Uint16: case VariantType::Uint: + case VariantType::Int16: case VariantType::Int: case VariantType::Int64: case VariantType::Float: @@ -2375,7 +2543,9 @@ bool Variant::CanCast(const Variant& v, const VariantType& to) switch (to.Type) { case VariantType::Bool: + case VariantType::Uint16: case VariantType::Uint: + case VariantType::Int16: case VariantType::Int: case VariantType::Int64: case VariantType::Float: @@ -2401,6 +2571,10 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) case VariantType::Bool: switch (to.Type) { + case VariantType::Int16: // No portable literal suffix for short ( Available in MSVC but Undocumented : i16 ) + return Variant((int16)(v.AsBool ? 1 : 0)); + case VariantType::Uint16: // No portable literal suffix for short ( Available in MSVC but Undocumented : ui16 ) + return Variant((uint16)(v.AsBool ? 1 : 0)); case VariantType::Int: return Variant(v.AsBool ? 1 : 0); case VariantType::Uint: @@ -2424,17 +2598,51 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) default: ; } break; + case VariantType::Int16: + switch (to.Type) + { + case VariantType::Bool: + return Variant(v.AsInt != 0); + case VariantType::Int: + return Variant((int32)v.AsInt16); + case VariantType::Int64: + return Variant((int64)v.AsInt16); + case VariantType::Uint16: + return Variant((uint16)v.AsInt16); + case VariantType::Uint: + return Variant((uint32)v.AsInt16); + case VariantType::Uint64: + return Variant((uint64)v.AsInt16); + case VariantType::Float: + return Variant((float)v.AsInt16); + case VariantType::Double: + return Variant((double)v.AsInt16); + case VariantType::Vector2: + return Variant(Vector2((float)v.AsInt16)); + case VariantType::Vector3: + return Variant(Vector3((float)v.AsInt16)); + case VariantType::Vector4: + return Variant(Vector4((float)v.AsInt16)); + case VariantType::Color: + return Variant(Color((float)v.AsInt16)); + default: ; + } + break; case VariantType::Int: switch (to.Type) { case VariantType::Bool: return Variant(v.AsInt != 0); + case VariantType::Int16: + return Variant((int16)v.AsInt); case VariantType::Int64: - return Variant((int64)v.AsUint); + return Variant((int64)v.AsInt); + case VariantType::Uint16: + return Variant((uint16)v.AsInt); case VariantType::Uint: return Variant((uint32)v.AsInt); case VariantType::Uint64: - return Variant((uint64)v.AsUint); + return Variant((uint64)v.AsInt); case VariantType::Float: return Variant((float)v.AsInt); case VariantType::Double: @@ -2450,15 +2658,49 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) default: ; } break; + case VariantType::Uint16: + switch (to.Type) + { + case VariantType::Bool: + return Variant(v.AsUint16 != 0); + case VariantType::Int16: + return Variant((int16)v.AsUint16); + case VariantType::Int: + return Variant((int32)v.AsUint16); + case VariantType::Int64: + return Variant((int64)v.AsUint16); + case VariantType::Uint16: + return Variant((uint16)v.AsUint16); + case VariantType::Uint64: + return Variant((uint64)v.AsUint16); + case VariantType::Float: + return Variant((float)v.AsUint16); + case VariantType::Double: + return Variant((double)v.AsUint16); + case VariantType::Vector2: + return Variant(Vector2((float)v.AsUint16)); + case VariantType::Vector3: + return Variant(Vector3((float)v.AsUint16)); + case VariantType::Vector4: + return Variant(Vector4((float)v.AsUint16)); + case VariantType::Color: + return Variant(Color((float)v.AsUint16)); + default: ; + } + break; case VariantType::Uint: switch (to.Type) { case VariantType::Bool: return Variant(v.AsUint != 0); + case VariantType::Int16: + return Variant((int16)v.AsUint); case VariantType::Int: return Variant((int32)v.AsUint); case VariantType::Int64: return Variant((int64)v.AsUint); + case VariantType::Uint16: + return Variant((uint16)v.AsUint); case VariantType::Uint64: return Variant((uint64)v.AsUint); case VariantType::Float: @@ -2481,8 +2723,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) { case VariantType::Bool: return Variant(v.AsInt64 != 0); - case VariantType::Int64: - return Variant((int64)v.AsUint); + case VariantType::Int16: + return Variant((int16)v.AsInt64); + case VariantType::Int: + return Variant((int32)v.AsInt64); + case VariantType::Uint16: + return Variant((uint16)v.AsInt64); case VariantType::Uint: return Variant((uint32)v.AsInt64); case VariantType::Uint64: @@ -2507,10 +2753,14 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) { case VariantType::Bool: return Variant(v.AsUint64 != 0); + case VariantType::Int16: + return Variant((int16)v.AsUint64); case VariantType::Int: return Variant((int32)v.AsUint64); case VariantType::Int64: return Variant((int64)v.AsUint64); + case VariantType::Uint16: + return Variant((uint16)v.AsUint16); case VariantType::Uint: return Variant((uint32)v.AsUint); case VariantType::Float: @@ -2533,8 +2783,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) { case VariantType::Bool: return Variant(Math::Abs(v.AsFloat) > ZeroTolerance); + case VariantType::Int16: + return Variant((int16)v.AsFloat); case VariantType::Int: return Variant((int32)v.AsFloat); + case VariantType::Uint16: + return Variant((uint16)v.AsFloat); case VariantType::Uint: return Variant((uint32)v.AsFloat); case VariantType::Int64: @@ -2559,8 +2813,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) { case VariantType::Bool: return Variant(Math::Abs(v.AsDouble) > ZeroTolerance); + case VariantType::Int16: + return Variant((int16)v.AsDouble); case VariantType::Int: return Variant((int32)v.AsDouble); + case VariantType::Uint16: + return Variant((uint16)v.AsDouble); case VariantType::Uint: return Variant((uint32)v.AsDouble); case VariantType::Int64: @@ -2585,8 +2843,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) { case VariantType::Bool: return Variant(Math::Abs(((Vector2*)v.AsData)->X) > ZeroTolerance); + case VariantType::Int16: + return Variant((int16)((Vector2*)v.AsData)->X); case VariantType::Int: return Variant((int32)((Vector2*)v.AsData)->X); + case VariantType::Uint16: + return Variant((uint16)((Vector2*)v.AsData)->X); case VariantType::Uint: return Variant((uint32)((Vector2*)v.AsData)->X); case VariantType::Int64: @@ -2611,8 +2873,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) { case VariantType::Bool: return Variant(Math::Abs(((Vector3*)v.AsData)->X) > ZeroTolerance); + case VariantType::Int16: + return Variant((int16)((Vector3*)v.AsData)->X); case VariantType::Int: return Variant((int32)((Vector3*)v.AsData)->X); + case VariantType::Uint16: + return Variant((uint16)((Vector3*)v.AsData)->X); case VariantType::Uint: return Variant((uint32)((Vector3*)v.AsData)->X); case VariantType::Int64: @@ -2637,8 +2903,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) { case VariantType::Bool: return Variant(Math::Abs(((Vector4*)v.AsData)->X) > ZeroTolerance); + case VariantType::Int16: + return Variant((int16)((Vector4*)v.AsData)->X); case VariantType::Int: return Variant((int32)((Vector4*)v.AsData)->X); + case VariantType::Uint16: + return Variant((uint16)((Vector4*)v.AsData)->X); case VariantType::Uint: return Variant((uint32)((Vector4*)v.AsData)->X); case VariantType::Int64: @@ -2663,8 +2933,12 @@ Variant Variant::Cast(const Variant& v, const VariantType& to) { case VariantType::Bool: return Variant(Math::Abs(((Color*)v.AsData)->R) > ZeroTolerance); + case VariantType::Int16: + return Variant((int16)((Color*)v.AsData)->R); case VariantType::Int: return Variant((int32)((Color*)v.AsData)->R); + case VariantType::Uint16: + return Variant((uint16)((Color*)v.AsData)->R); case VariantType::Uint: return Variant((uint32)((Color*)v.AsData)->R); case VariantType::Int64: @@ -2696,6 +2970,8 @@ bool Variant::NearEqual(const Variant& a, const Variant& b, float epsilon) return false; switch (a.Type.Type) { + case VariantType::Int16: + return Math::Abs(a.AsInt16 - b.AsInt16) <= (int32)epsilon; case VariantType::Int: return Math::Abs(a.AsInt - b.AsInt) <= (int32)epsilon; case VariantType::Int64: @@ -2737,8 +3013,12 @@ Variant Variant::Lerp(const Variant& a, const Variant& b, float alpha) { case VariantType::Bool: return alpha < 0.5f ? a : b; + case VariantType::Int16: + return Math::Lerp(a.AsInt16, b.AsInt16, alpha); case VariantType::Int: return Math::Lerp(a.AsInt, b.AsInt, alpha); + case VariantType::Uint16: + return Math::Lerp(a.AsUint16, b.AsUint16, alpha); case VariantType::Uint: return Math::Lerp(a.AsUint, b.AsUint, alpha); case VariantType::Int64: @@ -2844,8 +3124,12 @@ uint32 GetHash(const Variant& key) { case VariantType::Bool: return GetHash(key.AsBool); + case VariantType::Int16: + return GetHash(key.AsInt16); case VariantType::Int: return GetHash(key.AsInt); + case VariantType::Uint16: + return GetHash(key.AsUint16); case VariantType::Uint: return GetHash(key.AsUint); case VariantType::Int64: diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h index ac9af0e79..6ab7a7a67 100644 --- a/Source/Engine/Core/Types/Variant.h +++ b/Source/Engine/Core/Types/Variant.h @@ -58,6 +58,9 @@ API_STRUCT(InBuild) struct FLAXENGINE_API VariantType Int3, Int4, + Int16, + Uint16, + MAX }; @@ -135,6 +138,8 @@ API_STRUCT(InBuild) struct FLAXENGINE_API Variant union { bool AsBool; + int16 AsInt16; + uint16 AsUint16; int32 AsInt; uint32 AsUint; int64 AsInt64; @@ -184,6 +189,8 @@ public: Variant(Variant&& other) noexcept; Variant(bool v); + Variant(int16 v); + Variant(uint16 v); Variant(int32 v); Variant(uint32 v); Variant(int64 v); diff --git a/Source/Engine/Engine/CommandLine.cpp b/Source/Engine/Engine/CommandLine.cpp index d6c03b070..9f5078c64 100644 --- a/Source/Engine/Engine/CommandLine.cpp +++ b/Source/Engine/Engine/CommandLine.cpp @@ -102,6 +102,23 @@ bool CommandLine::Parse(const Char* cmdLine) *(end - len) = 0; \ end -= len; \ } + +#define PARSE_ARG_OPT_SWITCH(text, field) \ + pos = (Char*)StringUtils::FindIgnoreCase(buffer.Get(), TEXT(text)); \ + if (pos) \ + { \ + len = ARRAY_COUNT(text) - 1; \ + if (ParseArg(pos + len, argStart, argEnd)) \ + Options.field = String::Empty; \ + else \ + { \ + Options.field = String(argStart, static_cast(argEnd - argStart)); \ + len = static_cast((argEnd - pos) + 1); \ + Platform::MemoryCopy(pos, pos + len, (end - pos - len) * 2); \ + *(end - len) = 0; \ + end -= len; \ + } \ + } PARSE_BOOL_SWITCH("-windowed ", Windowed); PARSE_BOOL_SWITCH("-fullscreen ", Fullscreen); @@ -137,6 +154,7 @@ bool CommandLine::Parse(const Char* cmdLine) PARSE_ARG_SWITCH("-build ", Build); PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile); PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug); + PARSE_ARG_OPT_SWITCH("-play ", Play); #endif diff --git a/Source/Engine/Engine/CommandLine.h b/Source/Engine/Engine/CommandLine.h index 62b38ca99..d9d3547d3 100644 --- a/Source/Engine/Engine/CommandLine.h +++ b/Source/Engine/Engine/CommandLine.h @@ -164,6 +164,11 @@ public: /// Nullable ShaderDebug; + /// + /// -play !guid! ( Scene to play, can be empty to use default ) + /// + Nullable Play; + #endif }; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index dddbd6a3e..35b5fe5fb 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -363,7 +363,8 @@ void Mesh::GetDrawCallGeometry(DrawCall& drawCall) const void Mesh::Render(GPUContext* context) const { - ASSERT(IsInitialized()); + if (!IsInitialized()) + return; context->BindVB(ToSpan((GPUBuffer**)_vertexBuffers, 3)); context->BindIB(_indexBuffer); @@ -372,7 +373,7 @@ void Mesh::Render(GPUContext* context) const void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom) const { - if (!material || !material->IsSurface()) + if (!material || !material->IsSurface() || !IsInitialized()) return; // Submit draw call diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp index ae25e01e0..4d8e02a97 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp @@ -3,6 +3,12 @@ #include "ParticleEmitterGraph.CPU.h" #include "Engine/Core/Random.h" +// ReSharper disable CppCStyleCast +// ReSharper disable CppClangTidyClangDiagnosticCastAlign +// ReSharper disable CppDefaultCaseNotHandledInSwitchStatement +// ReSharper disable CppClangTidyCppcoreguidelinesMacroUsage +// ReSharper disable CppClangTidyClangDiagnosticOldStyleCast + #define RAND Random::Rand() #define RAND2 Vector2(RAND, RAND) #define RAND3 Vector3(RAND, RAND, RAND) @@ -62,7 +68,7 @@ namespace float scale = 1.0f; for (int32 i = 0; i < octaves; i++) { - const float curWeight = Math::Pow(1.0f - ((float)i / octaves), Math::Lerp(2.0f, 0.2f, roughness)); + const float curWeight = Math::Pow(1.0f - ((float)i / (float)octaves), Math::Lerp(2.0f, 0.2f, roughness)); noise += Noise3D(position * scale) * curWeight; weight += curWeight; @@ -156,7 +162,7 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) // Calculate actual spawn amount spawnCount = Math::Max(spawnCount, 0.0f); const int32 result = Math::FloorToInt(spawnCount); - spawnCount -= result; + spawnCount -= (float)result; data.SpawnCounter = spawnCount; return result; @@ -475,7 +481,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* auto& velocity = _data->Buffer->Layout->Attributes[node->Attributes[0]]; auto& mass = _data->Buffer->Layout->Attributes[node->Attributes[1]]; - byte* spriteSizePtr = useSpriteSize ? start + _data->Buffer->Layout->Attributes[node->Attributes[2]].Offset : 0; + byte* spriteSizePtr = useSpriteSize ? start + _data->Buffer->Layout->Attributes[node->Attributes[2]].Offset : nullptr; byte* velocityPtr = start + velocity.Offset; byte* massPtr = start + mass.Offset; diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index bb947d68d..1941038bf 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -156,7 +156,7 @@ public: // Left wheel of the rear axle. RearLeft, // Right wheel of the rear axle. - ReadRight, + RearRight, // Non-drivable wheel. NoDrive, }; diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 00a5c0d0f..2086cd8dc 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -4,6 +4,7 @@ #include "Engine/Platform/Platform.h" #include "Engine/Platform/Window.h" +#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/CreateWindowSettings.h" #include "Engine/Platform/WindowsManager.h" #include "Engine/Platform/MemoryStats.h" @@ -1118,6 +1119,19 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) { ASSERT(filename); + // Add folder to search path to load dependency libraries + StringView folder = StringUtils::GetDirectoryName(filename); + if (folder.HasChars() && FileSystem::IsRelative(folder)) + folder = StringView::Empty; + if (folder.HasChars()) + { + Char& end = ((Char*)folder.Get())[folder.Length()]; + const Char c = end; + end = 0; + SetDllDirectoryW(*folder); + end = c; + } + // Avoiding windows dialog boxes if missing const DWORD errorMode = SEM_NOOPENFILEERRORBOX; DWORD prevErrorMode = 0; @@ -1134,12 +1148,15 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) { SetThreadErrorMode(prevErrorMode, nullptr); } + if (folder.HasChars()) + { + SetDllDirectoryW(nullptr); + } #if CRASH_LOG_ENABLE // Refresh modules info during next stack trace collecting to have valid debug symbols information SymLocker.Lock(); - const auto folder = StringUtils::GetDirectoryName(filename); - if (!SymbolsPath.Contains(folder)) + if (folder.HasChars() && !SymbolsPath.Contains(folder)) { SymbolsPath.Add(folder); OnSymbolsPathModified(); diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp index 13b46991f..a8e16f715 100644 --- a/Source/Engine/Render2D/FontManager.cpp +++ b/Source/Engine/Render2D/FontManager.cpp @@ -287,6 +287,8 @@ bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) void FontManager::Invalidate(FontCharacterEntry& entry) { + if (entry.TextureIndex == MAX_uint8) + return; auto atlas = Atlases[entry.TextureIndex]; const uint32 padding = atlas->GetPaddingAmount(); const uint32 slotX = static_cast(entry.UV.X - padding); diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index 8c9326337..a43220f08 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -154,13 +154,13 @@ VariantType MUtils::UnboxVariantType(MonoType* monoType) if (klass == mono_get_boolean_class() || monoType->type == MONO_TYPE_BOOLEAN) return VariantType(VariantType::Bool); if (klass == mono_get_byte_class() || monoType->type == MONO_TYPE_U1) - return VariantType(VariantType::Int); + return VariantType(VariantType::Int16); if (klass == mono_get_sbyte_class() || monoType->type == MONO_TYPE_I1) - return VariantType(VariantType::Int); + return VariantType(VariantType::Int16); if (klass == mono_get_int16_class() || monoType->type == MONO_TYPE_I2) - return VariantType(VariantType::Int); + return VariantType(VariantType::Int16); if (klass == mono_get_uint16_class() || monoType->type == MONO_TYPE_U2) - return VariantType(VariantType::Uint); + return VariantType(VariantType::Uint16); if (klass == mono_get_int32_class() || monoType->type == MONO_TYPE_I4) return VariantType(VariantType::Int); if (klass == mono_get_uint32_class() || monoType->type == MONO_TYPE_U4) @@ -266,13 +266,13 @@ Variant MUtils::UnboxVariant(MonoObject* value) if (klass == mono_get_boolean_class()) return *static_cast(mono_object_unbox(value)); if (klass == mono_get_byte_class()) - return (int32)*static_cast(mono_object_unbox(value)); + return (int16)*static_cast(mono_object_unbox(value)); if (klass == mono_get_sbyte_class()) - return (int32)*static_cast(mono_object_unbox(value)); + return (int16)*static_cast(mono_object_unbox(value)); if (klass == mono_get_int16_class()) - return (int32)*static_cast(mono_object_unbox(value)); + return *static_cast(mono_object_unbox(value)); if (klass == mono_get_uint16_class()) - return (uint32)*static_cast(mono_object_unbox(value)); + return *static_cast(mono_object_unbox(value)); if (klass == mono_get_int32_class()) return *static_cast(mono_object_unbox(value)); if (klass == mono_get_uint32_class()) @@ -365,6 +365,10 @@ MonoObject* MUtils::BoxVariant(const Variant& value) return nullptr; case VariantType::Bool: return mono_value_box(mono_domain_get(), mono_get_boolean_class(), (void*)&value.AsBool); + case VariantType::Int16: + return mono_value_box(mono_domain_get(), mono_get_int16_class(), (void*)&value.AsInt16); + case VariantType::Uint16: + return mono_value_box(mono_domain_get(), mono_get_uint16_class(), (void*)&value.AsUint16); case VariantType::Int: return mono_value_box(mono_domain_get(), mono_get_int32_class(), (void*)&value.AsInt); case VariantType::Uint: @@ -534,6 +538,10 @@ MonoClass* MUtils::GetClass(const VariantType& value) return mono_get_void_class(); case VariantType::Bool: return mono_get_boolean_class(); + case VariantType::Int16: + return mono_get_int16_class(); + case VariantType::Uint16: + return mono_get_uint16_class(); case VariantType::Int: return mono_get_int32_class(); case VariantType::Uint: @@ -600,6 +608,10 @@ MonoClass* MUtils::GetClass(const Variant& value) return mono_get_void_class(); case VariantType::Bool: return mono_get_boolean_class(); + case VariantType::Int16: + return mono_get_int16_class(); + case VariantType::Uint16: + return mono_get_uint16_class(); case VariantType::Int: return mono_get_int32_class(); case VariantType::Uint: @@ -700,12 +712,18 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa case MONO_TYPE_CHAR: case MONO_TYPE_I1: case MONO_TYPE_I2: + if (value.Type.Type != VariantType::Int16) + value = (int16)value; + return &value.AsInt16; case MONO_TYPE_I4: if (value.Type.Type != VariantType::Int) value = (int32)value; return &value.AsInt; case MONO_TYPE_U1: case MONO_TYPE_U2: + if (value.Type.Type != VariantType::Uint16) + value = (uint16)value; + return &value.AsUint16; case MONO_TYPE_U4: if (value.Type.Type != VariantType::Uint) value = (uint32)value; diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs index 2ebf16a79..149bdc241 100644 --- a/Source/Engine/Serialization/JsonConverters.cs +++ b/Source/Engine/Serialization/JsonConverters.cs @@ -123,6 +123,13 @@ namespace FlaxEngine.Json writer.WriteStartObject(); { +#if FLAX_EDITOR + if ((serializer.TypeNameHandling & TypeNameHandling.Objects) == TypeNameHandling.Objects) + { + writer.WritePropertyName("$type"); + writer.WriteValue("FlaxEngine.Margin, FlaxEngine.CSharp"); + } +#endif writer.WritePropertyName("Left"); writer.WriteValue(valueMargin.Left); writer.WritePropertyName("Right"); diff --git a/Source/Engine/Serialization/Stream.cpp b/Source/Engine/Serialization/Stream.cpp index 269d49562..21d10f987 100644 --- a/Source/Engine/Serialization/Stream.cpp +++ b/Source/Engine/Serialization/Stream.cpp @@ -274,6 +274,12 @@ void ReadStream::ReadVariant(Variant* data) case VariantType::Bool: data->AsBool = ReadBool(); break; + case VariantType::Int16: + ReadInt16(&data->AsInt16); + break; + case VariantType::Uint16: + ReadUint16(&data->AsUint16); + break; case VariantType::Int: ReadInt32(&data->AsInt); break; @@ -573,6 +579,12 @@ void WriteStream::WriteVariant(const Variant& data) case VariantType::Bool: WriteBool(data.AsBool); break; + case VariantType::Int16: + WriteInt16(data.AsInt16); + break; + case VariantType::Uint16: + WriteUint16(data.AsUint16); + break; case VariantType::Int: WriteInt32(data.AsInt); break; diff --git a/Source/Engine/UI/GUI/Brushes/GPUTextureBrush.cs b/Source/Engine/UI/GUI/Brushes/GPUTextureBrush.cs index f91e3dfe3..77ad80758 100644 --- a/Source/Engine/UI/GUI/Brushes/GPUTextureBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/GPUTextureBrush.cs @@ -14,6 +14,12 @@ namespace FlaxEngine.GUI [HideInEditor] public GPUTexture Texture; + /// + /// The texture sampling filter mode. + /// + [ExpandGroups, Tooltip("The texture sampling filter mode.")] + public BrushFilter Filter = BrushFilter.Linear; + /// /// Initializes a new instance of the class. /// @@ -36,7 +42,10 @@ namespace FlaxEngine.GUI /// public void Draw(Rectangle rect, Color color) { - Render2D.DrawTexture(Texture, rect, color); + if (Filter == BrushFilter.Point) + Render2D.DrawTexturePoint(Texture, rect, color); + else + Render2D.DrawTexture(Texture, rect, color); } } } diff --git a/Source/Engine/UI/GUI/Brushes/IBrush.cs b/Source/Engine/UI/GUI/Brushes/IBrush.cs index ec5ba420a..5e4df2694 100644 --- a/Source/Engine/UI/GUI/Brushes/IBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/IBrush.cs @@ -2,6 +2,24 @@ namespace FlaxEngine.GUI { + /// + /// Texture brush sampling modes. + /// + public enum BrushFilter + { + /// + /// The point sampling without blending. + /// + [Tooltip("The point sampling without blending.")] + Point = 0, + + /// + /// The linear color sampling. + /// + [Tooltip("The linear color sampling.")] + Linear = 1, + }; + /// /// Interface that unifies input source textures, sprites, render targets, and any other brushes to be used in a more generic way. /// diff --git a/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs b/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs index 9867e1f25..05c0bb2e2 100644 --- a/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs @@ -11,9 +11,15 @@ namespace FlaxEngine.GUI /// /// The sprite. /// - [ExpandGroups] + [ExpandGroups, EditorOrder(0), Tooltip("The sprite.")] public SpriteHandle Sprite; + /// + /// The texture sampling filter mode. + /// + [ExpandGroups, EditorOrder(1), Tooltip("The texture sampling filter mode.")] + public BrushFilter Filter = BrushFilter.Linear; + /// /// Initializes a new instance of the class. /// @@ -36,7 +42,10 @@ namespace FlaxEngine.GUI /// public void Draw(Rectangle rect, Color color) { - Render2D.DrawSprite(Sprite, rect, color); + if (Filter == BrushFilter.Point) + Render2D.DrawSpritePoint(Sprite, rect, color); + else + Render2D.DrawSprite(Sprite, rect, color); } } } diff --git a/Source/Engine/UI/GUI/Brushes/TextureBrush.cs b/Source/Engine/UI/GUI/Brushes/TextureBrush.cs index 118128c71..1f772e0a6 100644 --- a/Source/Engine/UI/GUI/Brushes/TextureBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/TextureBrush.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; + namespace FlaxEngine.GUI { /// @@ -11,9 +13,15 @@ namespace FlaxEngine.GUI /// /// The texture. /// - [ExpandGroups, Tooltip("The texture asset.")] + [ExpandGroups, EditorOrder(0), Tooltip("The texture asset.")] public Texture Texture; + /// + /// The texture sampling filter mode. + /// + [ExpandGroups, EditorOrder(1), Tooltip("The texture sampling filter mode.")] + public BrushFilter Filter = BrushFilter.Linear; + /// /// Initializes a new instance of the class. /// @@ -36,7 +44,10 @@ namespace FlaxEngine.GUI /// public void Draw(Rectangle rect, Color color) { - Render2D.DrawTexture(Texture, rect, color); + if (Filter == BrushFilter.Point) + Render2D.DrawTexturePoint(Texture?.Texture, rect, color); + else + Render2D.DrawTexture(Texture, rect, color); } } } diff --git a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs index 3e2ad1b31..6667aab9f 100644 --- a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs @@ -71,6 +71,13 @@ namespace Flax.Build.Bindings } } + public void EnsureInited(Builder.BuildData buildData) + { + if (IsInited) + return; + Init(buildData); + } + public virtual void Init(Builder.BuildData buildData) { IsInited = true; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs index 27c902a5c..ded954e5a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs @@ -261,7 +261,7 @@ namespace Flax.Build.Bindings { if (typeInfo == null) return null; - var result = FindApiTypeInfoInner(typeInfo, caller); + var result = FindApiTypeInfoInner(buildData, typeInfo, caller); if (result != null) return result; if (buildData.TypeCache.TryGetValue(typeInfo, out result)) @@ -274,7 +274,7 @@ namespace Flax.Build.Bindings // Find across all loaded modules for this build foreach (var e in buildData.ModulesInfo) { - result = FindApiTypeInfoInner(typeInfo, e.Value); + result = FindApiTypeInfoInner(buildData, typeInfo, e.Value); if (result != null) { buildData.TypeCache.Add(typeInfo, result); @@ -291,7 +291,7 @@ namespace Flax.Build.Bindings { if (result == null) return null; - result = FindApiTypeInfoInner(new TypeInfo { Type = nesting[i], }, result); + result = FindApiTypeInfoInner(buildData, new TypeInfo { Type = nesting[i], }, result); } return result; } @@ -301,14 +301,17 @@ namespace Flax.Build.Bindings return null; } - private static ApiTypeInfo FindApiTypeInfoInner(TypeInfo typeInfo, ApiTypeInfo parent) + private static ApiTypeInfo FindApiTypeInfoInner(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent) { foreach (var child in parent.Children) { if (child.Name == typeInfo.Type) + { + child.EnsureInited(buildData); return child; + } - var result = FindApiTypeInfoInner(typeInfo, child); + var result = FindApiTypeInfoInner(buildData, typeInfo, child); if (result != null) return result; } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 26fa55231..37e5dda23 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -14,7 +14,7 @@ namespace Flax.Build.Bindings private static readonly HashSet CSharpUsedNamespaces = new HashSet(); private static readonly List CSharpUsedNamespacesSorted = new List(); - private static readonly Dictionary CSharpNativeToManagedBasicTypes = new Dictionary() + internal static readonly Dictionary CSharpNativeToManagedBasicTypes = new Dictionary() { // Language types { "int8", "sbyte" }, @@ -32,7 +32,7 @@ namespace Flax.Build.Bindings { "double", "double" }, }; - private static readonly Dictionary CSharpNativeToManagedDefault = new Dictionary() + internal static readonly Dictionary CSharpNativeToManagedDefault = new Dictionary() { // Engine types { "String", "string" }, @@ -201,6 +201,8 @@ namespace Flax.Build.Bindings var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) { + CSharpUsedNamespaces.Add(apiType.Namespace); + if (apiType.IsScriptingObject) return typeInfo.Type.Replace("::", "."); @@ -527,7 +529,7 @@ namespace Flax.Build.Bindings contents.Append("abstract "); contents.Append("unsafe partial class ").Append(classInfo.Name); if (classInfo.BaseType != null && !classInfo.IsBaseTypeHidden) - contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, classInfo.BaseType, classInfo)); + contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = classInfo.BaseType.Name }, classInfo)); contents.AppendLine(); contents.Append(indent + "{"); indent += " "; @@ -861,7 +863,7 @@ namespace Flax.Build.Bindings contents.Append("private "); contents.Append("unsafe partial struct ").Append(structureInfo.Name); if (structureInfo.BaseType != null && structureInfo.IsPod) - contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, structureInfo.BaseType, structureInfo)); + contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = structureInfo.BaseType.Name }, structureInfo)); contents.AppendLine(); contents.Append(indent + "{"); indent += " "; @@ -1048,30 +1050,6 @@ namespace Flax.Build.Bindings return true; } - private static void GenerateCSharpCollectNamespaces(BuildData buildData, ApiTypeInfo apiType, HashSet usedNamespaces) - { - if (apiType is ClassInfo classInfo) - { - foreach (var field in classInfo.Fields) - { - var fieldInfo = FindApiTypeInfo(buildData, field.Type, classInfo); - if (fieldInfo != null && !string.IsNullOrWhiteSpace(fieldInfo.Namespace) && fieldInfo.Namespace != apiType.Namespace) - usedNamespaces.Add(fieldInfo.Namespace); - } - } - else if (apiType is StructureInfo structureInfo) - { - foreach (var field in structureInfo.Fields) - { - var fieldInfo = FindApiTypeInfo(buildData, field.Type, structureInfo); - if (fieldInfo != null && !string.IsNullOrWhiteSpace(fieldInfo.Namespace) && fieldInfo.Namespace != apiType.Namespace) - usedNamespaces.Add(fieldInfo.Namespace); - } - } - foreach (var child in apiType.Children) - GenerateCSharpCollectNamespaces(buildData, child, usedNamespaces); - } - private static void GenerateCSharp(BuildData buildData, ModuleInfo moduleInfo, ref BindingsResult bindings) { var contents = new StringBuilder(); @@ -1084,29 +1062,17 @@ namespace Flax.Build.Bindings contents.Append("true").AppendLine(); contents.AppendLine("// This code was auto-generated. Do not modify it."); contents.AppendLine(); + var headerPos = contents.Length; - // Using declarations CSharpUsedNamespaces.Clear(); + CSharpUsedNamespaces.Add(null); + CSharpUsedNamespaces.Add(string.Empty); CSharpUsedNamespaces.Add("System"); CSharpUsedNamespaces.Add("System.ComponentModel"); CSharpUsedNamespaces.Add("System.Globalization"); CSharpUsedNamespaces.Add("System.Runtime.CompilerServices"); CSharpUsedNamespaces.Add("System.Runtime.InteropServices"); CSharpUsedNamespaces.Add("FlaxEngine"); - foreach (var e in moduleInfo.Children) - { - foreach (var apiTypeInfo in e.Children) - { - GenerateCSharpCollectNamespaces(buildData, apiTypeInfo, CSharpUsedNamespaces); - } - } - CSharpUsedNamespacesSorted.Clear(); - CSharpUsedNamespacesSorted.AddRange(CSharpUsedNamespaces); - CSharpUsedNamespacesSorted.Sort(); - foreach (var e in CSharpUsedNamespacesSorted) - contents.AppendLine($"using {e};"); - // TODO: custom using declarations support - // TODO: generate using declarations based on references modules (eg. using FlaxEngine, using Plugin1 in game API) // Process all API types from the file var useBindings = false; @@ -1121,7 +1087,21 @@ namespace Flax.Build.Bindings if (!useBindings) return; + { + var header = new StringBuilder(); + + // Using declarations + CSharpUsedNamespacesSorted.Clear(); + CSharpUsedNamespacesSorted.AddRange(CSharpUsedNamespaces); + CSharpUsedNamespacesSorted.Sort(); + for (var i = 2; i < CSharpUsedNamespacesSorted.Count; i++) + header.AppendLine($"using {CSharpUsedNamespacesSorted[i]};"); + + contents.Insert(headerPos, header.ToString()); + } + // Save generated file + contents.AppendLine(); contents.AppendLine("#endif"); Utilities.WriteFileIfChanged(bindings.GeneratedCSharpFilePath, contents.ToString()); } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs index 8fc7d18f2..0bb1f4cc7 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs @@ -19,7 +19,7 @@ namespace Flax.Build.Bindings partial class BindingsGenerator { private static readonly Dictionary TypeCache = new Dictionary(); - private const int CacheVersion = 7; + private const int CacheVersion = 8; internal static void Write(BinaryWriter writer, string e) { diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 78fd3df73..a2fffddd4 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1095,7 +1095,7 @@ namespace Flax.Build.Bindings var baseType = classInfo?.BaseType ?? structureInfo?.BaseType; if (classInfo != null && classInfo.IsBaseTypeHidden) baseType = null; - if (baseType != null && (baseType.Type == "PersistentScriptingObject" || baseType.Type == "ScriptingObject")) + if (baseType != null && (baseType.Name == "PersistentScriptingObject" || baseType.Name == "ScriptingObject")) baseType = null; CppAutoSerializeFields.Clear(); CppAutoSerializeProperties.Clear(); @@ -1105,7 +1105,7 @@ namespace Flax.Build.Bindings contents.Append($"void {typeNameNative}::Serialize(SerializeStream& stream, const void* otherObj)").AppendLine(); contents.Append('{').AppendLine(); if (baseType != null) - contents.Append($" {baseType}::Serialize(stream, otherObj);").AppendLine(); + contents.Append($" {baseType.FullNameNative}::Serialize(stream, otherObj);").AppendLine(); contents.Append($" SERIALIZE_GET_OTHER_OBJ({typeNameNative});").AppendLine(); if (classInfo != null) @@ -1161,7 +1161,7 @@ namespace Flax.Build.Bindings contents.Append($"void {typeNameNative}::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)").AppendLine(); contents.Append('{').AppendLine(); if (baseType != null) - contents.Append($" {baseType}::Deserialize(stream, modifier);").AppendLine(); + contents.Append($" {baseType.FullNameNative}::Deserialize(stream, modifier);").AppendLine(); foreach (var fieldInfo in CppAutoSerializeFields) { @@ -1554,7 +1554,7 @@ namespace Flax.Build.Bindings else contents.Append($"(ScriptingType::SpawnHandler)&{classTypeNameNative}::Spawn, "); if (classInfo.BaseType != null && useScripting) - contents.Append($"&{classInfo.BaseType}::TypeInitializer, "); + contents.Append($"&{classInfo.BaseType.FullNameNative}::TypeInitializer, "); else contents.Append("nullptr, "); contents.Append(setupScriptVTable); @@ -1566,7 +1566,7 @@ namespace Flax.Build.Bindings else contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor, "); if (classInfo.BaseType != null) - contents.Append($"&{classInfo.BaseType}::TypeInitializer"); + contents.Append($"&{classInfo.BaseType.FullNameNative}::TypeInitializer"); else contents.Append("nullptr"); } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 67e90efa9..8ed4a1634 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -416,38 +416,38 @@ namespace Flax.Build.Bindings token = accessToken; break; } - - var baseTypeInfo = new TypeInfo + var inheritType = new TypeInfo { Type = token.Value, }; - if (token.Value.Length > 2 && token.Value[0] == 'I' && char.IsUpper(token.Value[1])) - { - // Interface - if (desc.InterfaceNames == null) - desc.InterfaceNames = new List(); - desc.InterfaceNames.Add(baseTypeInfo); - token = context.Tokenizer.NextToken(); - continue; - } - - if (desc.BaseType != null) - { - // Allow for multiple base classes, just the first one needs to be a valid base type - break; - throw new Exception($"Invalid '{desc.Name}' inheritance (only single base class is allowed for scripting types, excluding interfaces)."); - } - desc.BaseType = baseTypeInfo; + if (desc.Inheritance == null) + desc.Inheritance = new List(); + desc.Inheritance.Add(inheritType); token = context.Tokenizer.NextToken(); if (token.Type == TokenType.LeftCurlyBrace) { break; } + if (token.Type == TokenType.Colon) + { + token = context.Tokenizer.ExpectToken(TokenType.Colon); + token = context.Tokenizer.NextToken(); + inheritType.Type = token.Value; + token = context.Tokenizer.NextToken(); + continue; + } + if (token.Type == TokenType.DoubleColon) + { + token = context.Tokenizer.NextToken(); + inheritType.Type += token.Value; + token = context.Tokenizer.NextToken(); + continue; + } if (token.Type == TokenType.LeftAngleBracket) { var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier); token = context.Tokenizer.ExpectToken(TokenType.RightAngleBracket); - desc.BaseType.GenericArgs = new List + inheritType.GenericArgs = new List { new TypeInfo { @@ -456,9 +456,9 @@ namespace Flax.Build.Bindings }; // TODO: find better way to resolve this (custom base type attribute?) - if (desc.BaseType.Type == "ShaderAssetTypeBase") + if (inheritType.Type == "ShaderAssetTypeBase") { - desc.BaseType = desc.BaseType.GenericArgs[0]; + desc.Inheritance[desc.Inheritance.Count - 1] = inheritType.GenericArgs[0]; } token = context.Tokenizer.NextToken(); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index f8d26314f..fa219477a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Flax.Build.NativeCpp; @@ -109,13 +108,6 @@ namespace Flax.Build.Bindings if (LoadCache(ref moduleInfo, moduleOptions, headerFiles)) { buildData.ModulesInfo[module] = moduleInfo; - - // Initialize API - using (new ProfileEventScope("Init")) - { - moduleInfo.Init(buildData); - } - return moduleInfo; } } @@ -164,12 +156,6 @@ namespace Flax.Build.Bindings } } - // Initialize API - using (new ProfileEventScope("Init")) - { - moduleInfo.Init(buildData); - } - return moduleInfo; } @@ -533,23 +519,10 @@ namespace Flax.Build.Bindings if (moduleInfo.IsFromCache) return; - // Process parsed API - using (new ProfileEventScope("Process")) + // Initialize parsed API + using (new ProfileEventScope("Init")) { - foreach (var child in moduleInfo.Children) - { - try - { - foreach (var apiTypeInfo in child.Children) - ProcessAndValidate(buildData, apiTypeInfo); - } - catch (Exception) - { - if (child is FileInfo fileInfo) - Log.Error($"Failed to validate '{fileInfo.Name}' file to generate bindings."); - throw; - } - } + moduleInfo.Init(buildData); } // Generate bindings for scripting @@ -583,122 +556,5 @@ namespace Flax.Build.Bindings } } } - - private static void ProcessAndValidate(BuildData buildData, ApiTypeInfo apiTypeInfo) - { - if (apiTypeInfo is ClassInfo classInfo) - ProcessAndValidate(buildData, classInfo); - else if (apiTypeInfo is StructureInfo structureInfo) - ProcessAndValidate(buildData, structureInfo); - - foreach (var child in apiTypeInfo.Children) - ProcessAndValidate(buildData, child); - } - - private static void ProcessAndValidate(BuildData buildData, ClassInfo classInfo) - { - if (classInfo.UniqueFunctionNames == null) - classInfo.UniqueFunctionNames = new HashSet(); - - foreach (var fieldInfo in classInfo.Fields) - { - if (fieldInfo.Access == AccessLevel.Private) - continue; - - fieldInfo.Getter = new FunctionInfo - { - Name = "Get" + fieldInfo.Name, - Comment = fieldInfo.Comment, - IsStatic = fieldInfo.IsStatic, - Access = fieldInfo.Access, - Attributes = fieldInfo.Attributes, - ReturnType = fieldInfo.Type, - Parameters = new List(), - IsVirtual = false, - IsConst = true, - Glue = new FunctionInfo.GlueInfo() - }; - ProcessAndValidate(classInfo, fieldInfo.Getter); - fieldInfo.Getter.Name = fieldInfo.Name; - - if (!fieldInfo.IsReadOnly) - { - fieldInfo.Setter = new FunctionInfo - { - Name = "Set" + fieldInfo.Name, - Comment = fieldInfo.Comment, - IsStatic = fieldInfo.IsStatic, - Access = fieldInfo.Access, - Attributes = fieldInfo.Attributes, - ReturnType = new TypeInfo - { - Type = "void", - }, - Parameters = new List - { - new FunctionInfo.ParameterInfo - { - Name = "value", - Type = fieldInfo.Type, - }, - }, - IsVirtual = false, - IsConst = true, - Glue = new FunctionInfo.GlueInfo() - }; - ProcessAndValidate(classInfo, fieldInfo.Setter); - fieldInfo.Setter.Name = fieldInfo.Name; - } - } - - foreach (var propertyInfo in classInfo.Properties) - { - if (propertyInfo.Getter != null) - ProcessAndValidate(classInfo, propertyInfo.Getter); - if (propertyInfo.Setter != null) - ProcessAndValidate(classInfo, propertyInfo.Setter); - } - - foreach (var functionInfo in classInfo.Functions) - ProcessAndValidate(classInfo, functionInfo); - } - - private static void ProcessAndValidate(BuildData buildData, StructureInfo structureInfo) - { - foreach (var fieldInfo in structureInfo.Fields) - { - if (fieldInfo.Type.IsBitField) - throw new NotImplementedException($"TODO: support bit-fields in structure fields (found field {fieldInfo} in structure {structureInfo.Name})"); - - // Pointers are fine - if (fieldInfo.Type.IsPtr) - continue; - - // In-build types - if (CSharpNativeToManagedBasicTypes.ContainsKey(fieldInfo.Type.Type)) - continue; - if (CSharpNativeToManagedDefault.ContainsKey(fieldInfo.Type.Type)) - continue; - - // Find API type info for this field type - var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo); - if (apiType != null) - continue; - - throw new Exception($"Unknown field type '{fieldInfo.Type} {fieldInfo.Name}' in structure '{structureInfo.Name}'."); - } - } - - private static void ProcessAndValidate(ClassInfo classInfo, FunctionInfo functionInfo) - { - // Ensure that methods have unique names for bindings - if (classInfo.UniqueFunctionNames == null) - classInfo.UniqueFunctionNames = new HashSet(); - int idx = 1; - functionInfo.UniqueName = functionInfo.Name; - while (classInfo.UniqueFunctionNames.Contains(functionInfo.UniqueName)) - functionInfo.UniqueName = functionInfo.Name + idx++; - classInfo.UniqueFunctionNames.Add(functionInfo.UniqueName); - } } } diff --git a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs index c68ff1a59..ff0bb077b 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs @@ -57,22 +57,87 @@ namespace Flax.Build.Bindings _isScriptingObject = true; else if (BaseType == null) _isScriptingObject = false; - else if (InBuildScriptingObjectTypes.Contains(BaseType.Type)) + else if (InBuildScriptingObjectTypes.Contains(BaseType.Name)) _isScriptingObject = true; else + _isScriptingObject = BaseType != null && BaseType.IsScriptingObject; + + if (UniqueFunctionNames == null) + UniqueFunctionNames = new HashSet(); + + foreach (var fieldInfo in Fields) { - var baseApiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, BaseType, this); - if (baseApiTypeInfo != null) + if (fieldInfo.Access == AccessLevel.Private) + continue; + + fieldInfo.Getter = new FunctionInfo { - if (!baseApiTypeInfo.IsInited) - baseApiTypeInfo.Init(buildData); - _isScriptingObject = baseApiTypeInfo.IsScriptingObject; - } - else + Name = "Get" + fieldInfo.Name, + Comment = fieldInfo.Comment, + IsStatic = fieldInfo.IsStatic, + Access = fieldInfo.Access, + Attributes = fieldInfo.Attributes, + ReturnType = fieldInfo.Type, + Parameters = new List(), + IsVirtual = false, + IsConst = true, + Glue = new FunctionInfo.GlueInfo() + }; + ProcessAndValidate(fieldInfo.Getter); + fieldInfo.Getter.Name = fieldInfo.Name; + + if (!fieldInfo.IsReadOnly) { - _isScriptingObject = false; + fieldInfo.Setter = new FunctionInfo + { + Name = "Set" + fieldInfo.Name, + Comment = fieldInfo.Comment, + IsStatic = fieldInfo.IsStatic, + Access = fieldInfo.Access, + Attributes = fieldInfo.Attributes, + ReturnType = new TypeInfo + { + Type = "void", + }, + Parameters = new List + { + new FunctionInfo.ParameterInfo + { + Name = "value", + Type = fieldInfo.Type, + }, + }, + IsVirtual = false, + IsConst = true, + Glue = new FunctionInfo.GlueInfo() + }; + ProcessAndValidate(fieldInfo.Setter); + fieldInfo.Setter.Name = fieldInfo.Name; } } + + foreach (var propertyInfo in Properties) + { + if (propertyInfo.Getter != null) + ProcessAndValidate(propertyInfo.Getter); + if (propertyInfo.Setter != null) + ProcessAndValidate(propertyInfo.Setter); + } + + foreach (var functionInfo in Functions) + ProcessAndValidate(functionInfo); + } + + private void ProcessAndValidate(FunctionInfo functionInfo) + { + // Ensure that methods have unique names for bindings + if (UniqueFunctionNames == null) + UniqueFunctionNames = new HashSet(); + int idx = 1; + functionInfo.UniqueName = functionInfo.Name; + while (UniqueFunctionNames.Contains(functionInfo.UniqueName)) + functionInfo.UniqueName = functionInfo.Name + idx++; + UniqueFunctionNames.Add(functionInfo.UniqueName); } public override void Write(BinaryWriter writer) @@ -113,7 +178,7 @@ namespace Flax.Build.Bindings { if (_scriptVTableSize == -1) { - if (BindingsGenerator.FindApiTypeInfo(buildData, BaseType, this) is ClassInfo baseApiTypeInfo) + if (BaseType is ClassInfo baseApiTypeInfo) { _scriptVTableOffset = baseApiTypeInfo.GetScriptVTableSize(buildData, out _); } diff --git a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs index 2f0dad003..ca1bb9257 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using System.IO; @@ -12,29 +13,37 @@ namespace Flax.Build.Bindings { public AccessLevel Access; public AccessLevel BaseTypeInheritance; - public TypeInfo BaseType; - public List Interfaces; // Optional - public List InterfaceNames; // Optional + public ClassStructInfo BaseType; + public List Interfaces; + public List Inheritance; // Data from parsing, used to interfaces and base type construct in Init public override void Init(Builder.BuildData buildData) { base.Init(buildData); - if (Interfaces == null && InterfaceNames != null && InterfaceNames.Count != 0) + if (BaseType == null && Interfaces == null && Inheritance != null) { - Interfaces = new List(); - for (var i = 0; i < InterfaceNames.Count; i++) + // Extract base class and interfaces from inheritance info + for (int i = 0; i < Inheritance.Count; i++) { - var interfaceName = InterfaceNames[i]; - var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, interfaceName, this); + var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, Inheritance[i], Parent); if (apiTypeInfo is InterfaceInfo interfaceInfo) { + if (Interfaces == null) + Interfaces = new List(); Interfaces.Add(interfaceInfo); } + else if (apiTypeInfo is ClassStructInfo otherInfo) + { + if (otherInfo == this) + throw new Exception($"Type '{Name}' inherits from itself."); + if (BaseType != null) + throw new Exception($"Invalid '{Name}' inheritance (only single base class is allowed for scripting types, excluding interfaces)."); + BaseType = otherInfo; + } } - if (Interfaces.Count == 0) - Interfaces = null; } + BaseType?.EnsureInited(buildData); } public override void Write(BinaryWriter writer) @@ -42,7 +51,7 @@ namespace Flax.Build.Bindings writer.Write((byte)Access); writer.Write((byte)BaseTypeInheritance); BindingsGenerator.Write(writer, BaseType); - BindingsGenerator.Write(writer, InterfaceNames); + BindingsGenerator.Write(writer, Inheritance); base.Write(writer); } @@ -52,7 +61,7 @@ namespace Flax.Build.Bindings Access = (AccessLevel)reader.ReadByte(); BaseTypeInheritance = (AccessLevel)reader.ReadByte(); BaseType = BindingsGenerator.Read(reader, BaseType); - InterfaceNames = BindingsGenerator.Read(reader, InterfaceNames); + Inheritance = BindingsGenerator.Read(reader, Inheritance); base.Read(reader); } diff --git a/Source/Tools/Flax.Build/Bindings/FileInfo.cs b/Source/Tools/Flax.Build/Bindings/FileInfo.cs index 6c4b4dcfe..7a38c8d87 100644 --- a/Source/Tools/Flax.Build/Bindings/FileInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FileInfo.cs @@ -17,6 +17,19 @@ namespace Flax.Build.Bindings base.AddChild(apiTypeInfo); } + public override void Init(Builder.BuildData buildData) + { + try + { + base.Init(buildData); + } + catch (Exception) + { + Log.Error($"Failed to init '{Name}' file scripting API."); + throw; + } + } + public int CompareTo(FileInfo other) { return Name.CompareTo(other.Name); diff --git a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs index 5b664cc09..7e6888d91 100644 --- a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs @@ -26,14 +26,14 @@ namespace Flax.Build.Bindings { base.Init(buildData); - if (ForceNoPod || (InterfaceNames != null && InterfaceNames.Count != 0)) + if (ForceNoPod || (Interfaces != null && Interfaces.Count != 0)) { _isPod = false; return; } // Structure is POD (plain old data) only if all of it's fields are (and has no base type ro base type is also POD) - _isPod = BaseType == null || (BindingsGenerator.FindApiTypeInfo(buildData, BaseType, Parent)?.IsPod ?? false); + _isPod = BaseType == null || (BaseType?.IsPod ?? false); for (int i = 0; _isPod && i < Fields.Count; i++) { var field = Fields[i]; @@ -42,6 +42,29 @@ namespace Flax.Build.Bindings _isPod = false; } } + + foreach (var fieldInfo in Fields) + { + if (fieldInfo.Type.IsBitField) + throw new NotImplementedException($"TODO: support bit-fields in structure fields (found field {fieldInfo} in structure {Name})"); + + // Pointers are fine + if (fieldInfo.Type.IsPtr) + continue; + + // In-build types + if (BindingsGenerator.CSharpNativeToManagedBasicTypes.ContainsKey(fieldInfo.Type.Type)) + continue; + if (BindingsGenerator.CSharpNativeToManagedDefault.ContainsKey(fieldInfo.Type.Type)) + continue; + + // Find API type info for this field type + var apiType = BindingsGenerator.FindApiTypeInfo(buildData, fieldInfo.Type, this); + if (apiType != null) + continue; + + throw new Exception($"Unknown field type '{fieldInfo.Type} {fieldInfo.Name}' in structure '{Name}'."); + } } public override void Write(BinaryWriter writer) diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index 443b0687a..9268e5998 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -36,8 +36,7 @@ namespace Flax.Build.Bindings var apiType = BindingsGenerator.FindApiTypeInfo(buildData, this, caller); if (apiType != null) { - if (!apiType.IsInited) - apiType.Init(buildData); + apiType.EnsureInited(buildData); return apiType.IsPod; } diff --git a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs index e2d01a820..ce39eb3a9 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/Builder.DotNet.cs @@ -46,22 +46,35 @@ namespace Flax.Build var outputPath = Path.GetDirectoryName(buildData.Target.GetOutputFilePath(buildOptions)); var outputFile = Path.Combine(outputPath, binaryModuleName + ".CSharp.dll"); var outputDocFile = Path.Combine(outputPath, binaryModuleName + ".CSharp.xml"); - string monoRoot, exePath; + string monoRoot, monoPath, cscPath; switch (buildPlatform) { case TargetPlatform.Windows: + { monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Windows", "Mono"); - exePath = Path.Combine(monoRoot, "bin", "mono.exe"); + + // Prefer installed Roslyn C# compiler over Mono one + monoPath = null; + cscPath = Path.Combine(Path.GetDirectoryName(Deploy.VCEnvironment.MSBuildPath), "Roslyn", "csc.exe"); + + if (!File.Exists(cscPath)) + { + // Fallback to Mono binaries + monoPath = Path.Combine(monoRoot, "bin", "mono.exe"); + cscPath = Path.Combine(monoRoot, "lib", "mono", "4.5", "csc.exe"); + } break; + } case TargetPlatform.Linux: monoRoot = Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Editor", "Linux", "Mono"); - exePath = Path.Combine(monoRoot, "bin", "mono"); + monoPath = Path.Combine(monoRoot, "bin", "mono"); + cscPath = Path.Combine(monoRoot, "lib", "mono", "4.5", "csc.exe"); break; default: throw new InvalidPlatformException(buildPlatform); } - var cscPath = Path.Combine(monoRoot, "lib", "mono", "4.5", "csc.exe"); var referenceAssemblies = Path.Combine(monoRoot, "lib", "mono", "4.5-api"); var references = new HashSet(buildOptions.ScriptingAPI.FileReferences); + foreach (var module in binaryModule) { if (!buildData.Modules.TryGetValue(module, out var moduleBuildOptions)) @@ -147,11 +160,23 @@ namespace Flax.Build task.PrerequisiteFiles.AddRange(references); task.ProducedFiles.Add(outputFile); task.WorkingDirectory = workspaceRoot; - task.CommandPath = exePath; - task.CommandArguments = $"\"{cscPath}\" /noconfig @\"{responseFile}\""; task.InfoMessage = "Compiling " + outputFile; task.Cost = task.PrerequisiteFiles.Count; + if (monoPath != null) + { + task.CommandPath = monoPath; + task.CommandArguments = $"\"{cscPath}\" /noconfig @\"{responseFile}\""; + } + else + { + // The "/shared" flag enables the compiler server support: + // https://github.com/dotnet/roslyn/blob/main/docs/compilers/Compiler%20Server.md + + task.CommandPath = cscPath; + task.CommandArguments = $"/noconfig /shared @\"{responseFile}\""; + } + // Copy referenced assemblies foreach (var reference in buildOptions.ScriptingAPI.FileReferences) { diff --git a/Source/Tools/Flax.Build/Deploy/Configuration.cs b/Source/Tools/Flax.Build/Deploy/Configuration.cs index 7cf30ccd6..aa97933f4 100644 --- a/Source/Tools/Flax.Build/Deploy/Configuration.cs +++ b/Source/Tools/Flax.Build/Deploy/Configuration.cs @@ -4,6 +4,12 @@ namespace Flax.Build { public static partial class Configuration { + /// + /// Package deployment output path. + /// + [CommandLine("deployOutput", "Package deployment output path.")] + public static string DeployOutput; + /// /// Builds and packages the editor. /// diff --git a/Source/Tools/Flax.Build/Deploy/Deployer.cs b/Source/Tools/Flax.Build/Deploy/Deployer.cs index 9b6ac7657..ce954a5f0 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployer.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployer.cs @@ -16,6 +16,7 @@ namespace Flax.Deploy public static int VersionMajor; public static int VersionMinor; public static int VersionBuild; + public static TargetConfiguration[] Configurations; public static bool Run() { @@ -64,6 +65,8 @@ namespace Flax.Deploy static void Initialize() { + Configurations = Configuration.BuildConfigurations != null ? Configuration.BuildConfigurations : new[] { TargetConfiguration.Debug, TargetConfiguration.Development, TargetConfiguration.Release }; + // Read the current engine version var engineVersion = EngineTarget.EngineVersion; VersionMajor = engineVersion.Major; @@ -81,9 +84,12 @@ namespace Flax.Deploy Utilities.WriteFileIfChanged(Path.Combine(Globals.EngineRoot, "Source/Engine/Core/Config.Gen.h"), buildConfigHeader.ToString()); // Prepare the package output - PackageOutputPath = Path.Combine(Globals.EngineRoot, string.Format("Package_{0}_{1:00}_{2:00000}", VersionMajor, VersionMinor, VersionBuild)); - Utilities.DirectoryDelete(PackageOutputPath); - Directory.CreateDirectory(PackageOutputPath); + if (string.IsNullOrEmpty(Configuration.DeployOutput)) + PackageOutputPath = Path.Combine(Globals.EngineRoot, string.Format("Package_{0}_{1:00}_{2:00000}", VersionMajor, VersionMinor, VersionBuild)); + else + PackageOutputPath = Configuration.DeployOutput; + if (!Directory.Exists(PackageOutputPath)) + Directory.CreateDirectory(PackageOutputPath); Log.Info(string.Empty); Log.Info(string.Empty); @@ -104,9 +110,10 @@ namespace Flax.Deploy private static void BuildEditor() { var targetPlatform = Platform.BuildPlatform.Target; - FlaxBuild.Build(Globals.EngineRoot, "FlaxEditor", targetPlatform, TargetArchitecture.x64, TargetConfiguration.Debug); - FlaxBuild.Build(Globals.EngineRoot, "FlaxEditor", targetPlatform, TargetArchitecture.x64, TargetConfiguration.Development); - FlaxBuild.Build(Globals.EngineRoot, "FlaxEditor", targetPlatform, TargetArchitecture.x64, TargetConfiguration.Release); + foreach (var configuration in Configurations) + { + FlaxBuild.Build(Globals.EngineRoot, "FlaxEditor", targetPlatform, TargetArchitecture.x64, configuration); + } } private static bool CannotBuildPlatform(TargetPlatform platform) @@ -129,9 +136,10 @@ namespace Flax.Deploy { if (Platform.IsPlatformSupported(platform, architecture)) { - FlaxBuild.Build(Globals.EngineRoot, "FlaxGame", platform, architecture, TargetConfiguration.Debug); - FlaxBuild.Build(Globals.EngineRoot, "FlaxGame", platform, architecture, TargetConfiguration.Development); - FlaxBuild.Build(Globals.EngineRoot, "FlaxGame", platform, architecture, TargetConfiguration.Release); + foreach (var configuration in Configurations) + { + FlaxBuild.Build(Globals.EngineRoot, "FlaxGame", platform, architecture, configuration); + } } } diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs index 33dbdbb78..60c2da62e 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Editor.cs @@ -34,15 +34,15 @@ namespace Flax.Deploy // Prepare RootPath = Globals.EngineRoot; OutputPath = Path.Combine(Deployer.PackageOutputPath, "Editor"); + Utilities.DirectoryDelete(OutputPath); Directory.CreateDirectory(OutputPath); Log.Info(string.Empty); Log.Info("Deploy editor files"); Log.Info(string.Empty); // Deploy binaries - DeployEditorBinaries(TargetConfiguration.Debug); - DeployEditorBinaries(TargetConfiguration.Development); - DeployEditorBinaries(TargetConfiguration.Release); + foreach (var configuration in Deployer.Configurations) + DeployEditorBinaries(configuration); { var binariesSubDir = "Binaries/Tools"; var src = Path.Combine(RootPath, binariesSubDir); @@ -130,12 +130,14 @@ namespace Flax.Deploy { // Use system tool (preserves executable file attributes and link files) editorPackageZipPath = Path.Combine(Deployer.PackageOutputPath, "FlaxEditorLinux.zip"); + Utilities.FileDelete(editorPackageZipPath); Utilities.Run("zip", "Editor.zip -r .", null, OutputPath, Utilities.RunOptions.None); File.Move(Path.Combine(OutputPath, "Editor.zip"), editorPackageZipPath); } else { editorPackageZipPath = Path.Combine(Deployer.PackageOutputPath, "Editor.zip"); + Utilities.FileDelete(editorPackageZipPath); using (ZipFile zip = new ZipFile()) { zip.AddDirectory(OutputPath); @@ -152,6 +154,7 @@ namespace Flax.Deploy { Log.Info("Compressing editor debug symbols files..."); editorPackageZipPath = Path.Combine(Deployer.PackageOutputPath, "EditorDebugSymbols.zip"); + Utilities.FileDelete(editorPackageZipPath); using (ZipFile zip = new ZipFile()) { zip.AddDirectory(Path.Combine(Deployer.PackageOutputPath, "EditorDebugSymbols")); diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs index e9fc98acc..8c30beda1 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs @@ -24,6 +24,7 @@ namespace Flax.Deploy string platformName = platform.ToString(); string src = Path.Combine(platformsRoot, platformName); string dst = Path.Combine(Deployer.PackageOutputPath, platformName); + Utilities.DirectoryDelete(dst); // Deploy files { @@ -69,6 +70,7 @@ namespace Flax.Deploy Log.Info("Compressing platform files..."); var packageZipPath = Path.Combine(Deployer.PackageOutputPath, platformName + ".zip"); + Utilities.FileDelete(packageZipPath); using (ZipFile zip = new ZipFile()) { zip.AddDirectory(dst);