Compare commits
4 Commits
main
...
integratio
| Author | SHA1 | Date | |
|---|---|---|---|
| 129aae563e | |||
| 18ac6b23fd | |||
| 02130d8324 | |||
| 9935805f06 |
2
Engine
2
Engine
Submodule Engine updated: ad444decbb...b9f3227f73
@@ -10,6 +10,7 @@
|
|||||||
<AssemblyName>MyUniverse</AssemblyName>
|
<AssemblyName>MyUniverse</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<ApplicationIcon>Icon.ico</ApplicationIcon>
|
<ApplicationIcon>Icon.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -26,20 +27,16 @@
|
|||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.2.1105" />
|
|
||||||
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.2.1105" />
|
|
||||||
<PackageReference Include="nulastudio.NetBeauty" Version="2.1.5" />
|
<PackageReference Include="nulastudio.NetBeauty" Version="2.1.5" />
|
||||||
|
<PackageReference Include="SDL3-CS" Version="3.3.7" />
|
||||||
|
<PackageReference Include="Silk.NET.OpenGL" Version="2.23.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../Engine/Engine.Integration/Engine.Integration.LiteNetLib/Engine.Integration.LiteNetLib.csproj" />
|
<ProjectReference Include="../../Engine/Engine.Integration/Engine.Integration.LiteNetLib/Engine.Integration.LiteNetLib.csproj" />
|
||||||
|
<ProjectReference Include="../../Engine/Engine.Integration/Engine.Integration.Yaml/Engine.Integration.Yaml.csproj" />
|
||||||
<ProjectReference Include="../../Shared/Shared.csproj" />
|
<ProjectReference Include="../../Shared/Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<MonoGameContentReference Include="../../Shared/Content/Content.mgcb">
|
|
||||||
<Link>Content/Content.mgcb</Link>
|
|
||||||
</MonoGameContentReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../../Shared/Shared.csproj" />
|
<ProjectReference Include="../../Shared/Shared.csproj" />
|
||||||
<ProjectReference Include="../../Engine/Engine.Integration/Engine.Integration.Yaml/Engine.Integration.Yaml.csproj" />
|
<ProjectReference Include="../../Engine/Engine.Integration/Engine.Integration.Yaml/Engine.Integration.Yaml.csproj" />
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Engine.Core;
|
||||||
|
|
||||||
|
using SDL3;
|
||||||
|
|
||||||
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public class CameraController2D : Behaviour2D, IFirstFrameUpdate, IUpdate
|
||||||
|
{
|
||||||
|
private Sdl3KeyboardInputs keyboardInputs = null!;
|
||||||
|
private ICamera2D camera = null!;
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
keyboardInputs = Universe.FindRequiredBehaviour<Sdl3KeyboardInputs>();
|
||||||
|
camera = BehaviourController.GetRequiredBehaviourInParent<ICamera2D>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (keyboardInputs.IsPressed(SDL.Keycode.W)) Transform.Position += Transform.Up * Universe.Time.DeltaTime * 100f;
|
||||||
|
if (keyboardInputs.IsPressed(SDL.Keycode.A)) Transform.Position += Transform.Left * Universe.Time.DeltaTime * 100f;
|
||||||
|
if (keyboardInputs.IsPressed(SDL.Keycode.S)) Transform.Position += Transform.Down * Universe.Time.DeltaTime * 100f;
|
||||||
|
if (keyboardInputs.IsPressed(SDL.Keycode.D)) Transform.Position += Transform.Right * Universe.Time.DeltaTime * 100f;
|
||||||
|
|
||||||
|
if (keyboardInputs.IsPressed(SDL.Keycode.Q)) Transform.Rotation += Universe.Time.DeltaTime * 45f;
|
||||||
|
if (keyboardInputs.IsPressed(SDL.Keycode.E)) Transform.Rotation -= Universe.Time.DeltaTime * 45f;
|
||||||
|
|
||||||
|
if (keyboardInputs.IsPressed(SDL.Keycode.R)) camera.Zoom += Universe.Time.DeltaTime * 15f;
|
||||||
|
if (keyboardInputs.IsPressed(SDL.Keycode.F)) camera.Zoom -= Universe.Time.DeltaTime * 15f;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using SDL3;
|
||||||
|
|
||||||
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public interface ISdl3EventProcessor
|
||||||
|
{
|
||||||
|
void Process(SDL.Event sdlEvent);
|
||||||
|
}
|
||||||
86
Platforms/Desktop/Engine.Integration.SDL3/Sdl3Camera2D.cs
Normal file
86
Platforms/Desktop/Engine.Integration.SDL3/Sdl3Camera2D.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
using Engine.Core;
|
||||||
|
|
||||||
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public class Sdl3Camera2D : Behaviour, ICamera2D, IFirstFrameUpdate, ILastFrameUpdate, IPreDraw
|
||||||
|
{
|
||||||
|
public Event<Sdl3Camera2D> OnViewMatrixChanged { get; } = new();
|
||||||
|
public Event<Sdl3Camera2D> OnProjectionMatrixChanged { get; } = new();
|
||||||
|
public Event<Sdl3Camera2D> OnViewportChanged { get; } = new();
|
||||||
|
public Event<Sdl3Camera2D> OnZoomChanged { get; } = new();
|
||||||
|
|
||||||
|
private IWindow window = null!;
|
||||||
|
public ITransform2D Transform { get; private set; } = null!;
|
||||||
|
|
||||||
|
public Matrix4x4 ProjectionMatrix
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (field == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
field = value;
|
||||||
|
OnProjectionMatrixChanged.Invoke(this);
|
||||||
|
}
|
||||||
|
} = Matrix4x4.Identity;
|
||||||
|
|
||||||
|
public Matrix4x4 ViewMatrix
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (field == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
field = value;
|
||||||
|
OnViewMatrixChanged.Invoke(this);
|
||||||
|
}
|
||||||
|
} = Matrix4x4.Identity;
|
||||||
|
|
||||||
|
public float Zoom
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
float newValue = Math.Max(0.1f, value);
|
||||||
|
|
||||||
|
if (field == newValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
field = newValue;
|
||||||
|
OnZoomChanged.Invoke(this);
|
||||||
|
}
|
||||||
|
} = 1f;
|
||||||
|
|
||||||
|
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
|
||||||
|
public Vector2D ScreenToWorldPosition(Vector2D screenPosition)
|
||||||
|
{
|
||||||
|
Vector2D worldPosition = Vector2D.Transform(screenPosition, Transform);
|
||||||
|
return worldPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2D WorldToScreenPosition(Vector2D worldPosition)
|
||||||
|
{
|
||||||
|
Vector2D screenPosition = Vector2D.Transform(worldPosition, Transform);
|
||||||
|
return screenPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LastActiveFrame() => Transform = null!;
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
window = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<IWindow>();
|
||||||
|
Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PreDraw()
|
||||||
|
{
|
||||||
|
ProjectionMatrix = Matrix4x4.CreateOrthographicViewCentered(window.Size.X, window.Size.Y);
|
||||||
|
|
||||||
|
ViewMatrix = Matrix4x4.Identity
|
||||||
|
.ApplyScale(Transform.Scale.X.Max(Transform.Scale.Y))
|
||||||
|
.ApplyScale(Zoom)
|
||||||
|
.ApplyRotationZ(-Transform.Rotation * Math.DegreeToRadian)
|
||||||
|
.ApplyTranslation(new Vector3D(-Transform.Position.X, -Transform.Position.Y, 0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Microsoft.Xna.Framework.Input;
|
|
||||||
|
|
||||||
using Engine.Core;
|
using Engine.Core;
|
||||||
using Engine.Integration.MonoGame;
|
|
||||||
using Engine.Systems.Input;
|
using Engine.Systems.Input;
|
||||||
|
|
||||||
using MyUniverse.Shared.Behaviours.Abstract;
|
using MyUniverse.Shared.Behaviours.Abstract;
|
||||||
|
|
||||||
namespace MyUniverse.Platforms.Desktop;
|
using SDL3;
|
||||||
|
|
||||||
public class DesktopInputs : Behaviour, IEnterUniverse, IExitUniverse, IGameInputs
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public class Sdl3GameInputs : Behaviour, IGameInputs, IFirstFrameUpdate
|
||||||
{
|
{
|
||||||
public IButtonInputs<IGameInputs.Button>.InputEvent OnAnyButtonPressed { get; } = new();
|
public IButtonInputs<IGameInputs.Button>.InputEvent OnAnyButtonPressed { get; } = new();
|
||||||
public IButtonInputs<IGameInputs.Button>.InputEvent OnAnyButtonReleased { get; } = new();
|
public IButtonInputs<IGameInputs.Button>.InputEvent OnAnyButtonReleased { get; } = new();
|
||||||
@@ -19,23 +18,23 @@ public class DesktopInputs : Behaviour, IEnterUniverse, IExitUniverse, IGameInpu
|
|||||||
private readonly Dictionary<IGameInputs.Button, IButtonInputs<IGameInputs.Button>.InputEvent> OnPressed = new(256);
|
private readonly Dictionary<IGameInputs.Button, IButtonInputs<IGameInputs.Button>.InputEvent> OnPressed = new(256);
|
||||||
private readonly Dictionary<IGameInputs.Button, IButtonInputs<IGameInputs.Button>.InputEvent> OnReleased = new(256);
|
private readonly Dictionary<IGameInputs.Button, IButtonInputs<IGameInputs.Button>.InputEvent> OnReleased = new(256);
|
||||||
|
|
||||||
private readonly Dictionary<IGameInputs.Button, Keys> keyMappingReversed = [];
|
private readonly Dictionary<IGameInputs.Button, SDL.Keycode> keyMappingReversed = [];
|
||||||
private readonly Dictionary<Keys, IGameInputs.Button> keyMapping = new() {
|
private readonly Dictionary<SDL.Keycode, IGameInputs.Button> keyMapping = new() {
|
||||||
{ Keys.Space, IGameInputs.Button.Interact }
|
{ SDL.Keycode.Space, IGameInputs.Button.Interact }
|
||||||
};
|
};
|
||||||
|
|
||||||
private KeyboardInputs keyboardInputs = null!;
|
private Sdl3KeyboardInputs keyboardInputs = null!;
|
||||||
|
|
||||||
public void EnterUniverse(IUniverse universe)
|
public void FirstActiveFrame()
|
||||||
{
|
{
|
||||||
keyboardInputs = BehaviourController.GetOrAddBehaviour<KeyboardInputs>();
|
keyboardInputs = BehaviourController.GetOrAddBehaviour<Sdl3KeyboardInputs>();
|
||||||
keyboardInputs.OnAnyButtonPressed.AddListener(AnyPressCallback);
|
keyboardInputs.OnAnyButtonPressed.AddListener(AnyPressCallback);
|
||||||
keyboardInputs.OnAnyButtonReleased.AddListener(AnyReleaseCallback);
|
keyboardInputs.OnAnyButtonReleased.AddListener(AnyReleaseCallback);
|
||||||
|
|
||||||
foreach ((Keys key, IGameInputs.Button button) in keyMapping)
|
foreach ((SDL.Keycode key, IGameInputs.Button button) in keyMapping)
|
||||||
keyboardInputs.RegisterOnPress(key, PressCallback);
|
keyboardInputs.RegisterOnPress(key, PressCallback);
|
||||||
|
|
||||||
foreach ((Keys key, IGameInputs.Button button) in keyMapping)
|
foreach ((SDL.Keycode key, IGameInputs.Button button) in keyMapping)
|
||||||
keyboardInputs.RegisterOnRelease(key, ReleaseCallback);
|
keyboardInputs.RegisterOnRelease(key, ReleaseCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,14 +43,14 @@ public class DesktopInputs : Behaviour, IEnterUniverse, IExitUniverse, IGameInpu
|
|||||||
keyboardInputs.OnAnyButtonPressed.RemoveListener(AnyPressCallback);
|
keyboardInputs.OnAnyButtonPressed.RemoveListener(AnyPressCallback);
|
||||||
keyboardInputs.OnAnyButtonReleased.RemoveListener(AnyReleaseCallback);
|
keyboardInputs.OnAnyButtonReleased.RemoveListener(AnyReleaseCallback);
|
||||||
|
|
||||||
foreach ((Keys key, IGameInputs.Button button) in keyMapping)
|
foreach ((SDL.Keycode key, IGameInputs.Button button) in keyMapping)
|
||||||
keyboardInputs.UnregisterOnPress(key, PressCallback);
|
keyboardInputs.UnregisterOnPress(key, PressCallback);
|
||||||
|
|
||||||
foreach ((Keys key, IGameInputs.Button button) in keyMapping)
|
foreach ((SDL.Keycode key, IGameInputs.Button button) in keyMapping)
|
||||||
keyboardInputs.UnregisterOnRelease(key, ReleaseCallback);
|
keyboardInputs.UnregisterOnRelease(key, ReleaseCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PressCallback(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args)
|
private void PressCallback(IButtonInputs<SDL.Keycode> sender, IButtonInputs<SDL.Keycode>.ButtonCallbackArguments args)
|
||||||
{
|
{
|
||||||
if (!keyMapping.TryGetValue(args.Button, out IGameInputs.Button button))
|
if (!keyMapping.TryGetValue(args.Button, out IGameInputs.Button button))
|
||||||
return;
|
return;
|
||||||
@@ -62,7 +61,7 @@ public class DesktopInputs : Behaviour, IEnterUniverse, IExitUniverse, IGameInpu
|
|||||||
@event.Invoke(this, new(button));
|
@event.Invoke(this, new(button));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReleaseCallback(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args)
|
private void ReleaseCallback(IButtonInputs<SDL.Keycode> sender, IButtonInputs<SDL.Keycode>.ButtonCallbackArguments args)
|
||||||
{
|
{
|
||||||
if (!keyMapping.TryGetValue(args.Button, out IGameInputs.Button button))
|
if (!keyMapping.TryGetValue(args.Button, out IGameInputs.Button button))
|
||||||
return;
|
return;
|
||||||
@@ -73,13 +72,13 @@ public class DesktopInputs : Behaviour, IEnterUniverse, IExitUniverse, IGameInpu
|
|||||||
@event.Invoke(this, new(button));
|
@event.Invoke(this, new(button));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AnyReleaseCallback(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args)
|
private void AnyReleaseCallback(IButtonInputs<SDL.Keycode> sender, IButtonInputs<SDL.Keycode>.ButtonCallbackArguments args)
|
||||||
{
|
{
|
||||||
if (keyMapping.TryGetValue(args.Button, out IGameInputs.Button button))
|
if (keyMapping.TryGetValue(args.Button, out IGameInputs.Button button))
|
||||||
OnAnyButtonPressed?.Invoke(this, new(button));
|
OnAnyButtonPressed?.Invoke(this, new(button));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AnyPressCallback(IButtonInputs<Keys> sender, IButtonInputs<Keys>.ButtonCallbackArguments args)
|
private void AnyPressCallback(IButtonInputs<SDL.Keycode> sender, IButtonInputs<SDL.Keycode>.ButtonCallbackArguments args)
|
||||||
{
|
{
|
||||||
if (keyMapping.TryGetValue(args.Button, out IGameInputs.Button button))
|
if (keyMapping.TryGetValue(args.Button, out IGameInputs.Button button))
|
||||||
OnAnyButtonPressed?.Invoke(this, new(button));
|
OnAnyButtonPressed?.Invoke(this, new(button));
|
||||||
@@ -121,15 +120,15 @@ public class DesktopInputs : Behaviour, IEnterUniverse, IExitUniverse, IGameInpu
|
|||||||
|
|
||||||
public bool IsPressed(IGameInputs.Button button)
|
public bool IsPressed(IGameInputs.Button button)
|
||||||
{
|
{
|
||||||
if (!keyMappingReversed.TryGetValue(button, out Keys key))
|
if (!keyMappingReversed.TryGetValue(button, out SDL.Keycode key))
|
||||||
throw new($"{Enum.GetName(button)} is not mapped correctly");
|
throw new($"{Enum.GetName(button)} is not mapped correctly");
|
||||||
|
|
||||||
return keyboardInputs.IsPressed(key);
|
return keyboardInputs.IsPressed(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DesktopInputs()
|
public Sdl3GameInputs()
|
||||||
{
|
{
|
||||||
foreach ((Keys key, IGameInputs.Button button) in keyMapping)
|
foreach ((SDL.Keycode key, IGameInputs.Button button) in keyMapping)
|
||||||
keyMappingReversed.Add(button, key);
|
keyMappingReversed.Add(button, key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Engine.Core;
|
||||||
|
using Engine.Systems.Input;
|
||||||
|
|
||||||
|
using SDL3;
|
||||||
|
|
||||||
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public class Sdl3KeyboardInputs : Behaviour, ISdl3EventProcessor, IButtonInputs<SDL.Keycode>
|
||||||
|
{
|
||||||
|
public IButtonInputs<SDL.Keycode>.InputEvent OnAnyButtonPressed { get; } = new();
|
||||||
|
public IButtonInputs<SDL.Keycode>.InputEvent OnAnyButtonReleased { get; } = new();
|
||||||
|
|
||||||
|
private readonly Dictionary<SDL.Keycode, IButtonInputs<SDL.Keycode>.InputEvent> OnPressed = new(256);
|
||||||
|
private readonly Dictionary<SDL.Keycode, IButtonInputs<SDL.Keycode>.InputEvent> OnReleased = new(256);
|
||||||
|
|
||||||
|
private readonly FastList<SDL.Keycode> currentlyPressedKeys = [];
|
||||||
|
|
||||||
|
public void RegisterOnPress(SDL.Keycode key, IButtonInputs<SDL.Keycode>.InputEvent.EventHandler callback)
|
||||||
|
{
|
||||||
|
if (!OnPressed.TryGetValue(key, out IButtonInputs<SDL.Keycode>.InputEvent? delegateCallback))
|
||||||
|
{
|
||||||
|
delegateCallback = new();
|
||||||
|
OnPressed.Add(key, delegateCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
delegateCallback.AddListener(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterOnPress(SDL.Keycode key, IButtonInputs<SDL.Keycode>.InputEvent.EventHandler callback)
|
||||||
|
{
|
||||||
|
if (OnPressed.TryGetValue(key, out IButtonInputs<SDL.Keycode>.InputEvent? delegateCallback))
|
||||||
|
delegateCallback.RemoveListener(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterOnRelease(SDL.Keycode key, IButtonInputs<SDL.Keycode>.InputEvent.EventHandler callback)
|
||||||
|
{
|
||||||
|
if (!OnReleased.TryGetValue(key, out IButtonInputs<SDL.Keycode>.InputEvent? delegateCallback))
|
||||||
|
{
|
||||||
|
delegateCallback = new();
|
||||||
|
OnReleased.Add(key, delegateCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
delegateCallback.AddListener(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterOnRelease(SDL.Keycode key, IButtonInputs<SDL.Keycode>.InputEvent.EventHandler callback)
|
||||||
|
{
|
||||||
|
if (OnReleased.TryGetValue(key, out IButtonInputs<SDL.Keycode>.InputEvent? delegateCallback))
|
||||||
|
delegateCallback.RemoveListener(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPressed(SDL.Keycode button) => currentlyPressedKeys.Contains(button);
|
||||||
|
|
||||||
|
public void Process(SDL.Event sdlEvent)
|
||||||
|
{
|
||||||
|
switch ((SDL.EventType)sdlEvent.Type)
|
||||||
|
{
|
||||||
|
case SDL.EventType.KeyDown:
|
||||||
|
SDL.Keycode currentlyPressedKey = sdlEvent.Key.Key;
|
||||||
|
if (IsPressed(currentlyPressedKey))
|
||||||
|
return;
|
||||||
|
|
||||||
|
currentlyPressedKeys.Add(currentlyPressedKey);
|
||||||
|
|
||||||
|
if (OnPressed.TryGetValue(currentlyPressedKey, out IButtonInputs<SDL.Keycode>.InputEvent? pressCallback))
|
||||||
|
pressCallback?.Invoke(this, new(currentlyPressedKey));
|
||||||
|
|
||||||
|
OnAnyButtonPressed?.Invoke(this, new(currentlyPressedKey));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SDL.EventType.KeyUp:
|
||||||
|
SDL.Keycode previouslyPressedKey = sdlEvent.Key.Key;
|
||||||
|
if (!IsPressed(previouslyPressedKey))
|
||||||
|
return;
|
||||||
|
|
||||||
|
currentlyPressedKeys.Remove(previouslyPressedKey);
|
||||||
|
|
||||||
|
if (OnReleased.TryGetValue(previouslyPressedKey, out IButtonInputs<SDL.Keycode>.InputEvent? releaseCallback))
|
||||||
|
releaseCallback?.Invoke(this, new(previouslyPressedKey));
|
||||||
|
|
||||||
|
OnAnyButtonReleased?.Invoke(this, new(previouslyPressedKey));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
269
Platforms/Desktop/Engine.Integration.SDL3/Sdl3TriangleBatch.cs
Normal file
269
Platforms/Desktop/Engine.Integration.SDL3/Sdl3TriangleBatch.cs
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
using System;
|
||||||
|
using Engine.Core;
|
||||||
|
using Engine.Systems.Graphics;
|
||||||
|
using SDL3;
|
||||||
|
using Silk.NET.OpenGL;
|
||||||
|
|
||||||
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public class Sdl3TriangleBatch : Behaviour, ITriangleBatch, IFirstFrameUpdate
|
||||||
|
{
|
||||||
|
private GL gl = null!;
|
||||||
|
private readonly uint MaxVertices = 1024;
|
||||||
|
private uint vao;
|
||||||
|
private uint vbo;
|
||||||
|
private uint shader;
|
||||||
|
|
||||||
|
private uint vertexIndex = 0;
|
||||||
|
private VertexPositionColor[] vertices = new VertexPositionColor[1024];
|
||||||
|
|
||||||
|
private Matrix4x4 view = Matrix4x4.Identity;
|
||||||
|
private Matrix4x4 projection = Matrix4x4.Identity;
|
||||||
|
private Sdl3Window sdl3window = null!;
|
||||||
|
private ICamera? camera = null!;
|
||||||
|
private nint glContext;
|
||||||
|
|
||||||
|
public void FirstActiveFrame()
|
||||||
|
{
|
||||||
|
sdl3window = BehaviourController.GetRequiredBehaviourInParent<Sdl3Window>();
|
||||||
|
camera = BehaviourController.GetRequiredBehaviourInParent<ICamera>();
|
||||||
|
nint window = sdl3window.Window;
|
||||||
|
|
||||||
|
SDL.GLSetAttribute(SDL.GLAttr.ContextMajorVersion, 3);
|
||||||
|
SDL.GLSetAttribute(SDL.GLAttr.ContextMinorVersion, 3);
|
||||||
|
SDL.GLSetAttribute(SDL.GLAttr.ContextProfileMask, (int)SDL.GLProfile.Core);
|
||||||
|
SDL.GLSetAttribute(SDL.GLAttr.DoubleBuffer, 1);
|
||||||
|
|
||||||
|
glContext = SDL.GLCreateContext(window);
|
||||||
|
if (glContext == nint.Zero)
|
||||||
|
throw new Exception(SDL.GetError());
|
||||||
|
|
||||||
|
SDL.GLMakeCurrent(window, glContext);
|
||||||
|
|
||||||
|
gl = GL.GetApi(procName => SDL.GLGetProcAddress(procName));
|
||||||
|
gl.Viewport(0, 0, (uint)sdl3window.Size.X, (uint)sdl3window.Size.Y);
|
||||||
|
vertices = new VertexPositionColor[MaxVertices];
|
||||||
|
|
||||||
|
// --- GL objects ---
|
||||||
|
vao = gl.GenVertexArray();
|
||||||
|
vbo = gl.GenBuffer();
|
||||||
|
|
||||||
|
gl.BindVertexArray(vao);
|
||||||
|
gl.BindBuffer(BufferTargetARB.ArrayBuffer, vbo);
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
gl.BufferData(
|
||||||
|
BufferTargetARB.ArrayBuffer,
|
||||||
|
(nuint)(MaxVertices * sizeof(VertexPositionColor)),
|
||||||
|
null,
|
||||||
|
BufferUsageARB.DynamicDraw
|
||||||
|
);
|
||||||
|
|
||||||
|
// position
|
||||||
|
gl.EnableVertexAttribArray(0);
|
||||||
|
gl.VertexAttribPointer(
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
VertexAttribPointerType.Float,
|
||||||
|
false,
|
||||||
|
(uint)sizeof(VertexPositionColor),
|
||||||
|
(void*)0
|
||||||
|
);
|
||||||
|
|
||||||
|
// color
|
||||||
|
gl.EnableVertexAttribArray(1);
|
||||||
|
gl.VertexAttribPointer(
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
VertexAttribPointerType.Float,
|
||||||
|
false,
|
||||||
|
(uint)sizeof(VertexPositionColor),
|
||||||
|
(void*)(3 * sizeof(float))
|
||||||
|
);
|
||||||
|
|
||||||
|
gl.BindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
shader = CreateShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(Triangle triangle, ColorRGBA colorRGBA)
|
||||||
|
{
|
||||||
|
if (vertexIndex + 3 >= vertices.Length)
|
||||||
|
Flush();
|
||||||
|
|
||||||
|
Vector2D A = triangle.A;
|
||||||
|
Vector2D B = triangle.B;
|
||||||
|
Vector2D C = triangle.C;
|
||||||
|
|
||||||
|
Vector4D c = new Vector4D(colorRGBA.R, colorRGBA.G, colorRGBA.B, colorRGBA.A) / 256f;
|
||||||
|
|
||||||
|
vertices[vertexIndex++] = new()
|
||||||
|
{
|
||||||
|
Position = new Vector3D(A.X, A.Y, 0f),
|
||||||
|
Color = c
|
||||||
|
};
|
||||||
|
|
||||||
|
vertices[vertexIndex++] = new()
|
||||||
|
{
|
||||||
|
Position = new Vector3D(B.X, B.Y, 0f),
|
||||||
|
Color = c
|
||||||
|
};
|
||||||
|
|
||||||
|
vertices[vertexIndex++] = new()
|
||||||
|
{
|
||||||
|
Position = new Vector3D(C.X, C.Y, 0f),
|
||||||
|
Color = c
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Begin(Matrix4x4? view = null, Matrix4x4? projection = null)
|
||||||
|
{
|
||||||
|
this.view = view ?? camera?.ViewMatrix ?? Matrix4x4.Identity;
|
||||||
|
|
||||||
|
if (projection != null)
|
||||||
|
this.projection = projection.Value;
|
||||||
|
else
|
||||||
|
this.projection = camera?.ProjectionMatrix ?? Matrix4x4.CreateOrthographicViewCentered(sdl3window.Size.X, sdl3window.Size.Y);
|
||||||
|
|
||||||
|
vertexIndex = 0;
|
||||||
|
SDL.GLMakeCurrent(sdl3window.Window, glContext);
|
||||||
|
gl.ClearColor(sdl3window.BackgroundColor.R / 256f, sdl3window.BackgroundColor.G / 256f, sdl3window.BackgroundColor.B / 256f, 1f);
|
||||||
|
gl.Clear(ClearBufferMask.ColorBufferBit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void End()
|
||||||
|
{
|
||||||
|
Flush();
|
||||||
|
|
||||||
|
SDL.GLSwapWindow(sdl3window.Window);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Flush()
|
||||||
|
{
|
||||||
|
if (vertexIndex == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gl.UseProgram(shader);
|
||||||
|
|
||||||
|
int viewLoc = gl.GetUniformLocation(shader, "uView");
|
||||||
|
int projLoc = gl.GetUniformLocation(shader, "uProjection");
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
Span<float> temp = stackalloc float[16];
|
||||||
|
|
||||||
|
CopyTo(view.Transposed, temp);
|
||||||
|
fixed (float* v = temp)
|
||||||
|
gl.UniformMatrix4(viewLoc, 1, false, v);
|
||||||
|
|
||||||
|
CopyTo(projection.Transposed, temp);
|
||||||
|
fixed (float* p = temp)
|
||||||
|
gl.UniformMatrix4(projLoc, 1, false, p);
|
||||||
|
|
||||||
|
gl.BindVertexArray(vao);
|
||||||
|
gl.BindBuffer(BufferTargetARB.ArrayBuffer, vbo);
|
||||||
|
|
||||||
|
fixed (VertexPositionColor* ptr = vertices)
|
||||||
|
{
|
||||||
|
gl.BufferSubData(
|
||||||
|
BufferTargetARB.ArrayBuffer,
|
||||||
|
0,
|
||||||
|
(nuint)(vertexIndex * sizeof(VertexPositionColor)),
|
||||||
|
ptr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.DrawArrays(PrimitiveType.Triangles, 0, vertexIndex);
|
||||||
|
|
||||||
|
vertexIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CopyTo(Matrix4x4 m, Span<float> destination)
|
||||||
|
{
|
||||||
|
if (destination.Length < 16)
|
||||||
|
throw new ArgumentException("Destination span must have at least 16 elements.");
|
||||||
|
|
||||||
|
destination[0] = m.M11;
|
||||||
|
destination[1] = m.M12;
|
||||||
|
destination[2] = m.M13;
|
||||||
|
destination[3] = m.M14;
|
||||||
|
|
||||||
|
destination[4] = m.M21;
|
||||||
|
destination[5] = m.M22;
|
||||||
|
destination[6] = m.M23;
|
||||||
|
destination[7] = m.M24;
|
||||||
|
|
||||||
|
destination[8] = m.M31;
|
||||||
|
destination[9] = m.M32;
|
||||||
|
destination[10] = m.M33;
|
||||||
|
destination[11] = m.M34;
|
||||||
|
|
||||||
|
destination[12] = m.M41;
|
||||||
|
destination[13] = m.M42;
|
||||||
|
destination[14] = m.M43;
|
||||||
|
destination[15] = m.M44;
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint CreateShader()
|
||||||
|
{
|
||||||
|
const string vertexSrc = """
|
||||||
|
#version 330 core
|
||||||
|
layout (location = 0) in vec3 aPosition;
|
||||||
|
layout (location = 1) in vec4 aColor;
|
||||||
|
|
||||||
|
uniform mat4 uView;
|
||||||
|
uniform mat4 uProjection;
|
||||||
|
|
||||||
|
out vec4 vColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = uProjection * uView * vec4(aPosition, 1.0);
|
||||||
|
vColor = aColor;
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string fragmentSrc = """
|
||||||
|
#version 330 core
|
||||||
|
in vec4 vColor;
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
FragColor = vColor;
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
uint vs = CompileShader(ShaderType.VertexShader, vertexSrc);
|
||||||
|
uint fs = CompileShader(ShaderType.FragmentShader, fragmentSrc);
|
||||||
|
|
||||||
|
uint program = gl.CreateProgram();
|
||||||
|
gl.AttachShader(program, vs);
|
||||||
|
gl.AttachShader(program, fs);
|
||||||
|
gl.LinkProgram(program);
|
||||||
|
|
||||||
|
gl.DeleteShader(vs);
|
||||||
|
gl.DeleteShader(fs);
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint CompileShader(ShaderType type, string src)
|
||||||
|
{
|
||||||
|
uint shader = gl.CreateShader(type);
|
||||||
|
gl.ShaderSource(shader, src);
|
||||||
|
gl.CompileShader(shader);
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VertexPositionColor
|
||||||
|
{
|
||||||
|
public Vector3D Position;
|
||||||
|
public Vector4D Color;
|
||||||
|
|
||||||
|
public const int SizeInBytes = (3 + 4) * sizeof(float);
|
||||||
|
}
|
||||||
|
}
|
||||||
224
Platforms/Desktop/Engine.Integration.SDL3/Sdl3Window.cs
Normal file
224
Platforms/Desktop/Engine.Integration.SDL3/Sdl3Window.cs
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using Engine.Core;
|
||||||
|
|
||||||
|
using SDL3;
|
||||||
|
|
||||||
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public class Sdl3Window : Behaviour, IWindow, IDisposable, IEnterUniverse, IExitUniverse
|
||||||
|
{
|
||||||
|
public Event<IWindow, IWindow.StateChangedArguments> OnStateChanged { get; } = new();
|
||||||
|
public Event<IWindow, IWindow.FocusStateChangedArguments> OnFocusStateChanged { get; } = new();
|
||||||
|
public Event<IWindow, IWindow.ModeChangedArguments> OnModeChanged { get; } = new();
|
||||||
|
public Event<IWindow, IWindow.ShowStateChangedArguments> OnShowStateChanged { get; } = new();
|
||||||
|
public Event<IWindow, IWindow.ResizeModeChangedArguments> OnResizeModeChanged { get; } = new();
|
||||||
|
|
||||||
|
public Event<IWindow, IWindow.TitleChangedArguments> OnTitleChanged { get; } = new();
|
||||||
|
public Event<IWindow, IWindow.TargetFpsChangedArguments> OnTargetFpsChanged { get; } = new();
|
||||||
|
public Event<IWindow, IWindow.SizeChangedArguments> OnSizeChanged { get; } = new();
|
||||||
|
public Event<IWindow, IWindow.BackgroundColorChangedArguments> OnBackgroundColorChanged { get; } = new();
|
||||||
|
|
||||||
|
public IntPtr Window { get; private set; } = IntPtr.Zero;
|
||||||
|
|
||||||
|
public WindowState State
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == field)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case WindowState.Open: OpenWindow(); break;
|
||||||
|
case WindowState.Closed: default: CloseWindow(); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowState previousState = field;
|
||||||
|
field = value;
|
||||||
|
OnStateChanged.Invoke(this, new(previousState));
|
||||||
|
}
|
||||||
|
} = WindowState.Open;
|
||||||
|
|
||||||
|
public WindowFocusState FocusState
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
if (value == field)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WindowFocusState previousFocusState = field;
|
||||||
|
field = value;
|
||||||
|
OnFocusStateChanged.Invoke(this, new(previousFocusState));
|
||||||
|
}
|
||||||
|
} = WindowFocusState.Unfocused;
|
||||||
|
|
||||||
|
public WindowMode Mode
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == field)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (value is WindowMode.Windowed or WindowMode.BorderlessWindowed)
|
||||||
|
{
|
||||||
|
SDL.SetWindowFullscreen(Window, false);
|
||||||
|
SDL.SyncWindow(Window);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Window != IntPtr.Zero)
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case WindowMode.Windowed: SDL.SetWindowBordered(Window, true); break;
|
||||||
|
case WindowMode.BorderlessWindowed: SDL.SetWindowBordered(Window, false); break;
|
||||||
|
case WindowMode.BorderlessFullscreen: SDL.SetWindowFullscreen(Window, true); break;
|
||||||
|
case WindowMode.Fullscreen: SDL.SetWindowFullscreen(Window, true); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL.SyncWindow(Window);
|
||||||
|
|
||||||
|
WindowMode previousMode = field;
|
||||||
|
field = value;
|
||||||
|
OnModeChanged.Invoke(this, new(previousMode));
|
||||||
|
}
|
||||||
|
} = WindowMode.Windowed;
|
||||||
|
|
||||||
|
public WindowShowState ShowState
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == field)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Window != IntPtr.Zero)
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case WindowShowState.Normal: SDL.RestoreWindow(Window); break;
|
||||||
|
case WindowShowState.Minimized: SDL.MinimizeWindow(Window); break;
|
||||||
|
case WindowShowState.Maximized: SDL.MaximizeWindow(Window); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowShowState previousShowState = field;
|
||||||
|
field = value;
|
||||||
|
OnShowStateChanged.Invoke(this, new(previousShowState));
|
||||||
|
}
|
||||||
|
} = WindowShowState.Normal;
|
||||||
|
|
||||||
|
public WindowResizeMode ResizeMode
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == field)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Window != IntPtr.Zero)
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case WindowResizeMode.Fixed:
|
||||||
|
SDL.SetWindowResizable(Window, false);
|
||||||
|
break;
|
||||||
|
case WindowResizeMode.Resizable:
|
||||||
|
case WindowResizeMode.AspectLocked:
|
||||||
|
SDL.SetWindowResizable(Window, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowResizeMode previousResizeMode = field;
|
||||||
|
field = value;
|
||||||
|
OnResizeModeChanged.Invoke(this, new(previousResizeMode));
|
||||||
|
}
|
||||||
|
} = WindowResizeMode.Fixed;
|
||||||
|
|
||||||
|
public ColorRGB BackgroundColor
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == field)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ColorRGB previousBackgroundColor = field;
|
||||||
|
field = value;
|
||||||
|
OnBackgroundColorChanged.Invoke(this, new(previousBackgroundColor));
|
||||||
|
}
|
||||||
|
} = new ColorRGB(35, 20, 35);
|
||||||
|
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == field)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string previousTitle = field;
|
||||||
|
field = value;
|
||||||
|
OnTitleChanged.Invoke(this, new(previousTitle));
|
||||||
|
}
|
||||||
|
} = nameof(Sdl3Window);
|
||||||
|
|
||||||
|
public Vector2DInt Size
|
||||||
|
{
|
||||||
|
get => field;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == field)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (value.Max(Vector2DInt.One) == Vector2DInt.One)
|
||||||
|
throw new Exception($"Window size can not be smaller than 1x1");
|
||||||
|
|
||||||
|
Vector2DInt previousSize = field;
|
||||||
|
field = value;
|
||||||
|
OnSizeChanged.Invoke(this, new(previousSize));
|
||||||
|
}
|
||||||
|
} = Vector2DInt.Zero;
|
||||||
|
|
||||||
|
public void ExitUniverse(IUniverse universe) => CloseWindow();
|
||||||
|
public void EnterUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
if (State == WindowState.Open)
|
||||||
|
OpenWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenWindow()
|
||||||
|
{
|
||||||
|
if (!UniverseObject.IsInUniverse)
|
||||||
|
throw new Exception($"{UniverseObject.Name} is not in a universe. The window can not be opened.");
|
||||||
|
|
||||||
|
SDL.WindowFlags windowFlags = 0;
|
||||||
|
switch (Mode)
|
||||||
|
{
|
||||||
|
case WindowMode.Windowed: break;
|
||||||
|
case WindowMode.Fullscreen: windowFlags |= SDL.WindowFlags.Fullscreen; break;
|
||||||
|
case WindowMode.BorderlessWindowed: windowFlags |= SDL.WindowFlags.Borderless; break;
|
||||||
|
case WindowMode.BorderlessFullscreen: windowFlags |= SDL.WindowFlags.Fullscreen | SDL.WindowFlags.Borderless; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SDL.Init(SDL.InitFlags.Video))
|
||||||
|
throw new Exception($"SDL_Init failed: {SDL.GetError()}");
|
||||||
|
|
||||||
|
Window = SDL.CreateWindow(Title, Size.X, Size.Y, windowFlags | SDL.WindowFlags.OpenGL);
|
||||||
|
if (Window == IntPtr.Zero)
|
||||||
|
throw new Exception($"SDL_CreateWindowAndRenderer failed: {SDL.GetError()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseWindow()
|
||||||
|
{
|
||||||
|
if (Window != IntPtr.Zero) SDL.DestroyWindow(Window);
|
||||||
|
|
||||||
|
Window = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
CloseWindow();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Engine.Core;
|
||||||
|
|
||||||
|
using SDL3;
|
||||||
|
|
||||||
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public class Sdl3WindowManager : Behaviour, IUpdate, IEnterUniverse, IExitUniverse
|
||||||
|
{
|
||||||
|
private readonly BehaviourCollector<Sdl3Window> windows = new();
|
||||||
|
private readonly BehaviourCollector<ISdl3EventProcessor> eventProcessors = new();
|
||||||
|
private readonly Dictionary<nint, Sdl3Window> windowsDictionary = [];
|
||||||
|
|
||||||
|
public Sdl3WindowManager()
|
||||||
|
{
|
||||||
|
windows.OnCollected.AddListener(OnWindowCollected);
|
||||||
|
windows.OnRemoved.AddListener(OnWindowRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWindowCollected(IBehaviourCollector<Sdl3Window> sender, IBehaviourCollector<Sdl3Window>.BehaviourCollectedArguments args)
|
||||||
|
=> windowsDictionary.Add(args.BehaviourCollected.Window, args.BehaviourCollected);
|
||||||
|
|
||||||
|
private void OnWindowRemoved(IBehaviourCollector<Sdl3Window> sender, IBehaviourCollector<Sdl3Window>.BehaviourRemovedArguments args)
|
||||||
|
{
|
||||||
|
windowsDictionary.Remove(args.BehaviourRemoved.Window);
|
||||||
|
if (windowsDictionary.Count == 0)
|
||||||
|
SDL.Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnterUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
windows.Assign(universe);
|
||||||
|
eventProcessors.Assign(universe);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitUniverse(IUniverse universe)
|
||||||
|
{
|
||||||
|
windows.Unassign();
|
||||||
|
eventProcessors.Unassign();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
while (SDL.PollEvent(out SDL.Event e))
|
||||||
|
ProcessInternalEvents(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessInternalEvents(SDL.Event sdlEvent)
|
||||||
|
{
|
||||||
|
if (!windowsDictionary.TryGetValue(SDL.GetWindowFromID(sdlEvent.Window.WindowID), out Sdl3Window? window))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < eventProcessors.Count; i++)
|
||||||
|
eventProcessors[i].Process(sdlEvent);
|
||||||
|
|
||||||
|
SDL.EventType eventType = (SDL.EventType)sdlEvent.Type;
|
||||||
|
|
||||||
|
switch (eventType)
|
||||||
|
{
|
||||||
|
case SDL.EventType.WindowRestored: window.ShowState = WindowShowState.Normal; break;
|
||||||
|
case SDL.EventType.WindowMaximized: window.ShowState = WindowShowState.Maximized; break;
|
||||||
|
case SDL.EventType.WindowMinimized: window.ShowState = WindowShowState.Minimized; break;
|
||||||
|
|
||||||
|
case SDL.EventType.WindowFocusGained: window.FocusState = WindowFocusState.Focused; break;
|
||||||
|
case SDL.EventType.WindowFocusLost: window.FocusState = WindowFocusState.Unfocused; break;
|
||||||
|
|
||||||
|
case SDL.EventType.WindowCloseRequested:
|
||||||
|
case SDL.EventType.Quit: Universe.Remove(window.UniverseObject); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using Engine.Core;
|
||||||
|
|
||||||
|
namespace Engine.Integration.SDL3;
|
||||||
|
|
||||||
|
public interface IWindow : IBehaviour
|
||||||
|
{
|
||||||
|
Event<IWindow, StateChangedArguments> OnStateChanged { get; }
|
||||||
|
Event<IWindow, FocusStateChangedArguments> OnFocusStateChanged { get; }
|
||||||
|
Event<IWindow, ModeChangedArguments> OnModeChanged { get; }
|
||||||
|
Event<IWindow, ShowStateChangedArguments> OnShowStateChanged { get; }
|
||||||
|
Event<IWindow, ResizeModeChangedArguments> OnResizeModeChanged { get; }
|
||||||
|
|
||||||
|
Event<IWindow, TitleChangedArguments> OnTitleChanged { get; }
|
||||||
|
Event<IWindow, TargetFpsChangedArguments> OnTargetFpsChanged { get; }
|
||||||
|
Event<IWindow, SizeChangedArguments> OnSizeChanged { get; }
|
||||||
|
Event<IWindow, BackgroundColorChangedArguments> OnBackgroundColorChanged { get; }
|
||||||
|
|
||||||
|
WindowState State { get; set; }
|
||||||
|
WindowFocusState FocusState { get; }
|
||||||
|
WindowMode Mode { get; set; }
|
||||||
|
WindowShowState ShowState { get; set; }
|
||||||
|
WindowResizeMode ResizeMode { get; set; }
|
||||||
|
|
||||||
|
string Title { get; set; }
|
||||||
|
Vector2DInt Size { get; set; }
|
||||||
|
ColorRGB BackgroundColor { get; set; }
|
||||||
|
|
||||||
|
readonly record struct StateChangedArguments(WindowState PreviousState);
|
||||||
|
readonly record struct FocusStateChangedArguments(WindowFocusState PreviousFocusState);
|
||||||
|
readonly record struct ModeChangedArguments(WindowMode PreviousMode);
|
||||||
|
readonly record struct ShowStateChangedArguments(WindowShowState PreviousShowState);
|
||||||
|
readonly record struct ResizeModeChangedArguments(WindowResizeMode PreviousResizeMode);
|
||||||
|
readonly record struct TitleChangedArguments(string PreviousTitle);
|
||||||
|
readonly record struct TargetFpsChangedArguments(uint PreviousTargetFps);
|
||||||
|
readonly record struct SizeChangedArguments(Vector2DInt PreviousSize);
|
||||||
|
readonly record struct BackgroundColorChangedArguments(ColorRGB PreviousColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WindowFocusState
|
||||||
|
{
|
||||||
|
Focused,
|
||||||
|
Unfocused
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WindowState
|
||||||
|
{
|
||||||
|
Open,
|
||||||
|
Closed
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WindowMode
|
||||||
|
{
|
||||||
|
Windowed,
|
||||||
|
Fullscreen,
|
||||||
|
BorderlessFullscreen,
|
||||||
|
BorderlessWindowed
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WindowShowState
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Minimized,
|
||||||
|
Maximized
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WindowResizeMode
|
||||||
|
{
|
||||||
|
Fixed,
|
||||||
|
Resizable,
|
||||||
|
AspectLocked
|
||||||
|
}
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
using Microsoft.Xna.Framework.Graphics;
|
using System;
|
||||||
|
|
||||||
using Engine.Core;
|
using Engine.Core;
|
||||||
using Engine.Core.Debug;
|
using Engine.Core.Debug;
|
||||||
using Engine.Core.Serialization;
|
using Engine.Core.Serialization;
|
||||||
using Engine.Integration.MonoGame;
|
using Engine.Integration.SDL3;
|
||||||
using Engine.Serializers.Yaml;
|
using Engine.Serializers.Yaml;
|
||||||
using Engine.Systems.Network;
|
using Engine.Systems.Network;
|
||||||
|
using Engine.Systems.Time;
|
||||||
using MyUniverse.Platforms.Desktop;
|
using MyUniverse.Shared.Behaviours;
|
||||||
|
|
||||||
Universe universe = new();
|
Universe universe = new();
|
||||||
using MonoGameWindow monoGameWindow = new(universe);
|
|
||||||
|
|
||||||
ISerializer serializer = new YamlSerializer();
|
ISerializer serializer = new YamlSerializer();
|
||||||
|
|
||||||
@@ -25,12 +24,11 @@ universe.InstantiateUniverseObject().SetUniverseObject("Logger")
|
|||||||
|
|
||||||
IUniverseObject desktopParent = universe.InstantiateUniverseObject().SetUniverseObject("Desktop");
|
IUniverseObject desktopParent = universe.InstantiateUniverseObject().SetUniverseObject("Desktop");
|
||||||
|
|
||||||
universe.InstantiateUniverseObject().SetUniverseObject("Desktop Inputs", desktopParent)
|
// universe.InstantiateUniverseObject().SetUniverseObject("Desktop Inputs", desktopParent)
|
||||||
.BehaviourController.AddBehaviour<DesktopInputs>();
|
// .BehaviourController.AddBehaviour<DesktopInputs>();
|
||||||
|
|
||||||
universe.InstantiateUniverseObject().SetUniverseObject("Visual Managers", desktopParent)
|
universe.InstantiateUniverseObject().SetUniverseObject("Visual Managers", desktopParent)
|
||||||
.BehaviourController.AddBehaviour<DrawManager>()
|
.BehaviourController.AddBehaviour<DrawManager>();
|
||||||
.BehaviourController.AddBehaviour<LoadContentManager>();
|
|
||||||
|
|
||||||
/* For Networking
|
/* For Networking
|
||||||
LiteNetLibClient client = universe.InstantiateUniverseObject().SetUniverseObject("Client").BehaviourController.AddBehaviour<LiteNetLibClient>();
|
LiteNetLibClient client = universe.InstantiateUniverseObject().SetUniverseObject("Client").BehaviourController.AddBehaviour<LiteNetLibClient>();
|
||||||
@@ -38,9 +36,102 @@ client.BehaviourController.AddBehaviour<NetworkManager>();
|
|||||||
universe.OnPreUpdate.AddOneTimeListener((_, _) => client.Connect("localhost", 8888));
|
universe.OnPreUpdate.AddOneTimeListener((_, _) => client.Connect("localhost", 8888));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
MyUniverse.Shared.UniverseSource.ApplyClient(universe);
|
MyUniverse.Shared.UniverseSource.ApplyCore(universe);
|
||||||
MyUniverse.Shared.UniverseSource.ApplyUniverse(universe);
|
MyUniverse.Shared.UniverseSource.ApplyUniverse(universe);
|
||||||
|
|
||||||
monoGameWindow.Graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
desktopParent.AddChild(universe.InstantiateUniverseObject().SetUniverseObject("Triangle Batcher").BehaviourController.AddBehaviour<Engine.Systems.Graphics.TriangleBatcher>().UniverseObject);
|
||||||
|
|
||||||
monoGameWindow.Run();
|
Sdl3Window window = Engine.Core.Factory.BehaviourFactory.Instantiate<Sdl3Window>();
|
||||||
|
window.Title = "MyUniverse";
|
||||||
|
window.Size = new(800, 600);
|
||||||
|
window.Mode = WindowMode.Windowed;
|
||||||
|
|
||||||
|
Sdl3Window window2 = Engine.Core.Factory.BehaviourFactory.Instantiate<Sdl3Window>();
|
||||||
|
window2.Title = "MyUniverse 2";
|
||||||
|
window2.Size = new(400, 300);
|
||||||
|
window2.Mode = WindowMode.Windowed;
|
||||||
|
|
||||||
|
universe.InstantiateUniverseObject().SetUniverseObject("SDL3 Manager", desktopParent)
|
||||||
|
.BehaviourController.AddBehaviour<Sdl3WindowManager>()
|
||||||
|
.BehaviourController.AddBehaviour<Sdl3KeyboardInputs>()
|
||||||
|
.BehaviourController.AddBehaviour<Sdl3GameInputs>();
|
||||||
|
|
||||||
|
IUniverseObject windowUO = universe.InstantiateUniverseObject().SetUniverseObject("SDL3 Window", desktopParent);
|
||||||
|
windowUO.BehaviourController.AddBehaviour<Transform2D>();
|
||||||
|
windowUO.BehaviourController.AddBehaviour<Engine.Systems.Graphics.DrawableShape2D>(Shape2D.Square, new ColorRGB(0, 0, 128));
|
||||||
|
windowUO.BehaviourController.AddBehaviour(window);
|
||||||
|
windowUO.BehaviourController.AddBehaviour<Sdl3Camera2D>().Zoom = 20f;
|
||||||
|
windowUO.BehaviourController.AddBehaviour<Sdl3TriangleBatch>();
|
||||||
|
windowUO.BehaviourController.AddBehaviour<CameraController2D>();
|
||||||
|
|
||||||
|
IUniverseObject windowUO2 = universe.InstantiateUniverseObject().SetUniverseObject("SDL3 Window 2", desktopParent);
|
||||||
|
windowUO2.BehaviourController.AddBehaviour<Transform2D>().Rotation = 45f;
|
||||||
|
windowUO2.BehaviourController.AddBehaviour<Engine.Systems.Graphics.DrawableShape2D>(Shape2D.Square, new ColorRGB(128, 0, 0));
|
||||||
|
windowUO2.BehaviourController.AddBehaviour(window2);
|
||||||
|
windowUO2.BehaviourController.AddBehaviour<Sdl3Camera2D>().Zoom = 20f;
|
||||||
|
windowUO2.BehaviourController.AddBehaviour<Sdl3TriangleBatch>();
|
||||||
|
windowUO2.BehaviourController.AddBehaviour<LinearRotator>();
|
||||||
|
|
||||||
|
// Testing Start
|
||||||
|
// TickerStopwatch tickerStopwatch = windowUO.BehaviourController.AddBehaviour<TickerStopwatch>();
|
||||||
|
// tickerStopwatch.Period = 5f;
|
||||||
|
// tickerStopwatch.OnTick.AddListener(sender =>
|
||||||
|
// {
|
||||||
|
// if (window.IsInitialized)
|
||||||
|
// windowUO.BehaviourController.RemoveBehaviour(window);
|
||||||
|
// else
|
||||||
|
// windowUO.BehaviourController.AddBehaviour(window);
|
||||||
|
// });
|
||||||
|
// tickerStopwatch.Start();
|
||||||
|
|
||||||
|
// universe.InstantiateUniverseObject()
|
||||||
|
// .SetUniverseObject("Square")
|
||||||
|
// .BehaviourController.AddBehaviour<Transform2D>()
|
||||||
|
// .BehaviourController.AddBehaviour<Engine.Systems.Graphics.DrawableShape2D>(Shape2D.Square, new ColorRGB(255, 255, 0));
|
||||||
|
|
||||||
|
// Testing End
|
||||||
|
|
||||||
|
universe.Initialize();
|
||||||
|
|
||||||
|
// window.OnRender.AddListener((sender, args) => universe.Draw());
|
||||||
|
// window.OnUpdate.AddListener((sender, args) =>
|
||||||
|
// {
|
||||||
|
// timeSinceStart += args.TimeSpan;
|
||||||
|
// universe.Update(new(timeSinceStart, args.TimeSpan));
|
||||||
|
// });
|
||||||
|
|
||||||
|
// window.Run();
|
||||||
|
|
||||||
|
DateTime lastRun = DateTime.UtcNow;
|
||||||
|
TimeSpan timeSinceStart = new(0);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
DateTime now = DateTime.UtcNow;
|
||||||
|
TimeSpan updateTimeSpan = now - lastRun;
|
||||||
|
|
||||||
|
timeSinceStart += updateTimeSpan;
|
||||||
|
universe.Update(new(timeSinceStart, updateTimeSpan));
|
||||||
|
universe.Draw();
|
||||||
|
|
||||||
|
lastRun = now;
|
||||||
|
System.Threading.Thread.Sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DateTime lastRun = DateTime.UtcNow;
|
||||||
|
// TimeSpan interval = new(0, 0, 0, 0, 16);
|
||||||
|
// TimeSpan timeSinceStart = new(0);
|
||||||
|
|
||||||
|
// universe.Initialize();
|
||||||
|
|
||||||
|
// while (true)
|
||||||
|
// {
|
||||||
|
// if (lastRun + interval <= DateTime.UtcNow)
|
||||||
|
// {
|
||||||
|
// lastRun += interval;
|
||||||
|
// timeSinceStart += interval;
|
||||||
|
// universe.Update(new(timeSinceStart, interval));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Thread.Sleep(1);
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
using Microsoft.Xna.Framework.Input;
|
|
||||||
|
|
||||||
using Engine.Core;
|
|
||||||
using Engine.Integration.MonoGame;
|
|
||||||
|
|
||||||
namespace MyUniverse.Shared.Behaviours;
|
|
||||||
|
|
||||||
public class MousePositionerBehaviour : Behaviour2D, IUpdate, IFirstFrameUpdate
|
|
||||||
{
|
|
||||||
private ICamera2D camera = null!;
|
|
||||||
|
|
||||||
public void FirstActiveFrame()
|
|
||||||
{
|
|
||||||
camera = Universe.FindRequiredBehaviour<ICamera2D>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
Transform.Position = camera.ScreenToWorldPosition(Mouse.GetState().Position.ToVector2D());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,5 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../Engine/Engine/Engine.csproj" />
|
<ProjectReference Include="../Engine/Engine/Engine.csproj" />
|
||||||
<ProjectReference Include="../Engine/Engine.Integration/Engine.Integration.MonoGame/Engine.Integration.MonoGame.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Engine.Core;
|
using Engine.Core;
|
||||||
using Engine.Integration.MonoGame;
|
|
||||||
using Engine.Systems.Graphics;
|
using Engine.Systems.Graphics;
|
||||||
using Engine.Systems.Tween;
|
using Engine.Systems.Tween;
|
||||||
|
|
||||||
@@ -19,33 +18,11 @@ public static class UniverseSource
|
|||||||
coreManagersUO.AddChild(universe.InstantiateUniverseObject().SetUniverseObject("Tween Manager").BehaviourController.AddBehaviour<TweenManager>().UniverseObject);
|
coreManagersUO.AddChild(universe.InstantiateUniverseObject().SetUniverseObject("Tween Manager").BehaviourController.AddBehaviour<TweenManager>().UniverseObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ApplyClient(IUniverse universe)
|
|
||||||
{
|
|
||||||
ApplyCore(universe);
|
|
||||||
|
|
||||||
IUniverseObject clientManagersUO = universe.InstantiateUniverseObject().SetUniverseObject("Core Managers");
|
|
||||||
clientManagersUO.AddChild(universe.InstantiateUniverseObject().SetUniverseObject("Triangle Batcher").BehaviourController.AddBehaviour<TriangleBatcher>().UniverseObject);
|
|
||||||
clientManagersUO.AddChild(universe.InstantiateUniverseObject().SetUniverseObject("Sprite Batcher").BehaviourController.AddBehaviour<SpriteBatcher>().UniverseObject);
|
|
||||||
clientManagersUO.AddChild(universe.InstantiateUniverseObject().SetUniverseObject("MonoGame Triangle Batch").BehaviourController.AddBehaviour<MonoGameTriangleBatch>().UniverseObject);
|
|
||||||
|
|
||||||
MonoGameCamera2D camera = universe.InstantiateUniverseObject().SetUniverseObject("Camera")
|
|
||||||
.BehaviourController.AddBehaviour<Transform2D>()
|
|
||||||
.BehaviourController.AddBehaviour<MonoGameCamera2D>();
|
|
||||||
|
|
||||||
camera.Zoom = 20f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ApplyUniverse(IUniverse universe)
|
public static void ApplyUniverse(IUniverse universe)
|
||||||
{
|
{
|
||||||
universe.InstantiateUniverseObject().SetUniverseObject("Example Object")
|
universe.InstantiateUniverseObject().SetUniverseObject("Example Object")
|
||||||
.BehaviourController.AddBehaviour<ExampleBehaviour>();
|
.BehaviourController.AddBehaviour<ExampleBehaviour>();
|
||||||
|
|
||||||
universe.InstantiateUniverseObject().SetUniverseObject("MouseObject")
|
|
||||||
.BehaviourController.AddBehaviour<Transform2D>()
|
|
||||||
.BehaviourController.AddBehaviour<LinearRotator>()
|
|
||||||
.BehaviourController.AddBehaviour<MousePositionerBehaviour>()
|
|
||||||
.BehaviourController.AddBehaviour<DrawableShape2D>(Shape2D.CreateNgon(4), new ColorRGB(255, 255, 0)).Priority = 10;
|
|
||||||
|
|
||||||
universe.InstantiateUniverseObject()
|
universe.InstantiateUniverseObject()
|
||||||
.SetUniverseObject("Rotating Triangle")
|
.SetUniverseObject("Rotating Triangle")
|
||||||
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new(10f, 6f), scale: Vector2D.One * 5)
|
.BehaviourController.AddBehaviour<Transform2D>().SetTransform(position: new(10f, 6f), scale: Vector2D.One * 5)
|
||||||
|
|||||||
Reference in New Issue
Block a user