// 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);
}
}
}
}