using Editor.MapDoc; using NativeHammer; namespace Editor.MapEditor; // [CanDrop( "vmat" )] // We can do this and stomp the native stuff once face selection works [CanDrop( "material" )] [Expose] class MaterialDropTarget : IMapViewDropTarget { MapMesh MapMesh { get; set; } Material Material { get; set; } CSavedObjects SavedObjects { get; set; } MapDocument doc; MapNode PlacedNode { get; set; } public void DragEnter( Package package, MapView view ) { doc = view.MapDoc; // Start fetching the material, what we could maybe do is make a fake material from the thumbnail _ = GetMaterialFromPackage( package ); SavedObjects = CSavedObjects.Create(); } /// /// Calculate the angles required to align the entity to the plane perpendicular to the given normal /// Now rotate the angle 90 degrees so that it matches the orientation of the texture as it is authored /// private static Angles DeriveOverlayAnglesFromNormal( Vector3 hitNormal ) { return (Rotation.From( hitNormal.EulerAngles - Vector3.Up.EulerAngles ) * Rotation.FromAxis( Vector3.Up, 90.0f )).Angles(); } public void DragMove( MapView view ) { // Pointless for us to do any "iterative" work on a pending material if ( Material == null ) return; if ( PlacedNode.IsValid() ) { view.BuildRay( out Vector3 rayStart, out Vector3 rayEnd ); var tr = Trace.Ray( rayStart, rayEnd ).SkipToolsMaterials().MeshesOnly().Run( view.MapDoc.World ); PlacedNode.Position = tr.HitPosition; PlacedNode.Angles = DeriveOverlayAnglesFromNormal( tr.Normal ); } else { view.BuildRay( out Vector3 rayStart, out Vector3 rayEnd ); var tr = Trace.Ray( rayStart, rayEnd ).Run( view.MapDoc.World ); RevertIterativeDragWork(); if ( tr.MapNode is MapMesh mesh ) { MapMesh = mesh; SavedObjects.SaveObject( mesh ); mesh.SetMaterial( Material ); } else { MapMesh = null; } } } public void DragLeave( MapView view ) { doc = null; if ( PlacedNode.IsValid() ) { view.MapDoc.DeleteNode( PlacedNode ); } RevertIterativeDragWork(); } public void DragDropped( MapView view ) { if ( PlacedNode.IsValid() ) { History.MarkUndoPosition( $"Drop {PlacedNode}" ); History.KeepNew( PlacedNode ); return; } RevertIterativeDragWork(); if ( MapMesh == null || Material == null ) return; History.MarkUndoPosition( "Drop material" ); History.Keep( MapMesh ); // This doesn't work in faces mode, which sucks - probably needs glue MapMesh.SetMaterial( Material ); SavedObjects.DeleteThis(); } private void RevertIterativeDragWork() { if ( !SavedObjects.IsValid ) return; SavedObjects.RestoreObjects(); SavedObjects.RemoveAll(); } async Task GetMaterialFromPackage( Package package ) { // Install our package ( Do we need any sort of loading indicator, it's gonna be fast surely ) var asset = await AssetSystem.InstallAsync( package ); Material = Material.Load( asset.Path ); CreateNode(); } public void CreateNode() { if ( !doc.IsValid() ) return; // This fucking stupid shit is defined from shaders wtf if ( Material.Attributes.GetInt( "decal", 0 ) == 1 ) { var overlayEnt = new MapEntity( doc ); overlayEnt.ClassName = "info_overlay"; overlayEnt.SetKeyValue( "material", Material.ResourcePath ); PlacedNode = overlayEnt; } else if ( Material.Attributes.GetInt( "overlay", 0 ) == 1 ) { var width = NativeHammer.Global.MaterialGetMappingWidth( Material.native ); var height = NativeHammer.Global.MaterialGetMappingHeight( Material.native ); var staticOverlay = new MapStaticOverlay( doc ); staticOverlay.CreateCenteredQuad( new Vector2( width, height ), Material ); PlacedNode = staticOverlay; } else if ( Material.Attributes.GetInt( "sky", 0 ) == 1 ) { var skyEnt = new MapEntity( doc ); skyEnt.ClassName = "env_sky"; skyEnt.SetKeyValue( "skyname", Material.ResourcePath ); PlacedNode = skyEnt; } } }