// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; using FlaxEditor.Content.Thumbnails; using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows; using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; using Object = FlaxEngine.Object; namespace FlaxEditor.Content { /// /// Content proxy for . /// /// [ContentContextMenu("New/Prefab")] public sealed class PrefabProxy : JsonAssetBaseProxy { private PrefabPreview _preview; /// /// The prefab files extension. /// public static readonly string Extension = "prefab"; /// /// The prefab asset data typename. /// public static readonly string AssetTypename = typeof(Prefab).FullName; /// public override string Name => "Prefab"; /// public override string FileExtension => Extension; /// public override EditorWindow Open(Editor editor, ContentItem item) { return new PrefabWindow(editor, (AssetItem)item); } /// public override bool IsProxyFor(ContentItem item) { return item is PrefabItem; } /// public override bool IsProxyFor() { return typeof(T) == typeof(Prefab); } /// public override Color AccentColor => Color.FromRGB(0x7eef21); /// public override string TypeName => AssetTypename; /// public override AssetItem ConstructItem(string path, string typeName, ref Guid id) { return new PrefabItem(path, id); } /// public override bool CanCreate(ContentFolder targetLocation) { return targetLocation.CanHaveAssets; } /// public override void Create(string outputPath, object arg) { if (!(arg is Actor actor)) { // Create default prefab root object actor = new EmptyActor { Name = "Root" }; // Cleanup it after usage Object.Destroy(actor, 20.0f); } PrefabManager.CreatePrefab(actor, outputPath, true); } /// public override void OnThumbnailDrawPrepare(ThumbnailRequest request) { if (_preview == null) { _preview = new PrefabPreview(false); InitAssetPreview(_preview); } // TODO: disable streaming for asset during thumbnail rendering (and restore it after) } /// public override bool CanDrawThumbnail(ThumbnailRequest request) { if (!_preview.HasLoadedAssets) return false; // Check if asset is streamed enough var asset = (Prefab)request.Asset; return asset.IsLoaded; } private void Prepare(Actor actor) { if (actor is TextRender textRender) { textRender.UpdateLayout(); } for (int i = 0; i < actor.ChildrenCount; i++) { Prepare(actor.GetChild(i)); } } /// public override void OnThumbnailDrawBegin(ThumbnailRequest request, ContainerControl guiRoot, GPUContext context) { _preview.Prefab = (Prefab)request.Asset; _preview.Parent = guiRoot; _preview.Scale = Float2.One; _preview.ShowDefaultSceneActors = true; _preview.SyncBackbufferSize(); // Special case for UI prefabs if (_preview.Instance is UIControl uiControl && uiControl.HasControl) { // Ensure to place UI in a proper way uiControl.Control.Location = Float2.Zero; uiControl.Control.Scale *= PreviewsCache.AssetIconSize / uiControl.Control.Size.MaxValue; uiControl.Control.AnchorPreset = AnchorPresets.TopLeft; uiControl.Control.AnchorPreset = AnchorPresets.MiddleCenter; // Tweak preview _preview.ShowDefaultSceneActors = false; } else { // Update some actors data (some actor types update bounds/data later but its required to be done before rendering) Prepare(_preview.Instance); // Auto fit actor to camera float targetSize = 30.0f; var bounds = _preview.Instance.EditorBoxChildren; var maxSize = Math.Max(0.001f, (float)bounds.Size.MaxValue); _preview.Instance.Scale = new Float3(targetSize / maxSize); _preview.Instance.Position = Vector3.Zero; } _preview.Task.OnDraw(); } /// public override void OnThumbnailDrawEnd(ThumbnailRequest request, ContainerControl guiRoot) { _preview.RemoveChildren(); _preview.Prefab = null; _preview.Parent = null; } /// public override void Dispose() { if (_preview != null) { _preview.Dispose(); _preview = null; } base.Dispose(); } } }