From 1253e3017be243a8d9ba989fd7ad9d7daa55329f Mon Sep 17 00:00:00 2001 From: Ian Burgmyer Date: Thu, 12 Sep 2019 15:38:37 -0400 Subject: [PATCH] Unified the drawing routines. * Moved both the Shown and ZOrder properties to the Canvas class. * Sprites and Backgrounds are now both considered when doing z-order checks. As such, sprites can now be drawn under backgrounds. This will likely need more optimization. --- DotSDL/Graphics/Background.cs | 2 +- DotSDL/Graphics/Canvas.cs | 11 ++++ DotSDL/Graphics/SdlWindow.cs | 147 ++++++++++++++++++++++-------------------- 3 files changed, 88 insertions(+), 72 deletions(-) diff --git a/DotSDL/Graphics/Background.cs b/DotSDL/Graphics/Background.cs index b725cb5..f5dda1f 100644 --- a/DotSDL/Graphics/Background.cs +++ b/DotSDL/Graphics/Background.cs @@ -30,7 +30,7 @@ namespace DotSDL.Graphics { // TODO: Might be able to make this faster by leveraging memcpy(). Need to benchmark it. unsafe { var copySize = Width * Height * 4; // Width x Height at 4 bytes per pixel. - Render.LockTexture(Texture, IntPtr.Zero, out var pixels, out var _); + Render.LockTexture(Texture, IntPtr.Zero, out var pixels, out _); Buffer.MemoryCopy(GetCanvasPointer().ToPointer(), pixels, copySize, copySize); Render.UnlockTexture(Texture); } diff --git a/DotSDL/Graphics/Canvas.cs b/DotSDL/Graphics/Canvas.cs index dc1996b..ed8f516 100644 --- a/DotSDL/Graphics/Canvas.cs +++ b/DotSDL/Graphics/Canvas.cs @@ -153,6 +153,17 @@ namespace DotSDL.Graphics { } } + /// + /// true if the should be drawn to the screen, otherwise false. + /// + public bool Shown { get; set; } = true; + + /// + /// The order in which the canvas is drawn. Lower numbered instances are drawn first + /// and will appear on the bottom. This number can be negative. + /// + public int ZOrder { get; set; } + /// /// Sets the section of the that should be drawn. If the size values are set to 0, the /// will fill as much of its containing object as possible. diff --git a/DotSDL/Graphics/SdlWindow.cs b/DotSDL/Graphics/SdlWindow.cs index 64be46f..7cfc834 100644 --- a/DotSDL/Graphics/SdlWindow.cs +++ b/DotSDL/Graphics/SdlWindow.cs @@ -31,6 +31,8 @@ namespace DotSDL.Graphics { private float _nextGameUpdate; private float _updateDelta; + private List _drawList = new List(); + private ScalingQuality _scalingQuality = ScalingQuality.Nearest; /// @@ -228,6 +230,7 @@ namespace DotSDL.Graphics { BlendMode = BlendMode.None } }; + Background.ZOrder = int.MinValue; Background.CreateTexture(); CameraView = new Rectangle( @@ -277,20 +280,29 @@ namespace DotSDL.Graphics { Render.SetRenderTarget(_renderer, _texture); - // Blit the background canvases to the target texture. - foreach(var canvas in Layers) { - canvas.Clipping.Position = CameraView.Position; - canvas.Clipping.Size = CameraView.Size; - canvas.UpdateTexture(); - unsafe { - var canvasClippingRect = canvas.Clipping.SdlRect; - Render.RenderCopy(_renderer, canvas.Texture, new IntPtr(&canvasClippingRect), IntPtr.Zero); + // Sort all of the canvases, then draw them. + _drawList.Clear(); + _drawList.AddRange(Layers.Where(l => l.Shown).ToArray()); + _drawList.AddRange(Sprites.Where(s => s.Shown).ToArray()); + + foreach(var canvas in _drawList.OrderBy(layer => layer.ZOrder)) { + switch(canvas) { + case Sprite sprite: + DrawSprite(sprite); + break; + default: + canvas.Clipping.Position = CameraView.Position; + canvas.Clipping.Size = CameraView.Size; + canvas.UpdateTexture(); + unsafe { + var canvasClippingRect = canvas.Clipping.SdlRect; + Render.RenderCopy(_renderer, canvas.Texture, new IntPtr(&canvasClippingRect), IntPtr.Zero); + } + + break; } } - // Plot sprites on top of the background layer. - if(Sprites.Count > 0) DrawSprites(); - Render.SetRenderTarget(_renderer, IntPtr.Zero); Render.RenderCopy(_renderer, _texture, IntPtr.Zero, IntPtr.Zero); @@ -343,61 +355,58 @@ namespace DotSDL.Graphics { } /// - /// Plots the sprites stored in to the screen. Please note that this method is called by - /// DotSDL's drawing routines and does not need to be called manually. Additionally, this method will not be - /// called if there are no sprites defined. You usually do not need to override this method. - /// - public virtual void DrawSprites() { - Render.SetRenderTarget(_renderer, _texture); - foreach(var sprite in Sprites.Where(e => e.Shown).OrderBy(e => e.ZOrder)) { - var srcRect = sprite.Clipping.SdlRect; - var drawSize = sprite.DrawSize; - - Rectangle dest; - Point rotationCenter; - if(sprite.CoordinateSystem == CoordinateSystem.ScreenSpace) { - dest = new Rectangle(sprite.Position, drawSize); - rotationCenter = sprite.RotationCenter; - } else { - // Create a set of world coordinates based on the position of the camera - // and this sprite. - var relPosition = new Point(sprite.Position - CameraView.Position); - var screenPosition = new Point( - (int)((float)relPosition.X / CameraView.Size.X * RenderWidth), - (int)((float)relPosition.Y / CameraView.Size.Y * RenderHeight) - ); - var scaleFactorX = (float)RenderWidth / CameraView.Size.X; - var scaleFactorY = (float)RenderHeight / CameraView.Size.Y; - var size = new Point( - (int)(drawSize.X * scaleFactorX), - (int)(drawSize.Y * scaleFactorY) - ); - - dest = new Rectangle(screenPosition, size); - rotationCenter = new Point( - (int)(sprite.RotationCenter.X * scaleFactorX), - (int)(sprite.RotationCenter.Y * scaleFactorY) - ); - } + /// Plots a sprite to the screen. Please note that this method is called by DotSDL's drawing + /// routines and does not need to be called manually. Additionally, this method will never be + /// called if there are no active sprites. You usually do not need to override this method. + /// + public virtual void DrawSprite(Sprite sprite) { + var srcRect = sprite.Clipping.SdlRect; + var drawSize = sprite.DrawSize; + + Rectangle dest; + Point rotationCenter; + if(sprite.CoordinateSystem == CoordinateSystem.ScreenSpace) { + dest = new Rectangle(sprite.Position, drawSize); + rotationCenter = sprite.RotationCenter; + } else { + // Create a set of world coordinates based on the position of the camera + // and this sprite. + var relPosition = new Point(sprite.Position - CameraView.Position); + var screenPosition = new Point( + (int)((float)relPosition.X / CameraView.Size.X * RenderWidth), + (int)((float)relPosition.Y / CameraView.Size.Y * RenderHeight) + ); + var scaleFactorX = (float)RenderWidth / CameraView.Size.X; + var scaleFactorY = (float)RenderHeight / CameraView.Size.Y; + var size = new Point( + (int)(drawSize.X * scaleFactorX), + (int)(drawSize.Y * scaleFactorY) + ); + + dest = new Rectangle(screenPosition, size); + rotationCenter = new Point( + (int)(sprite.RotationCenter.X * scaleFactorX), + (int)(sprite.RotationCenter.Y * scaleFactorY) + ); + } - var destRect = dest.SdlRect; - var rotationCenterPoint = rotationCenter.SdlPoint; - - unsafe { - var srcRectPtr = new IntPtr(&srcRect); - var destRectPtr = new IntPtr(&destRect); - var rotationCenterPtr = new IntPtr(&rotationCenterPoint); - - Render.RenderCopyEx( - renderer: _renderer, - texture: sprite.Texture, - srcRect: srcRectPtr, - dstRect: destRectPtr, - angle: sprite.Rotation, - center: rotationCenterPtr, - flip: sprite.Flip - ); - } + var destRect = dest.SdlRect; + var rotationCenterPoint = rotationCenter.SdlPoint; + + unsafe { + var srcRectPtr = new IntPtr(&srcRect); + var destRectPtr = new IntPtr(&destRect); + var rotationCenterPtr = new IntPtr(&rotationCenterPoint); + + Render.RenderCopyEx( + renderer: _renderer, + texture: sprite.Texture, + srcRect: srcRectPtr, + dstRect: destRectPtr, + angle: sprite.Rotation, + center: rotationCenterPtr, + flip: sprite.Flip + ); } } @@ -537,17 +546,13 @@ namespace DotSDL.Graphics { /// /// Displays the window and begins executing code that's associated with it. /// - public void Start() { - Start(0, 0); - } + public void Start() => Start(0, 0); /// /// Displays the window and begins executing code that's associated with it. /// /// The desired number of video and game logic updates per second. 0 causes the display and game to be updated as quickly as possible. - public void Start(float updateRate) { - Start(updateRate, updateRate); - } + public void Start(float updateRate) => Start(updateRate, updateRate); /// /// Displays the window and begins executing code that's associated with it.