Browse Source

Support for multiple background layers.

* An arbitrary number of background layers can now be created and deleted
      using AddLayer and RemoveLayer.
    * The SdlWindow's Background object now points to Layer 0, which cannot be
      deleted. This should ensure that no code has been broken by this.
    * A basic sample project has been added to demonstrate this new feature.
improved_timing
Ian Burgmyer 5 years ago
parent
commit
d7213bcd70
  1. 11
      DotSDL.sln
  2. 99
      DotSDL/Graphics/SdlWindow.cs
  3. 2
      DotSDL/Graphics/Sprite.cs
  4. 8
      Samples/Sample.Layers/Program.cs
  5. 13
      Samples/Sample.Layers/Sample.Layers.csproj
  6. 91
      Samples/Sample.Layers/Window.cs

11
DotSDL.sln

@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Power", "Samples\Sam
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Sprites", "Samples\Sample.Sprites\Sample.Sprites.csproj", "{305585D5-BD22-400D-80AA-459E05A9E243}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Layers", "Samples\Sample.Layers\Sample.Layers.csproj", "{23D3396E-6B46-4177-ACBB-CEE66910F05B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -69,6 +71,14 @@ Global
{305585D5-BD22-400D-80AA-459E05A9E243}.Release|x64.Build.0 = Release|Any CPU
{305585D5-BD22-400D-80AA-459E05A9E243}.Release|x86.ActiveCfg = Release|Any CPU
{305585D5-BD22-400D-80AA-459E05A9E243}.Release|x86.Build.0 = Release|Any CPU
{23D3396E-6B46-4177-ACBB-CEE66910F05B}.Debug|x64.ActiveCfg = Debug|Any CPU
{23D3396E-6B46-4177-ACBB-CEE66910F05B}.Debug|x64.Build.0 = Debug|Any CPU
{23D3396E-6B46-4177-ACBB-CEE66910F05B}.Debug|x86.ActiveCfg = Debug|Any CPU
{23D3396E-6B46-4177-ACBB-CEE66910F05B}.Debug|x86.Build.0 = Debug|Any CPU
{23D3396E-6B46-4177-ACBB-CEE66910F05B}.Release|x64.ActiveCfg = Release|Any CPU
{23D3396E-6B46-4177-ACBB-CEE66910F05B}.Release|x64.Build.0 = Release|Any CPU
{23D3396E-6B46-4177-ACBB-CEE66910F05B}.Release|x86.ActiveCfg = Release|Any CPU
{23D3396E-6B46-4177-ACBB-CEE66910F05B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -80,5 +90,6 @@ Global
{B71D69F1-3BB1-4CC8-AB6A-2A0F2DAE9FE5} = {70DA3135-B76E-421D-B9CF-E49CD6440B0A}
{45E8AA1C-97DC-4889-B64E-2874BCAC6D40} = {70DA3135-B76E-421D-B9CF-E49CD6440B0A}
{305585D5-BD22-400D-80AA-459E05A9E243} = {70DA3135-B76E-421D-B9CF-E49CD6440B0A}
{23D3396E-6B46-4177-ACBB-CEE66910F05B} = {70DA3135-B76E-421D-B9CF-E49CD6440B0A}
EndGlobalSection
EndGlobal

99
DotSDL/Graphics/SdlWindow.cs

@ -2,6 +2,7 @@
using DotSDL.Input;
using DotSDL.Interop.Core;
using System;
using System.Collections.Generic;
using System.Linq;
namespace DotSDL.Graphics {
@ -18,8 +19,6 @@ namespace DotSDL.Graphics {
private IntPtr _texture;
private bool _hasTexture;
public readonly Canvas Background;
private bool _running;
private uint _nextVideoUpdate;
@ -27,6 +26,18 @@ namespace DotSDL.Graphics {
private ScalingQuality _scalingQuality = ScalingQuality.Nearest;
/// <summary>Gets the background layer of this window. This is equivalent to accessing Layers[0].</summary>
public Canvas Background => Layers[0];
/// <summary>Gets the list of background layers for this window.</summary>
public List<Canvas> Layers { get; }
/// <summary>
/// Gets the number of background layers for this windows. This number will always be greater
/// than or equal to 1.
/// </summary>
public int LayerCount => Layers.Count;
/// <summary><c>true</c> if this <see cref="SdlWindow"/> instance has been destroyed, othersize <c>false</c>.</summary>
public bool IsDestroyed { get; set; }
@ -50,9 +61,9 @@ namespace DotSDL.Graphics {
}
/// <summary>Gets the width of the rendering target used by this <see cref="SdlWindow"/>.</summary>
public int TextureWidth { get; }
public int RenderWidth { get; }
/// <summary>Gets the height of the rendering target used by this <see cref="SdlWindow"/>.</summary>
public int TextureHeight { get; }
public int RenderHeight { get; }
/// <summary>The amount of time, in milliseconds, from when the application was started.</summary>
public uint TicksElapsed => Timer.GetTicks();
@ -144,9 +155,9 @@ namespace DotSDL.Graphics {
/// <param name="position">A <see cref="Point"/> representing the starting position of the window. The X and Y coordinates of the Point can be set to <see cref="WindowPosUndefined"/> or <see cref="WindowPosCentered"/>.</param>
/// <param name="windowWidth">The width of the window.</param>
/// <param name="windowHeight">The height of the window.</param>
/// <param name="textureWidth">The width of the window's texture.</param>
/// <param name="textureHeight">The height of the window's texture.</param>
public SdlWindow(string title, Point position, int windowWidth, int windowHeight, int textureWidth, int textureHeight) : this(title, position, windowWidth, windowHeight, textureWidth, textureHeight, ScalingQuality.Nearest) { }
/// <param name="renderWidth">The width of the rendering target.</param>
/// <param name="renderHeight">The height of the rendering target.</param>
public SdlWindow(string title, Point position, int windowWidth, int windowHeight, int renderWidth, int renderHeight) : this(title, position, windowWidth, windowHeight, renderWidth, renderHeight, ScalingQuality.Nearest) { }
/// <summary>
/// Creates a new <see cref="SdlWindow"/>.
@ -155,10 +166,10 @@ namespace DotSDL.Graphics {
/// <param name="position">A <see cref="Point"/> representing the starting position of the window. The X and Y coordinates of the Point can be set to <see cref="WindowPosUndefined"/> or <see cref="WindowPosCentered"/>.</param>
/// <param name="windowWidth">The width of the window.</param>
/// <param name="windowHeight">The height of the window.</param>
/// <param name="textureWidth">The width of the window's texture.</param>
/// <param name="textureHeight">The height of the window's texture.</param>
/// <param name="renderWidth">The width of the rendering target.</param>
/// <param name="renderHeight">The height of the rendering target.</param>
/// <param name="scalingQuality">The scaling (filtering) method to use for the background canvas texture.</param>
public SdlWindow(string title, Point position, int windowWidth, int windowHeight, int textureWidth, int textureHeight, ScalingQuality scalingQuality) {
public SdlWindow(string title, Point position, int windowWidth, int windowHeight, int renderWidth, int renderHeight, ScalingQuality scalingQuality) {
_sdlInit.InitSubsystem(Init.SubsystemFlags.Video);
_windowTitle = title;
@ -168,15 +179,17 @@ namespace DotSDL.Graphics {
WindowWidth = windowWidth;
WindowHeight = windowHeight;
TextureWidth = textureWidth;
TextureHeight = textureHeight;
RenderWidth = renderWidth;
RenderHeight = renderHeight;
ScalingQuality = scalingQuality;
CreateTexture();
Background = new Background(textureWidth, textureHeight) {
Renderer = _renderer,
BlendMode = BlendMode.None
Layers = new List<Canvas> {
new Background(renderWidth, renderHeight) {
Renderer = _renderer,
BlendMode = BlendMode.None
}
};
Background.CreateTexture();
@ -199,6 +212,24 @@ namespace DotSDL.Graphics {
_resources.UnregisterResource(this);
}
/// <summary>
/// Adds a background layer to the top of the layer stack.
/// </summary>
/// <param name="width">The width of the new layer's texture.</param>
/// <param name="height">The height of the new layer's texture.</param>
/// <param name="blendMode">The blending mode of the new layer.</param>
/// <returns>An integer identifying the new layer.</returns>
public int AddLayer(int width, int height, BlendMode blendMode = BlendMode.Alpha) {
var layer = new Background(width, height) {
Renderer = _renderer,
BlendMode = blendMode
};
layer.CreateTexture();
Layers.Add(layer);
return Layers.Count - 1;
}
/// <summary>
/// Handles calling the user draw function and passing the CLR objects to SDL2.
/// </summary>
@ -209,13 +240,15 @@ namespace DotSDL.Graphics {
Render.SetRenderTarget(_renderer, _texture);
// Blit the Canvas to the target texture.
Background.Clipping.Position = CameraView.Position;
Background.Clipping.Size = CameraView.Size;
Background.UpdateTexture();
unsafe {
var canvasClippingRect = Background.Clipping.SdlRect;
Render.RenderCopy(_renderer, Background.Texture, new IntPtr(&canvasClippingRect), IntPtr.Zero);
// 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);
}
}
// Plot sprites on top of the background layer.
@ -250,7 +283,7 @@ namespace DotSDL.Graphics {
private void CreateTexture() {
DestroyTexture();
Hints.SetHint(Hints.RenderScaleQuality, ScalingQuality.ToString());
_texture = Render.CreateTexture(_renderer, Pixels.PixelFormatArgb8888, Render.TextureAccess.Target, TextureWidth, TextureHeight);
_texture = Render.CreateTexture(_renderer, Pixels.PixelFormatArgb8888, Render.TextureAccess.Target, RenderWidth, RenderHeight);
_hasTexture = true;
}
@ -291,11 +324,11 @@ namespace DotSDL.Graphics {
// and this sprite.
var relPosition = new Point(sprite.Position - CameraView.Position);
var screenPosition = new Point(
(int)((float)relPosition.X / CameraView.Size.X * TextureWidth),
(int)((float)relPosition.Y / CameraView.Size.Y * TextureHeight)
(int)((float)relPosition.X / CameraView.Size.X * RenderWidth),
(int)((float)relPosition.Y / CameraView.Size.Y * RenderHeight)
);
var scaleFactorX = (float)TextureWidth / CameraView.Size.X;
var scaleFactorY = (float)TextureHeight / CameraView.Size.Y;
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)
@ -430,6 +463,18 @@ namespace DotSDL.Graphics {
/// </summary>
protected virtual void OnUpdate() { }
/// <summary>
/// Removes a layer from the layer stack.
/// </summary>
/// <param name="id">The unique identifier of the layer to remove.</param>
/// <remarks>The background layer (layer 0) cannot be deleted.</remarks>
/// <exception cref="ArgumentOutOfRangeException"><c>id</c> is less than 0.</exception>
/// /// <exception cref="ArgumentOutOfRangeException"><c>id</c> is equal to or greater than <see cref="LayerCount"/>.</exception>
public void RemoveLayer(int id) {
if(id == 0) throw new ArgumentOutOfRangeException(nameof(id), "The background object (layer 0) cannot be deleted.");
Layers.RemoveAt(id);
}
/// <summary>
/// Displays the window and begins executing code that's associated with it.
/// </summary>

2
DotSDL/Graphics/Sprite.cs

@ -7,7 +7,7 @@ namespace DotSDL.Graphics {
/// Represents a graphical, two-dimensional object in a program.
/// </summary>
public class Sprite : Canvas {
private Point _effectiveSize = new Point();
private readonly Point _effectiveSize = new Point();
private Vector2<float> _scale;
/// <summary>

8
Samples/Sample.Layers/Program.cs

@ -0,0 +1,8 @@
namespace Sample.Layers {
internal class Program {
private static void Main(string[] args) {
var window = new Window(4);
window.Start(16);
}
}
}

13
Samples/Sample.Layers/Sample.Layers.csproj

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\DotSDL\DotSDL.csproj" />
</ItemGroup>
</Project>

91
Samples/Sample.Layers/Window.cs

@ -0,0 +1,91 @@
using System;
using DotSDL.Events;
using DotSDL.Graphics;
using DotSDL.Input.Keyboard;
namespace Sample.Layers {
public class Window : SdlWindow {
private const double Amplitude = 32.0f;
private const double Period = Math.PI * 4;
private readonly int _redLayer, _greenLayer, _blueLayer;
private double _redPhase = 0.0, _greenPhase = 2.0, _bluePhase = 4.0;
private const double RedSpeed = 0.010, GreenSpeed = 0.015, BlueSpeed = 0.020;
public Window(int scale) : base("Layer Test",
new Point(WindowPosUndefined, WindowPosUndefined),
256 * scale, 196 * scale,
256, 196) {
KeyPressed += OnKeyPressed;
// Base background layer.
for(var i = RenderHeight / 2 * RenderWidth; i < RenderHeight / 2 * RenderWidth + RenderWidth; i++) {
Background.Pixels[i].A = 255;
Background.Pixels[i].R =
Background.Pixels[i].G =
Background.Pixels[i].B = 32;
}
for(var i = (RenderHeight / 2 + 1) * RenderWidth; i < RenderWidth * RenderHeight; i++) {
Background.Pixels[i].A = 255;
Background.Pixels[i].R =
Background.Pixels[i].G =
Background.Pixels[i].B = 64;
}
// Add new layers.
_redLayer = AddLayer(RenderWidth, RenderHeight, BlendMode.Additive);
_greenLayer = AddLayer(RenderWidth, RenderHeight, BlendMode.Additive);
_blueLayer = AddLayer(RenderWidth, RenderHeight, BlendMode.Additive);
}
private void DrawSlice(in Color[] pixels, int x, double phase, byte rVal, byte gVal, byte bVal) {
var val = Math.Abs(Math.Sin((double)x / RenderWidth * Period + phase));
var yMax = val * Amplitude;
var yMaxInt = (int)yMax;
var center = RenderHeight / 2;
Func<int, int, int> pos = (xPix, yPix) => (yPix + center) * RenderWidth + xPix;
for(var y = -yMaxInt; y < yMaxInt + 1; y++) {
var idx = pos(x, y);
pixels[idx].A = 255;
pixels[idx].R = rVal;
pixels[idx].G = gVal;
pixels[idx].B = bVal;
}
// Update the alpha value on the top/bottom pixels to make it sorta look
// antialiased. :)
pixels[pos(x, yMaxInt)].A = pixels[pos(x, -yMaxInt)].A = (byte)(255.0 * (yMax - yMaxInt));
}
protected override void OnDraw() {
// Clear the sine wave from each of the layers.
for(var i = (RenderHeight / 2 - (int)Amplitude) * RenderWidth; i < (RenderHeight / 2 + (int)Amplitude) * RenderWidth; i++) {
Layers[_redLayer].Pixels[i].R = 0;
Layers[_greenLayer].Pixels[i].G = 0;
Layers[_blueLayer].Pixels[i].B = 0;
}
for(var x = 0; x < RenderWidth; x++) {
DrawSlice(Layers[_redLayer].Pixels, x, _redPhase, 255, 0, 0);
DrawSlice(Layers[_greenLayer].Pixels, x, _greenPhase, 0, 255, 0);
DrawSlice(Layers[_blueLayer].Pixels, x, _bluePhase, 0, 0, 255);
}
}
private void OnKeyPressed(object sender, KeyboardEvent e) {
if(e.Keycode == Keycode.Escape)
Stop();
}
protected override void OnUpdate() {
_redPhase += RedSpeed;
_greenPhase += GreenSpeed;
_bluePhase += BlueSpeed;
}
}
}
Loading…
Cancel
Save