Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / map / image / ImageCanvas_Static.cs


using System;
using System.Collections.Generic;
using Godot;

public partial class ImageCanvas
{
    
    /// <summary>
    /// 同一帧下将队列里的image绘制到指定画布下最大消耗时间, 如果绘制的时间超过了这个值, 则队列中后面的image将会放到下一帧绘制
    /// </summary>
    public static float MaxHandlerTime { get; set; } = 4f;
    
    /// <summary>
    /// 渲染窗口
    /// </summary>
    public static SubViewport RenderViewport { get; private set; }
    
    /// <summary>
    /// 渲染偏移位置
    /// </summary>
    public static Vector2 RenderOffset { get; set; }

    //预渲染队列
    private static readonly Queue<ImageRenderData> _enqueueItems = new Queue<ImageRenderData>();
    //渲染中的队列
    private static readonly Queue<ImageRenderData> _drawingEnqueueItems = new Queue<ImageRenderData>();

    private static readonly Stack<ImageRenderSprite> _renderSpriteStack = new Stack<ImageRenderSprite>();

    public static void Init(SubViewport renderViewport, Vector2 renderOffset)
    {
        RenderViewport = renderViewport;
        RenderOffset = renderOffset;
        RenderingServer.FramePostDraw += OnFramePostDraw;
    }

    private static ImageRenderSprite GetRenderSprite(Vector2 position)
    {
        ImageRenderSprite renderSprite;
        if (_renderSpriteStack.Count > 0)
        {
            renderSprite = _renderSpriteStack.Pop();
        }
        else
        {
            renderSprite = new ImageRenderSprite();
        }

        renderSprite.Sprite.Position = position;
        RenderViewport.AddChild(renderSprite.Sprite);
        return renderSprite;
    }

    private static void ReclaimRenderSprite(ImageRenderSprite renderSprite)
    {
        RenderViewport.RemoveChild(renderSprite.Sprite);
        _renderSpriteStack.Push(renderSprite);
    }

    private static void OnFramePostDraw()
    {
        //上一帧绘制的image
        if (_drawingEnqueueItems.Count > 0)
        {
            var redrawCanvas = new HashSet<ImageCanvas>();
            var viewportTexture = RenderViewport.GetTexture();
            using (var image = viewportTexture.GetImage())
            {
                var num = 0;
                do
                {
                    var item = _drawingEnqueueItems.Dequeue();
                    if (!item.ImageCanvas.IsDestroyed)
                    {
                        redrawCanvas.Add(item.ImageCanvas);
                        //截取Viewport像素点
                        item.ImageCanvas._canvas.BlendRect(image,
                            new Rect2I(0, 0, item.RenderWidth, item.RenderHeight),
                            new Vector2I(item.X - item.RenderOffsetX, item.Y - item.RenderOffsetY)
                        );

                        item.SrcImage.Dispose();
                        item.SrcImage = null;
                        num++;
                    }

                    //回收 RenderSprite
                    if (item.RenderSprite != null)
                    {
                        ReclaimRenderSprite(item.RenderSprite);
                    }
                } while (_drawingEnqueueItems.Count > 0);

                GD.Print($"当前帧绘制完成数量: {num}, 绘制队列数量: {_drawingEnqueueItems.Count}");
            }

            //重绘画布
            foreach (var drawCanvas in redrawCanvas)
            {
                drawCanvas.Redraw();
            }
        }

        //处理下一批image
        if (_enqueueItems.Count > 0)
        {
            var startTime = DateTime.Now;

            var num = 0;
            //执行绘制操作
            //绘制的总时间不能超过MaxHandlerTime, 如果超过了, 那就下一帧再画其它的吧
            do
            {
                var item = _enqueueItems.Dequeue();
                if (!item.ImageCanvas.IsDestroyed)
                {

                    var cosAngle = Mathf.Cos(item.Rotation);
                    var sinAngle = Mathf.Sin(item.Rotation);
                    if (cosAngle == 0)
                    {
                        cosAngle = 1e-6f;
                    }

                    if (sinAngle == 0)
                    {
                        sinAngle = 1e-6f;
                    }

                    var width = item.SrcImage.GetWidth();
                    var height = item.SrcImage.GetHeight();
                    //旋转后的图片宽高
                    item.RenderWidth = Mathf.CeilToInt(width * Mathf.Abs(cosAngle) + height * sinAngle) + 2;
                    item.RenderHeight = Mathf.CeilToInt(width * sinAngle + height * Mathf.Abs(cosAngle)) + 2;

                    item.RenderOffsetX =
                        (int)((item.CenterX / sinAngle +
                                        (0.5f * item.RenderWidth * sinAngle - 0.5f * item.RenderHeight * cosAngle +
                                         0.5f * height) /
                                        cosAngle -
                                        (-0.5f * item.RenderWidth * cosAngle - 0.5f * item.RenderHeight * sinAngle +
                                         0.5f * width) /
                                        sinAngle - item.CenterY / cosAngle) /
                        (cosAngle / sinAngle + sinAngle / cosAngle)) + 1;


                    item.RenderOffsetY =
                        (int)((item.CenterX / cosAngle -
                                  (-0.5f * item.RenderWidth * cosAngle - 0.5f * item.RenderHeight * sinAngle + 0.5f * width) /
                                  cosAngle -
                                  (0.5f * item.RenderWidth * sinAngle - 0.5f * item.RenderHeight * cosAngle + 0.5f * height) /
                                  sinAngle + item.CenterY / sinAngle) /
                              (sinAngle / cosAngle + cosAngle / sinAngle)) + 1;

                    var renderSprite = GetRenderSprite(new Vector2(0 + item.RenderOffsetX, 0 + item.RenderOffsetY));
                    item.RenderSprite = renderSprite;
                    //设置绘制信息
                    renderSprite.Sprite.Offset = new Vector2(item.CenterX, item.CenterY);
                    renderSprite.Sprite.Rotation = item.Rotation;

                    renderSprite.SetImage(item.SrcImage);
                    _drawingEnqueueItems.Enqueue(item);
                    num++;
                }

            } while (_enqueueItems.Count > 0 && (DateTime.Now - startTime).TotalMilliseconds < MaxHandlerTime);

            GD.Print($"当前帧进入绘制绘队列数量: {num}, 待绘制队列数量: {_enqueueItems.Count}, 绘制队列数量: {_drawingEnqueueItems.Count}");
        }
    }
}