更换为wpf
This commit is contained in:
134
SFMLRenderer/ISFMLRenderer.cs
Normal file
134
SFMLRenderer/ISFMLRenderer.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using SFML.Graphics;
|
||||
using SFML.System;
|
||||
using SFML.Window;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SFMLRenderer
|
||||
{
|
||||
public interface ISFMLRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// 发生在资源首次创建完成后
|
||||
/// </summary>
|
||||
public event EventHandler? RendererCreated;
|
||||
|
||||
/// <summary>
|
||||
/// 发生在资源即将不可用之前
|
||||
/// </summary>
|
||||
public event EventHandler? RendererDisposing;
|
||||
|
||||
public event EventHandler<MouseMoveEventArgs>? CanvasMouseMove;
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasMouseButtonPressed;
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasMouseButtonReleased;
|
||||
public event EventHandler<MouseWheelScrollEventArgs>? CanvasMouseWheelScrolled;
|
||||
|
||||
/// <summary>
|
||||
/// 分辨率, 影响画面的相对比例
|
||||
/// </summary>
|
||||
public Vector2u Resolution { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 快捷设置视区中心点
|
||||
/// </summary>
|
||||
public Vector2f Center { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 快捷设置视区缩放
|
||||
/// </summary>
|
||||
public float Zoom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 快捷设置视区旋转
|
||||
/// </summary>
|
||||
public float Rotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 快捷设置视区水平翻转
|
||||
/// </summary>
|
||||
public bool FlipX { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 快捷设置视区垂直翻转
|
||||
/// </summary>
|
||||
public bool FlipY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大帧率, 影响 Draw 的最大调用频率, <see cref="RenderWindow.SetFramerateLimit(uint)"/>
|
||||
/// </summary>
|
||||
public uint MaxFps { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 垂直同步
|
||||
/// </summary>
|
||||
public bool VerticalSync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.SetActive(bool)"/>
|
||||
/// </summary>
|
||||
public void SetActive(bool active);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.GetView"/>
|
||||
/// </summary>
|
||||
public View GetView();
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.SetView(View)"/>
|
||||
/// </summary>
|
||||
public void SetView(View view);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.MapPixelToCoords(Vector2i)"/>
|
||||
/// </summary>
|
||||
public Vector2f MapPixelToCoords(Vector2i point);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.MapCoordsToPixel(Vector2f)"/>
|
||||
/// </summary>
|
||||
public Vector2i MapCoordsToPixel(Vector2f point);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.Clear()"/>
|
||||
/// </summary>
|
||||
public void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.Clear(Color)"/>
|
||||
/// </summary>
|
||||
public void Clear(Color color);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.Draw(Drawable)"/>
|
||||
/// </summary>
|
||||
public void Draw(Drawable drawable);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.Draw(Drawable, RenderStates)"/>
|
||||
/// </summary>
|
||||
public void Draw(Drawable drawable, RenderStates states);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.Draw(Vertex[], PrimitiveType)"/>
|
||||
/// </summary>
|
||||
public void Draw(Vertex[] vertices, PrimitiveType type);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.Draw(Vertex[], PrimitiveType, RenderStates)"/>
|
||||
/// </summary>
|
||||
public void Draw(Vertex[] vertices, PrimitiveType type, RenderStates states);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.Draw(Vertex[], uint, uint, PrimitiveType)"/>
|
||||
/// </summary>
|
||||
public void Draw(Vertex[] vertices, uint start, uint count, PrimitiveType type);
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="RenderWindow.Display"/>
|
||||
/// </summary>
|
||||
public void Display();
|
||||
}
|
||||
}
|
||||
3
SFMLRenderer/README.md
Normal file
3
SFMLRenderer/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# SFMLRenderer
|
||||
|
||||
这个库封装了一个用于 WPF 的 SFML 渲染控件.
|
||||
60
SFMLRenderer/SFMLHwndHost.cs
Normal file
60
SFMLRenderer/SFMLHwndHost.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace SFMLRenderer
|
||||
{
|
||||
public class SFMLHwndHost : HwndHost
|
||||
{
|
||||
private HwndSource? _hwndSource;
|
||||
private SFML.Graphics.RenderWindow? _renderWindow;
|
||||
|
||||
public SFML.Graphics.RenderWindow? RenderWindow => _renderWindow;
|
||||
|
||||
public event EventHandler? RenderWindowBuilded;
|
||||
public event EventHandler? RenderWindowDestroying;
|
||||
|
||||
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
|
||||
{
|
||||
var ps = new HwndSourceParameters(GetType().Name, (int)Width, (int)Height)
|
||||
{
|
||||
ParentWindow = hwndParent.Handle,
|
||||
WindowStyle = 0x40000000 | 0x10000000, // WS_CHILD | WS_VISIBLE
|
||||
HwndSourceHook = HwndMessageHook
|
||||
};
|
||||
_hwndSource = new HwndSource(ps);
|
||||
_renderWindow = new(_hwndSource.Handle);
|
||||
_renderWindow.SetActive(false);
|
||||
|
||||
RenderWindowBuilded?.Invoke(this, EventArgs.Empty);
|
||||
return new HandleRef(this, _hwndSource.Handle);
|
||||
}
|
||||
|
||||
protected override void DestroyWindowCore(HandleRef hwnd)
|
||||
{
|
||||
RenderWindowDestroying?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
_renderWindow?.Close();
|
||||
var rw = _renderWindow;
|
||||
_renderWindow = null;
|
||||
rw?.Dispose();
|
||||
var hs = _hwndSource;
|
||||
_hwndSource = null;
|
||||
hs?.Dispose();
|
||||
}
|
||||
|
||||
private nint HwndMessageHook(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
|
||||
{
|
||||
_renderWindow?.DispatchEvents();
|
||||
return nint.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
SFMLRenderer/SFMLRenderPanel.xaml
Normal file
14
SFMLRenderer/SFMLRenderPanel.xaml
Normal file
@@ -0,0 +1,14 @@
|
||||
<UserControl x:Class="SFMLRenderer.SFMLRenderPanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:SFMLRenderer"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<local:SFMLHwndHost x:Name="_hwndHost"
|
||||
Width="100"
|
||||
Height="100"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</UserControl>
|
||||
253
SFMLRenderer/SFMLRenderPanel.xaml.cs
Normal file
253
SFMLRenderer/SFMLRenderPanel.xaml.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
using SFML.Graphics;
|
||||
using SFML.System;
|
||||
using SFML.Window;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SFMLRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// SFMLRenderPanel.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class SFMLRenderPanel : System.Windows.Controls.UserControl, ISFMLRenderer
|
||||
{
|
||||
private RenderWindow? RenderWindow => _hwndHost.RenderWindow;
|
||||
|
||||
public SFMLRenderPanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public event EventHandler? RendererCreated
|
||||
{
|
||||
add => _hwndHost.RenderWindowBuilded += value;
|
||||
remove => _hwndHost.RenderWindowBuilded -= value;
|
||||
}
|
||||
|
||||
public event EventHandler? RendererDisposing
|
||||
{
|
||||
add => _hwndHost.RenderWindowDestroying += value;
|
||||
remove => _hwndHost.RenderWindowDestroying -= value;
|
||||
}
|
||||
|
||||
public event EventHandler<MouseMoveEventArgs>? CanvasMouseMove
|
||||
{
|
||||
add { if (RenderWindow is RenderWindow w) w.MouseMoved += value; }
|
||||
remove { if (RenderWindow is RenderWindow w) w.MouseMoved -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasMouseButtonPressed
|
||||
{
|
||||
add { if (RenderWindow is RenderWindow w) w.MouseButtonPressed += value; }
|
||||
remove { if (RenderWindow is RenderWindow w) w.MouseButtonPressed -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasMouseButtonReleased
|
||||
{
|
||||
add { if (RenderWindow is RenderWindow w) w.MouseButtonReleased += value; }
|
||||
remove { if (RenderWindow is RenderWindow w) w.MouseButtonReleased -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseWheelScrollEventArgs>? CanvasMouseWheelScrolled
|
||||
{
|
||||
add { if (RenderWindow is RenderWindow w) w.MouseWheelScrolled += value; }
|
||||
remove { if (RenderWindow is RenderWindow w) w.MouseWheelScrolled -= value; }
|
||||
}
|
||||
|
||||
public Vector2u Resolution
|
||||
{
|
||||
get => _resolution;
|
||||
set
|
||||
{
|
||||
if (RenderWindow is null) return;
|
||||
if (value == _resolution) return;
|
||||
if (value.X <= 0 || value.Y <= 0) return;
|
||||
|
||||
var zoom = Zoom;
|
||||
|
||||
float parentW = (float)ActualWidth;
|
||||
float parentH = (float)ActualHeight;
|
||||
float renderW = value.X;
|
||||
float renderH = value.Y;
|
||||
float scale = Math.Min(parentW / renderW, parentH / renderH); // 两方向取较小值, 保证 parent 覆盖 render
|
||||
renderW *= scale;
|
||||
renderH *= scale;
|
||||
|
||||
_hwndHost.Width = renderW;
|
||||
_hwndHost.Height = renderH;
|
||||
|
||||
_resolution = value;
|
||||
|
||||
// 设置完 resolution 后还原缩放比例
|
||||
Zoom = zoom;
|
||||
}
|
||||
}
|
||||
private Vector2u _resolution = new(100, 100);
|
||||
|
||||
public Vector2f Center
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RenderWindow is null) return default;
|
||||
using var view = RenderWindow.GetView();
|
||||
return view.Center;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (RenderWindow is null) return;
|
||||
using var view = RenderWindow.GetView();
|
||||
view.Center = value;
|
||||
RenderWindow.SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RenderWindow is null) return 1;
|
||||
using var view = RenderWindow.GetView();
|
||||
return Math.Abs(_resolution.X / view.Size.X); // XXX: 仅使用宽度进行缩放计算
|
||||
}
|
||||
set
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
if (RenderWindow is null || value <= 0) return;
|
||||
using var view = RenderWindow.GetView();
|
||||
var signX = Math.Sign(view.Size.X);
|
||||
var signY = Math.Sign(view.Size.Y);
|
||||
view.Size = new(_resolution.X / value * signX, _resolution.Y / value * signY);
|
||||
RenderWindow.SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RenderWindow is null) return default;
|
||||
using var view = RenderWindow.GetView();
|
||||
return view.Rotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (RenderWindow is null) return;
|
||||
using var view = RenderWindow.GetView();
|
||||
view.Rotation = value;
|
||||
RenderWindow.SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FlipX
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RenderWindow is null) return false;
|
||||
using var view = RenderWindow.GetView();
|
||||
return view.Size.X < 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (RenderWindow is null) return;
|
||||
|
||||
using var view = RenderWindow.GetView();
|
||||
var size = view.Size;
|
||||
if (size.X > 0 && value || size.X < 0 && !value)
|
||||
size.X *= -1;
|
||||
view.Size = size;
|
||||
RenderWindow.SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FlipY
|
||||
{
|
||||
get
|
||||
{
|
||||
if (RenderWindow is null) return false;
|
||||
using var view = RenderWindow.GetView();
|
||||
return view.Size.Y < 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (RenderWindow is null) return;
|
||||
|
||||
using var view = RenderWindow.GetView();
|
||||
var size = view.Size;
|
||||
if (size.Y > 0 && value || size.Y < 0 && !value)
|
||||
size.Y *= -1;
|
||||
view.Size = size;
|
||||
RenderWindow.SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public uint MaxFps
|
||||
{
|
||||
get => _maxFps;
|
||||
set
|
||||
{
|
||||
if (RenderWindow is null) return;
|
||||
RenderWindow.SetFramerateLimit(value);
|
||||
_maxFps = value;
|
||||
}
|
||||
}
|
||||
private uint _maxFps = 0;
|
||||
|
||||
public bool VerticalSync
|
||||
{
|
||||
get => _verticalSync;
|
||||
set
|
||||
{
|
||||
if (RenderWindow is null) return;
|
||||
RenderWindow.SetVerticalSyncEnabled(value);
|
||||
_verticalSync = value;
|
||||
}
|
||||
}
|
||||
private bool _verticalSync = false;
|
||||
|
||||
public void Clear() => RenderWindow?.Clear();
|
||||
|
||||
public void Clear(Color color) => RenderWindow?.Clear(color);
|
||||
|
||||
public void Display() => RenderWindow?.Display();
|
||||
|
||||
public void Draw(Drawable drawable) => RenderWindow?.Draw(drawable);
|
||||
|
||||
public void Draw(Drawable drawable, RenderStates states) => RenderWindow?.Draw(drawable, states);
|
||||
|
||||
public void Draw(Vertex[] vertices, PrimitiveType type) => RenderWindow?.Draw(vertices, type);
|
||||
|
||||
public void Draw(Vertex[] vertices, PrimitiveType type, RenderStates states) => RenderWindow?.Draw(vertices, type, states);
|
||||
|
||||
public void Draw(Vertex[] vertices, uint start, uint count, PrimitiveType type) => RenderWindow?.Draw(vertices, start, count, type);
|
||||
|
||||
public View GetView() => RenderWindow?.GetView() ?? new();
|
||||
|
||||
public Vector2i MapCoordsToPixel(Vector2f point) => RenderWindow?.MapCoordsToPixel(point) ?? default;
|
||||
|
||||
public Vector2f MapPixelToCoords(Vector2i point) => RenderWindow?.MapPixelToCoords(point) ?? default;
|
||||
|
||||
public void SetActive(bool active) => RenderWindow?.SetActive(active);
|
||||
|
||||
public void SetView(View view) => RenderWindow?.SetView(view);
|
||||
|
||||
protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo sizeInfo)
|
||||
{
|
||||
base.OnRenderSizeChanged(sizeInfo);
|
||||
|
||||
if (RenderWindow is null) return;
|
||||
float parentW = (float)sizeInfo.NewSize.Width;
|
||||
float parentH = (float)sizeInfo.NewSize.Height;
|
||||
float renderW = (float)_hwndHost.ActualWidth;
|
||||
float renderH = (float)_hwndHost.ActualHeight;
|
||||
float scale = Math.Min(parentW / renderW, parentH / renderH); // 两方向取较小值, 保证 parent 覆盖 render
|
||||
renderW *= scale;
|
||||
renderH *= scale;
|
||||
|
||||
_hwndHost.Width = renderW;
|
||||
_hwndHost.Height = renderH;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
SFMLRenderer/SFMLRenderer.csproj
Normal file
22
SFMLRenderer/SFMLRenderer.csproj
Normal file
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>x64</Platforms>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<Version>0.15.0</Version>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);NETSDK1206</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SFML.Net" Version="2.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user