using System.Threading;
namespace Editor;
public abstract class BaseDropObject
{
CancellationTokenSource DragCancelSource = new CancellationTokenSource();
public bool Dropped { get; set; }
public bool Deleted { get; set; }
public string PackageStatus { get; set; }
public Vector3 PivotPosition { get; set; }
public BBox Bounds { get; set; } = BBox.FromPositionAndSize( 0, 16 );
public Rotation Rotation { get; set; } = Rotation.Identity;
public Vector3 Scale { get; set; } = Vector3.One;
public GameObject GameObject { get; set; }
protected SceneTraceResult trace;
protected Transform traceTransform;
public bool IsInitialized { get; private set; }
///
/// Download/load asset
///
protected abstract Task Initialize( string dragData, CancellationToken token );
///
/// Position and update the preview. If Dropped is true, finalize action and update
///
public virtual void OnUpdate() { }
///
/// Position and update the preview. If Dropped is true, finalize action and update
///
public virtual Task OnDrop() => Task.CompletedTask;
///
/// Clean up after yourself
///
public virtual void OnDestroy() { }
public async Task StartInitialize( string dragData )
{
try
{
await Initialize( dragData, DragCancelSource.Token );
IsInitialized = true;
}
catch ( System.Exception e )
{
Log.Warning( $"Error when dropping {this} - {e.Message}" );
Delete();
}
}
public void Tick()
{
try
{
OnUpdate();
if ( Deleted ) return;
if ( !IsInitialized ) return;
if ( Dropped )
{
_ = OnDrop();
Delete();
}
}
catch ( System.Exception e )
{
Log.Warning( $"Error when updating {this} - {e.Message}" );
Delete();
}
}
public virtual void UpdateDrag( SceneTraceResult tr, Gizmo.SceneSettings settings )
{
trace = tr;
traceTransform = TraceTransform( settings );
}
public void Delete()
{
DragCancelSource.Cancel();
Deleted = true;
OnDestroy();
}
protected async Task WaitForLoad()
{
if ( IsInitialized ) return;
var token = DragCancelSource.Token;
while ( !IsInitialized )
{
token.ThrowIfCancellationRequested();
await Task.Yield();
}
}
///
/// If the asset is a url, we'll download it. If it's a path, we'll try to return it.
///
protected async Task InstallAsset( string urlPath, CancellationToken token )
{
if ( !Uri.TryCreate( urlPath, UriKind.Absolute, out var uri ) || uri.IsFile || uri.Scheme != "https" )
{
return AssetSystem.FindByPath( urlPath );
}
PackageStatus = "Fetching Package";
var package = await Package.Fetch( uri.ToString(), false );
if ( package is not null )
{
var b = Bounds;
b.Mins = package.GetMeta( "RenderMins" );
b.Maxs = package.GetMeta( "RenderMaxs" );
Bounds = b;
var boffset = b.ClosestPoint( Vector3.Down * 10000 );
PivotPosition = boffset;
}
PackageStatus = "Downloading - 0%";
var a = await AssetSystem.InstallAsync( uri.ToString(), true, f => PackageStatus = $"Downloading - {f * 100.0f:n0}%", token );
PackageStatus = null;
return a;
}
private Transform TraceTransform( Gizmo.SceneSettings settings )
{
var rot = Rotation.LookAt( trace.Normal, Vector3.Up ) * Rotation.From( 90, 0, 0 ) * Rotation;
var pos = trace.EndPosition;
if ( EditorPreferences.BoundsPlacement )
{
pos += trace.Normal * PivotPosition.Length;
}
var isCtrlPressed = Application.KeyboardModifiers.HasFlag( KeyboardModifiers.Ctrl );
var snap = settings.SnapToGrid;
if ( snap && isCtrlPressed ) snap = false;
else if ( !snap && isCtrlPressed ) snap = true;
if ( snap )
{
var spacing = settings.GridSpacing;
var localPos = rot.Inverse * pos;
localPos = localPos.SnapToGrid( spacing, true, true, false );
pos = rot * localPos;
}
return new Transform( pos, rot, Scale );
}
public static async Task CreateDropFor( string text )
{
if ( string.IsNullOrEmpty( text ) ) return null;
string type = "unknown";
if ( Uri.TryCreate( text, UriKind.Absolute, out var url ) && !url.IsFile && url.Scheme == "https" )
{
var package = await Package.FetchAsync( text, false );
if ( package is not null )
{
type = package.TypeName;
}
}
var dropObjs = EditorTypeLibrary.GetTypesWithAttribute();
foreach ( var obj in dropObjs )
{
var attribute = obj.Attribute;
if ( (!string.IsNullOrEmpty( attribute.Type ) && attribute.Type == type) || attribute.Extensions.Any( text.EndsWith ) )
{
return EditorTypeLibrary.Create( obj.Type.TargetType );
}
}
return null;
}
}