using Sandbox.Internal; using System; using System.Collections.Immutable; namespace Sandbox; public static partial class SandboxToolExtensions { /// /// Render this camera to the target widget. Once you do this the target widget becomes "externally painted", so you /// won't be able to paint on it anymore with Qt's Paint stuff. /// public static bool RenderToPixmap( this SceneCamera camera, Pixmap targetPixmap, bool async = false ) { if ( camera.World == null ) return false; if ( targetPixmap == null || targetPixmap.Width <= 1 || targetPixmap.Height <= 1 ) return false; camera.OnPreRender( targetPixmap.Size ); using Bitmap bitmap = new Bitmap( targetPixmap.Width, targetPixmap.Height ); camera.RenderToBitmap( bitmap ); targetPixmap.UpdateFromPixels( bitmap ); return true; } /// /// Render this camera to the target widget. Once you do this the target widget becomes "externally painted", so you /// won't be able to paint on it anymore with Qt's Paint stuff. /// public static bool RenderToPixmap( this CameraComponent camera, Pixmap targetPixmap, bool async = false ) { if ( targetPixmap == null || targetPixmap.Width <= 1 || targetPixmap.Height <= 1 ) return false; if ( !camera.IsValid() ) return false; using ( camera.Scene.Push() ) { camera.Scene.PreCameraRender(); camera.InitializeRendering(); camera.SceneCamera.OnPreRender( targetPixmap.Size ); using Bitmap bitmap = new Bitmap( targetPixmap.Width, targetPixmap.Height ); camera.RenderToBitmap( bitmap ); targetPixmap.UpdateFromPixels( bitmap ); return true; } } /// /// Render this camera to the target widget. Once you do this the target widget becomes "externally painted", so you /// won't be able to paint on it anymore with Qt's Paint stuff. /// public static bool RenderToPixmap( this Scene scene, Pixmap targetPixmap, bool async = false ) { return RenderToPixmap( scene.Camera, targetPixmap, async ); } /// /// Render this camera to the target widget. Once you do this the target widget becomes "externally painted", so you /// won't be able to paint on it anymore with Qt's Paint stuff. /// public static unsafe bool RenderToVideo( this SceneCamera camera, VideoWriter videoWriter, TimeSpan? time = default ) { if ( camera.World == null ) return false; if ( videoWriter == null || videoWriter.Width <= 1 || videoWriter.Height <= 1 ) return false; camera.OnPreRender( new Vector2( videoWriter.Width, videoWriter.Height ) ); using Bitmap bitmap = new Bitmap( videoWriter.Width, videoWriter.Height ); camera.RenderToBitmap( bitmap ); videoWriter.AddFrame( bitmap, time ); return true; } /// /// Render this camera to the target widget. Once you do this the target widget becomes "externally painted", so you /// won't be able to paint on it anymore with Qt's Paint stuff. /// public static async Task RenderToVideoAsync( this SceneCamera camera, VideoWriter videoWriter, TimeSpan? time = default ) { if ( camera.World == null ) return false; if ( videoWriter == null || videoWriter.Width <= 1 || videoWriter.Height <= 1 ) return false; camera.OnPreRender( new Vector2( videoWriter.Width, videoWriter.Height ) ); using Bitmap bitmap = new Bitmap( videoWriter.Width, videoWriter.Height ); camera.RenderToBitmap( bitmap ); return await Task.Run( () => videoWriter.AddFrame( bitmap, time ) ); } /// /// Shortcut for EditorTypeLibrary.GetSerializedObject( x ) /// public static SerializedObject GetSerialized( this object self ) { try { return EditorTypeLibrary.GetSerializedObject( self ); } catch { return new ReflectionSerializedObject( self ); } } /// /// Describes the path to a from either a /// or . /// public sealed class PropertyPath { private string _fullName; /// /// Full path to reach the original property, starting from a property on a or /// . /// public IReadOnlyList Properties { get; } /// /// Names of each property in , separated by '.'s. /// public string FullName => _fullName ??= string.Join( ".", Properties.Select( x => x.Name ) ); /// /// (s) or (s) that contain the original property. /// public IEnumerable Targets => Properties[0].Parent.Targets; internal PropertyPath( IEnumerable properties ) { Properties = properties.ToImmutableList(); } /// /// Returns . /// public override string ToString() => FullName; } /// /// Tries to find the path from a or to this property. /// Returns if not found. /// public static PropertyPath FindPathInScene( this SerializedProperty prop ) { var path = new List(); while ( prop?.Parent is not null ) { path.Add( prop ); if ( prop.Parent.Targets.Any( x => x is Component or GameObject ) ) { path.Reverse(); return new PropertyPath( path ); } prop = prop.Parent?.ParentProperty; } return null; } /// /// Tries to find the that contains the given property. /// Returns if not found. /// public static GameObject GetContainingGameObject( this SerializedProperty prop ) { if ( prop.FindPathInScene() is not { } path ) { return null; } if ( path.Targets.OfType().FirstOrDefault() is { } go ) { return go; } if ( path.Targets.OfType().FirstOrDefault() is { } component ) { return component.GameObject; } return null; } /// /// Create a feasible title from the current selection /// public static string ConstructTitle( this SelectionSystem sys ) { if ( sys.Count == 0 ) return "Nothing"; if ( sys.Count > 1 ) return $"{sys.Count} Objects"; if ( sys.First() is GameObject go ) return go.Name; return sys.First().ToString(); } }