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