// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #if USE_LARGE_WORLDS using Real = System.Double; #else using Real = System.Single; #endif using System; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Viewport.Cameras; using FlaxEngine; using Object = FlaxEngine.Object; namespace FlaxEditor.Viewport.Previews { /// /// Generic asset preview editor viewport base class. /// /// public abstract class AssetPreview : EditorViewport, IEditorPrimitivesOwner { private ContextMenuButton _showDefaultSceneButton; private IntPtr _debugDrawContext; private bool _debugDrawEnable; private bool _editorPrimitivesEnable; private EditorPrimitives _editorPrimitives; /// /// The preview light. Allows to modify rendering settings. /// public DirectionalLight PreviewLight; /// /// The env probe. Allows to modify rendering settings. /// public EnvironmentProbe EnvProbe; /// /// The sky. Allows to modify rendering settings. /// public Sky Sky; /// /// The sky light. Allows to modify rendering settings. /// public SkyLight SkyLight; /// /// Gets the post fx volume. Allows to modify rendering settings. /// public PostFxVolume PostFxVolume; /// /// Gets or sets a value indicating whether show default scene actors (sky, env probe, skylight, directional light, etc.). /// public bool ShowDefaultSceneActors { get => PreviewLight.IsActive; set { if (ShowDefaultSceneActors != value) { PreviewLight.IsActive = value; EnvProbe.IsActive = value; Sky.IsActive = value; SkyLight.IsActive = value; if (_showDefaultSceneButton != null) _showDefaultSceneButton.Checked = value; } } } /// /// Gets or sets a value indicating whether draw shapes. /// public bool ShowDebugDraw { get => _debugDrawEnable; set { if (_debugDrawEnable == value) return; _debugDrawEnable = value; if (_debugDrawContext == IntPtr.Zero) { _debugDrawContext = DebugDraw.AllocateContext(); var view = Task.View; view.Flags |= ViewFlags.DebugDraw; Task.View = view; } if (value) { // Need to show editor primitives to show debug shapes ShowEditorPrimitives = true; } } } /// /// Gets or sets a value indicating whether draw shapes and other editor primitives such as gizmos. /// public bool ShowEditorPrimitives { get => _editorPrimitivesEnable; set { if (_editorPrimitivesEnable == value) return; _editorPrimitivesEnable = value; if (_editorPrimitives == null) { _editorPrimitives = Object.New(); _editorPrimitives.Viewport = this; Task.AddCustomPostFx(_editorPrimitives); Task.PostRender += OnPostRender; var view = Task.View; view.Flags |= ViewFlags.CustomPostProcess; Task.View = view; } } } /// /// Gets the editor primitives renderer. Valid only if is true. /// public EditorPrimitives EditorPrimitives => _editorPrimitives; /// /// Custom debug drawing event (via ). /// public event CustomDebugDrawDelegate CustomDebugDraw; /// /// Debug shapes drawing delegate. /// /// The GPU context. /// The render context. public delegate void CustomDebugDrawDelegate(GPUContext context, ref RenderContext renderContext); /// /// Initializes a new instance of the class. /// /// If set to true use widgets for viewport, otherwise hide them. /// The initial orbit radius. protected AssetPreview(bool useWidgets, float orbitRadius = 50.0f) : this(useWidgets, new ArcBallCamera(Vector3.Zero, orbitRadius)) { } /// /// Initializes a new instance of the class. /// /// If set to true use widgets for viewport, otherwise hide them. /// The camera controller. protected AssetPreview(bool useWidgets, ViewportCamera camera) : base(Object.New(), camera, useWidgets) { Task.ViewFlags = ViewFlags.DefaultAssetPreview; Task.AllowGlobalCustomPostFx = false; Real orbitRadius = 200.0f; if (camera is ArcBallCamera arcBallCamera) orbitRadius = arcBallCamera.OrbitRadius; camera.SetArcBallView(new Quaternion(-0.08f, -0.92f, 0.31f, -0.23f), Vector3.Zero, orbitRadius); if (useWidgets) { // Show Default Scene _showDefaultSceneButton = ViewWidgetShowMenu.AddButton("Default Scene", () => ShowDefaultSceneActors = !ShowDefaultSceneActors); _showDefaultSceneButton.Checked = true; _showDefaultSceneButton.CloseMenuOnClick = false; } // Setup preview scene PreviewLight = new DirectionalLight { Brightness = 8.0f, ShadowsMode = ShadowsCastingMode.None, Orientation = Quaternion.Euler(new Vector3(52.1477f, -109.109f, -111.739f)) }; // EnvProbe = new EnvironmentProbe { CustomProbe = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.DefaultSkyCubeTexture) }; // Sky = new Sky { SunLight = PreviewLight, SunPower = 9.0f }; // SkyLight = new SkyLight { Mode = SkyLight.Modes.CustomTexture, Brightness = 2.1f, CustomTexture = EnvProbe.CustomProbe }; // PostFxVolume = new PostFxVolume { IsBounded = false }; // Link actors for rendering Task.ActorsSource = ActorsSources.CustomActors; Task.AddCustomActor(PreviewLight); Task.AddCustomActor(EnvProbe); Task.AddCustomActor(Sky); Task.AddCustomActor(SkyLight); Task.AddCustomActor(PostFxVolume); } private void OnPostRender(GPUContext context, ref RenderContext renderContext) { if (renderContext.View.Mode != ViewMode.Default && _editorPrimitives && _editorPrimitives.CanRender()) { // Render editor primitives, gizmo and debug shapes in debug view modes // Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers _editorPrimitives.Render(context, ref renderContext, renderContext.Task.Output, renderContext.Task.Output); } } /// /// Called when drawing debug shapes with for this viewport. /// /// The GPU context. /// The render context. protected virtual void OnDebugDraw(GPUContext context, ref RenderContext renderContext) { } /// public override bool HasLoadedAssets => base.HasLoadedAssets && Sky.HasContentLoaded && EnvProbe.HasContentLoaded && PostFxVolume.HasContentLoaded; /// public override void OnDestroy() { Object.Destroy(ref PreviewLight); Object.Destroy(ref EnvProbe); Object.Destroy(ref Sky); Object.Destroy(ref SkyLight); Object.Destroy(ref PostFxVolume); Object.Destroy(ref _editorPrimitives); if (_debugDrawContext != IntPtr.Zero) { DebugDraw.FreeContext(_debugDrawContext); _debugDrawContext = IntPtr.Zero; } base.OnDestroy(); } /// public virtual void DrawEditorPrimitives(GPUContext context, ref RenderContext renderContext, GPUTexture target, GPUTexture targetDepth) { // Draw selected objects debug shapes and visuals if (ShowDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw) { DebugDraw.SetContext(_debugDrawContext); DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1)); CustomDebugDraw?.Invoke(context, ref renderContext); OnDebugDraw(context, ref renderContext); DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true); DebugDraw.SetContext(IntPtr.Zero); } } } }