XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Numerics;
using Microsoft.UI.Composition;
using Windows.UI;

namespace XFEEDToolBox.Controls;

/// <summary>Retained sky at infinity. It rotates with the camera, never follows
/// map translation/zoom, and has no hit targets or continuously running animation.</summary>
internal sealed class GalaxySkybox
{
    private readonly ContainerVisual root;
    private readonly SpriteVisual background;
    private readonly ShapeVisual dots,clouds;
    private readonly (Vector3 Direction,CompositionSpriteShape Shape)[] stars;
    private readonly (Vector3 Direction,CompositionEllipseGeometry Geometry,CompositionSpriteShape Shape)[] nebulae;
    private double lastYaw=double.NaN,lastPitch,lastWidth,lastHeight;
    internal Visual Visual=>root;
    internal int Nodes=>stars.Length+nebulae.Length+3;
    internal long Rebuilds { get; private set; }
    internal GalaxySkybox(Compositor compositor)
    {
        root=compositor.CreateContainerVisual();background=compositor.CreateSpriteVisual();
        var night=compositor.CreateLinearGradientBrush();night.StartPoint=new(0,0);night.EndPoint=new(1,1);
        night.ColorStops.Add(compositor.CreateColorGradientStop(0,Color.FromArgb(255,3,7,17)));
        night.ColorStops.Add(compositor.CreateColorGradientStop(.55f,Color.FromArgb(255,8,15,29)));
        night.ColorStops.Add(compositor.CreateColorGradientStop(1,Color.FromArgb(255,4,9,20)));background.Brush=night;
        clouds=compositor.CreateShapeVisual();dots=compositor.CreateShapeVisual();
        root.Children.InsertAtBottom(background);root.Children.InsertAtTop(clouds);root.Children.InsertAtTop(dots);
        stars=new (Vector3,CompositionSpriteShape)[900];
        var brushes=new[]{compositor.CreateColorBrush(Color.FromArgb(150,138,171,207)),compositor.CreateColorBrush(Color.FromArgb(180,216,223,243)),compositor.CreateColorBrush(Color.FromArgb(125,242,203,171))};
        for(var i=0;i<stars.Length;i++)
        {
            var y=1-2*(i+.5)/stars.Length;var r=Math.Sqrt(1-y*y);var angle=i*2.399963229728653;
            var geometry=compositor.CreateEllipseGeometry();var radius=i%31==0?1.35f:i%7==0?.85f:.48f;geometry.Radius=new(radius,radius);
            var shape=compositor.CreateSpriteShape(geometry);shape.FillBrush=brushes[i%brushes.Length];shape.Scale=Vector2.Zero;dots.Shapes.Add(shape);
            stars[i]=(new((float)(r*Math.Cos(angle)),(float)y,(float)(r*Math.Sin(angle))),shape);
        }
        nebulae=new (Vector3,CompositionEllipseGeometry,CompositionSpriteShape)[10];
        for(var i=0;i<nebulae.Length;i++)
        {
            var angle=i*Math.Tau/nebulae.Length;
            var geometry=compositor.CreateEllipseGeometry();var shape=compositor.CreateSpriteShape(geometry);
            shape.FillBrush=Radial(compositor,i%2==0?Color.FromArgb(52,83,110,160):Color.FromArgb(40,106,72,141));shape.Scale=Vector2.Zero;clouds.Shapes.Add(shape);
            nebulae[i]=(Vector3.Normalize(new((float)Math.Cos(angle),(float)(.15*Math.Sin(angle*2)),(float)Math.Sin(angle))),geometry,shape);
        }
    }
    internal static CompositionRadialGradientBrush Radial(Compositor compositor,Color colour)
    {
        var brush=compositor.CreateRadialGradientBrush();brush.CenterPoint=new(.5f,.5f);brush.EllipseCenter=new(.5f,.5f);brush.EllipseRadius=new(.5f,.5f);
        brush.ColorStops.Add(compositor.CreateColorGradientStop(0,colour));
        brush.ColorStops.Add(compositor.CreateColorGradientStop(.22f,Color.FromArgb((byte)(colour.A*.55),colour.R,colour.G,colour.B)));
        brush.ColorStops.Add(compositor.CreateColorGradientStop(1,Color.FromArgb(0,colour.R,colour.G,colour.B)));
        return brush;
    }
    internal void Draw(double width,double height,double yaw,double pitch)
    {
        if(width==lastWidth&&height==lastHeight&&yaw==lastYaw&&pitch==lastPitch)return;
        lastWidth=width;lastHeight=height;lastYaw=yaw;lastPitch=pitch;Rebuilds++;
        root.Size=background.Size=dots.Size=clouds.Size=new((float)width,(float)height);
        var cy=Math.Cos(yaw);var sy=Math.Sin(yaw);var cp=Math.Cos(pitch);var sp=Math.Sin(pitch);var focal=Math.Max(width,height)*.7;
        foreach(var (direction,shape) in stars)
        {
            var (x,y,z)=View(direction);
            if(z<.08){shape.Scale=Vector2.Zero;continue;}
            var px=width/2+x/z*focal;var py=height/2-y/z*focal;
            if(px<0||py<0||px>width||py>height){shape.Scale=Vector2.Zero;continue;}
            shape.Offset=new((float)px,(float)py);shape.Scale=Vector2.One;
        }
        foreach(var (direction,geometry,shape) in nebulae)
        {
            var (x,y,z)=View(direction);
            if(z<.2){shape.Scale=Vector2.Zero;continue;}
            var extent=Math.Min(1.2,.6/z);geometry.Radius=new((float)(focal*extent),(float)(focal*extent*.22));
            shape.Offset=new((float)(width/2+x/z*focal),(float)(height/2-y/z*focal));
            var tangent=new Vector3(-direction.Z,0,direction.X);var (tx,ty,tz)=View(tangent);
            shape.RotationAngleInDegrees=(float)(Math.Atan2(-(ty*z-y*tz),tx*z-x*tz)*180/Math.PI);shape.Scale=Vector2.One;
        }
        (double X,double Y,double Z) View(Vector3 p)
        {var depth=-p.X*sy+p.Z*cy;return(p.X*cy+p.Z*sy,p.Y*cp-depth*sp,p.Y*sp+depth*cp);}
    }
}