// Supporting Obsolete paths #pragma warning disable CS0612 #pragma warning disable CS0618 namespace Sandbox.UI { /// /// Allows to render a scene world onto a panel. /// [Expose] [Library( "scene" )] public partial class ScenePanel : Panel { /// /// Shortcut to Camera.World /// [Obsolete( "Handling SceneObjects like this manually will be removed soon. Use the actual Scene." )] public SceneWorld World { get => Camera.World; set => Camera.World = value; } /// /// The camera we're going to be using to render /// [Obsolete( "Handling SceneObjects like this manually will be removed soon. Use the actual Scene." )] public SceneCamera Camera { get; } = new( "Scene Panel" ); /// /// If enabled, the scene will only render once. That isn't totally accurate though, because we'll /// also re-render the scene when the size of the panel changes. /// public bool RenderOnce { get; set; } /// /// The texture that the panel is rendering to internally. This will change to a different /// texture if the panel changes size, so I wouldn't hold onto this object. /// public Texture RenderTexture { get; private set; } /// /// The Scene this panel renders. /// public Scene RenderScene { get; set; } public ScenePanel() { RenderScene = new() { WantsSystemScene = false }; Camera.FieldOfView = 60; Camera.BackgroundColor = Color.Transparent; Camera.ZNear = 0.1f; Camera.ZFar = 1000.0f; } /// /// Creates and loads a Scene from a file to render to this panel. /// public ScenePanel( string sceneFilename ) : this() { var options = new SceneLoadOptions { ShowLoadingScreen = false }; if ( !options.SetScene( sceneFilename ) ) return; RenderScene.Load( options ); } public override void Tick() { base.Tick(); if ( !RenderScene.IsValid() ) return; using ( RenderScene.Push() ) { RenderScene.GameTick( RealTime.Delta ); } } internal bool shouldRenderNextFrame = true; /// /// Render the panel again next frame. This is meant to be used with RenderOnce, where /// you might want to render on demand or only once. /// public void RenderNextFrame() => shouldRenderNextFrame = true; public override bool HasContent => RenderScene.IsValid() || World != null; public override void Delete( bool immediate = false ) { RenderTexture?.Dispose(); RenderTexture = null; RenderScene?.Destroy(); RenderScene = null; base.Delete( immediate ); } internal override void DrawContent( PanelRenderer renderer, ref RenderState state ) { if ( Box.RectInner.Size.x <= 0 ) return; if ( Box.RectInner.Size.y <= 0 ) return; // work out whether we should actually render the scene bool shouldRender = shouldRenderNextFrame; if ( !RenderOnce ) shouldRender = true; if ( RenderTexture == null ) shouldRender = true; var oldRt = RenderTexture; RenderTexture = Texture.CreateRenderTarget( "__scenePanel", ImageFormat.RGBA8888, Box.RectInner.Size, RenderTexture ); if ( RenderTexture == null ) return; // Texture changed - force an update if ( oldRt != RenderTexture ) shouldRender = true; if ( shouldRender ) { // reset shouldRenderNextFrame = false; if ( Camera.World.IsValid() ) { Camera.RenderToTexture( RenderTexture, null, default ); } else if ( RenderScene.IsValid() && RenderScene.Camera.IsValid() ) { RenderScene.PreCameraRender(); RenderScene.Camera.RenderToTexture( RenderTexture ); } } renderer.DrawBackgroundTexture( this, RenderTexture, state, Length.Contain ); } public override void SetProperty( string name, string value ) { base.SetProperty( name, value ); } } namespace Construct { public static class SceneConstructor { [Obsolete( "Will be deleted if anyone for some reason still uses this" )] public static ScenePanel ScenePanel( this PanelCreator self, SceneWorld world, Vector3 position, Rotation rotation, float fieldOfView, string classname = null ) { var control = self.panel.AddChild(); control.World = world; control.Camera.Position = position; control.Camera.Rotation = rotation; control.Camera.FieldOfView = fieldOfView; if ( classname != null ) control.AddClass( classname ); return control; } } } }