using System.Linq; using System.Runtime.CompilerServices; using Editor; using Facepunch.ActionGraphs.Nodes; namespace Sandbox.ActionGraphs; #nullable enable public static class GameObjectThumbnail { public const int Size = 128; private static ConditionalWeakTable _cache = new(); /// /// If this game object is a prefab root, return the prefab asset. /// private static Asset? GetPrefabAsset( GameObject go ) { if ( go.Scene is not PrefabScene { Source: { } source } || go.Scene != go ) { return null; } return AssetSystem.FindByPath( source.ResourcePath ); } public static Pixmap GetGameObjectThumb( this GameObject go ) { if ( GetPrefabAsset( go ) is { } prefabAsset ) { return prefabAsset.GetAssetThumb(); } if ( _cache.TryGetValue( go, out var cached ) ) { return cached; } using var sceneScope = go.Scene.Push(); using var camera = new SceneCamera( "Thumbnail Cam" ); var bounds = go.GetBounds(); var center = bounds.Center; var distance = Math.Max( 16f, (bounds.Maxs - bounds.Mins).Length * 0.5f ) * 4f; var baseYaw = go.WorldRotation.Yaw(); var cameraRotation = Rotation.From( 30f, baseYaw + 60f, 0f ); var sunRotation = Rotation.From( 80f, baseYaw + 30f, 0f ); var lightRotation = Rotation.From( 60f, baseYaw + 120f, 0f ); var thumb = new Pixmap( Size, Size ); SceneDirectionalLight? sun = null; SceneLight? light = null; if ( !go.Scene.GetAllComponents().Any() ) { sun = new SceneDirectionalLight( go.Scene.SceneWorld, sunRotation, Color.White * 2.5f + Color.Cyan * 0.05f ) { ShadowsEnabled = true, ShadowTextureResolution = 1024 }; light = new SceneLight( go.Scene.SceneWorld, center - lightRotation.Forward * distance, distance * 1.5f, new Color( 1.0f, 0.9f, 0.9f ) * 10.0f ); } try { camera.World = go.Scene.SceneWorld; camera.Position = center - cameraRotation.Forward * distance; camera.Rotation = cameraRotation; camera.FieldOfView = 30f; camera.BackgroundColor = Color.Transparent; camera.RenderToPixmap( thumb ); _cache.TryAdd( go, thumb ); return thumb; } finally { sun?.Delete(); light?.Delete(); } } [Event( "scene.saved" )] static void OnSceneSaved( Scene scene ) { foreach ( var (key, value) in _cache.ToArray() ) { if ( key.Scene == scene ) { _cache.Remove( key ); } } } }