using System.Text.Json.Serialization;
namespace Editor;
public partial class SceneViewportWidget
{
private class SceneCookie
{
public Vector3 CameraPosition { get; set; }
public Rotation CameraRotation { get; set; }
public float? CameraOrthoHeight { get; set; }
}
public enum ViewMode
{
[Title( "3D" ), Icon( "view_in_ar" )]
Perspective,
[Title( "Top 2D" ), Icon( "roofing" )]
Top2d,
[Title( "Front 2D" ), Icon( "cottage" )]
Front2d,
[Title( "Side 2D" ), Icon( "gite" )]
Side2d
}
public class ViewportState
{
public Vector3 CameraPosition { get; set; }
public Rotation CameraRotation { get; set; }
///
/// View mode of this viewport
///
public ViewMode View
{
get => _mode;
set => SetViewmode( value );
}
private ViewMode _mode;
[JsonIgnore] public bool Is2D => View != ViewMode.Perspective;
///
/// Render mode to use for this viewport
///
public SceneCameraDebugMode RenderMode { get; set; } = SceneCameraDebugMode.Normal;
///
/// Should the scene render in wireframe mode
///
public bool WireframeMode { get; set; } = false;
///
/// Should we render post processing effects?
///
public bool EnablePostProcessing { get; set; } = true;
///
/// Enable the default directional light when editing prefabs.
///
public bool EnablePrefabLighting { get; set; } = true;
///
/// Should the skybox be visible in 2D mode
///
public bool ShowSkyIn2D
{
get => !Is2D || _showSky;
set => _showSky = value;
}
private bool _showSky;
///
/// Show the grid
///
public bool ShowGrid { get; set; } = true;
///
/// Should we fade the grid
///
[Range( 0.0f, 1.0f )]
public float GridOpacity { get; set; } = 0.2f;
///
/// The plane the grid is shown on
///
public Gizmo.GridAxis GridAxis { get; set; } = Gizmo.GridAxis.XY;
///
/// The orthographic size for the camera
///
[Title( "Orthographic Height" )]
public float CameraOrthoHeight { get; set; } = 1000.0f;
private void SetViewmode( ViewMode viewmode )
{
_mode = viewmode;
switch ( viewmode )
{
case ViewMode.Top2d:
CameraRotation = Rotation.LookAt( Vector3.Down, Vector3.Left );
GridAxis = Gizmo.GridAxis.XY;
break;
case ViewMode.Front2d:
CameraRotation = Rotation.LookAt( Vector3.Forward, Vector3.Up );
GridAxis = Gizmo.GridAxis.YZ;
break;
case ViewMode.Side2d:
CameraRotation = Rotation.LookAt( Vector3.Left, Vector3.Up );
GridAxis = Gizmo.GridAxis.ZX;
break;
default:
GridAxis = Gizmo.GridAxis.XY;
break;
}
}
}
public ViewportState State { get; init; }
private void InitializeCamera()
{
Session.MakeActive();
using var scope = SceneEditorSession.Scope();
Scene scene = Session.Scene;
if ( !scene.IsValid() )
return;
// 1. load last camera position from cookies if possible
if ( scene.Source is not null &&
ProjectCookie.Get( $"{scene.Source.ResourcePath}.Viewport{Id}", null ) is SceneCookie cookie )
{
State.CameraPosition = cookie.CameraPosition;
if ( cookie.CameraOrthoHeight.HasValue )
State.CameraOrthoHeight = cookie.CameraOrthoHeight.Value;
if ( !State.Is2D )
State.CameraRotation = cookie.CameraRotation;
return;
}
//
// 2. Place camera where a Camera component is
//
var cc = scene.Camera;
if ( cc.IsValid() )
{
State.CameraPosition = cc.WorldPosition;
if ( !State.Is2D )
State.CameraRotation = cc.WorldRotation;
return;
}
//
// 3. BBox frame the scene
//
if ( !State.Is2D )
State.CameraRotation = Rotation.From( 45, 45, 0 );
var fieldOfView = 80.0f;
var bounds = scene.GetBounds();
var distance = MathX.SphereCameraDistance( bounds.Size.Length * 0.5f, fieldOfView ) * 1.0f;
State.CameraPosition = bounds.Center + distance * State.CameraRotation.Backward;
}
[Event( "scene.session.save" )]
public void SaveState()
{
if ( ProjectCookie is null ) return;
Scene scene = Session.Scene;
if ( !scene.IsValid() )
return;
if ( scene.Source is not null )
{
// TODO: this should still store something for non-resource scenes?
ProjectCookie.Set( $"{scene.Source.ResourcePath}.Viewport{Id}", new SceneCookie()
{
CameraPosition = State.CameraPosition,
CameraRotation = State.CameraRotation,
CameraOrthoHeight = State.CameraOrthoHeight,
} );
}
ProjectCookie.Set( $"SceneView.Viewport{Id}.Settings", State );
}
[Shortcut( "scene.cycle-viewmode", "CTRL+SPACE" )]
public void CycleViewmode()
{
ViewMode newMode = (ViewMode)(((int)State.View + 1) % ((int)ViewMode.Side2d + 1));
State.View = newMode == ViewMode.Perspective ? ViewMode.Top2d : newMode; // skip 3d
Vector3 center = Vector3.Zero;
int count = 0;
// TODO: support other (mesh tool?) selections here
if ( SceneEditorSession.Active is not null )
{
foreach ( var selected in SceneEditorSession.Active.Selection.OfType() )
{
center += selected.WorldPosition;
count++;
}
}
if ( count > 0 )
{
State.CameraPosition = (center / count) - State.CameraRotation.Forward * 400;
}
}
}