From 6e903c268f6d915d370e418cca6da415f8cd9a50 Mon Sep 17 00:00:00 2001 From: ww-rm Date: Fri, 28 Feb 2025 17:02:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A2=84=E8=A7=88=E7=94=BB?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SpineViewer/src/MainForm.Designer.cs | 68 +++-- SpineViewer/src/MainForm.cs | 42 ++- SpineViewer/src/MainForm.resx | 10 +- SpineViewer/src/Spine/Spine.cs | 20 +- SpineViewer/src/SpinePreviewer.Designer.cs | 67 +++++ SpineViewer/src/SpinePreviewer.cs | 288 +++++++++++++++++++++ SpineViewer/src/SpinePreviewer.resx | 123 +++++++++ 7 files changed, 561 insertions(+), 57 deletions(-) create mode 100644 SpineViewer/src/SpinePreviewer.Designer.cs create mode 100644 SpineViewer/src/SpinePreviewer.cs create mode 100644 SpineViewer/src/SpinePreviewer.resx diff --git a/SpineViewer/src/MainForm.Designer.cs b/SpineViewer/src/MainForm.Designer.cs index b610cb3..de9fcb9 100644 --- a/SpineViewer/src/MainForm.Designer.cs +++ b/SpineViewer/src/MainForm.Designer.cs @@ -49,13 +49,11 @@ splitContainer_Config = new SplitContainer(); groupBox_SkelConfig = new GroupBox(); groupBox_PreviewConfig = new GroupBox(); + propertyGrid_Previewer = new PropertyGrid(); groupBox_Preview = new GroupBox(); - panel_PreviewContainer = new Panel(); - panel_Preview = new Panel(); + spinePreviewer = new SpinePreviewer(); panel_MainForm = new Panel(); - openFileDialog_Skel = new OpenFileDialog(); - openFileDialog_Atlas = new OpenFileDialog(); - toolTip1 = new ToolTip(components); + toolTip = new ToolTip(components); menuStrip.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)splitContainer_MainForm).BeginInit(); splitContainer_MainForm.Panel1.SuspendLayout(); @@ -75,8 +73,8 @@ splitContainer_Config.Panel2.SuspendLayout(); splitContainer_Config.SuspendLayout(); groupBox_SkelConfig.SuspendLayout(); + groupBox_PreviewConfig.SuspendLayout(); groupBox_Preview.SuspendLayout(); - panel_PreviewContainer.SuspendLayout(); panel_MainForm.SuspendLayout(); SuspendLayout(); // @@ -266,6 +264,7 @@ propertyGrid_Skel.Size = new Size(369, 506); propertyGrid_Skel.TabIndex = 0; propertyGrid_Skel.ToolbarVisible = false; + propertyGrid_Skel.PropertyValueChanged += propertyGrid_PropertyValueChanged; // // splitContainer_Config // @@ -304,6 +303,7 @@ // // groupBox_PreviewConfig // + groupBox_PreviewConfig.Controls.Add(propertyGrid_Previewer); groupBox_PreviewConfig.Dock = DockStyle.Fill; groupBox_PreviewConfig.Location = new Point(0, 0); groupBox_PreviewConfig.Name = "groupBox_PreviewConfig"; @@ -312,9 +312,20 @@ groupBox_PreviewConfig.TabStop = false; groupBox_PreviewConfig.Text = "画面参数"; // + // propertyGrid_Previewer + // + propertyGrid_Previewer.Dock = DockStyle.Fill; + propertyGrid_Previewer.HelpVisible = false; + propertyGrid_Previewer.Location = new Point(3, 26); + propertyGrid_Previewer.Name = "propertyGrid_Previewer"; + propertyGrid_Previewer.Size = new Size(369, 227); + propertyGrid_Previewer.TabIndex = 1; + propertyGrid_Previewer.ToolbarVisible = false; + propertyGrid_Previewer.PropertyValueChanged += propertyGrid_PropertyValueChanged; + // // groupBox_Preview // - groupBox_Preview.Controls.Add(panel_PreviewContainer); + groupBox_Preview.Controls.Add(spinePreviewer); groupBox_Preview.Dock = DockStyle.Fill; groupBox_Preview.Location = new Point(0, 0); groupBox_Preview.Name = "groupBox_Preview"; @@ -323,23 +334,14 @@ groupBox_Preview.TabStop = false; groupBox_Preview.Text = "预览画面"; // - // panel_PreviewContainer + // spinePreviewer // - panel_PreviewContainer.Controls.Add(panel_Preview); - panel_PreviewContainer.Dock = DockStyle.Fill; - panel_PreviewContainer.Location = new Point(3, 26); - panel_PreviewContainer.Margin = new Padding(0); - panel_PreviewContainer.Name = "panel_PreviewContainer"; - panel_PreviewContainer.Size = new Size(986, 766); - panel_PreviewContainer.TabIndex = 1; - // - // panel_Preview - // - panel_Preview.BackColor = SystemColors.ControlDark; - panel_Preview.Location = new Point(107, 95); - panel_Preview.Name = "panel_Preview"; - panel_Preview.Size = new Size(256, 256); - panel_Preview.TabIndex = 0; + spinePreviewer.Dock = DockStyle.Fill; + spinePreviewer.Location = new Point(3, 26); + spinePreviewer.Name = "spinePreviewer"; + spinePreviewer.Size = new Size(986, 766); + spinePreviewer.TabIndex = 0; + spinePreviewer.RenderFrame += spinePreviewer_RenderFrame; // // panel_MainForm // @@ -351,17 +353,9 @@ panel_MainForm.Size = new Size(1741, 973); panel_MainForm.TabIndex = 4; // - // openFileDialog_Skel + // toolTip // - openFileDialog_Skel.AddExtension = false; - openFileDialog_Skel.AddToRecent = false; - openFileDialog_Skel.Filter = "skel 文件 (*.skel; *.json)|*.skel;*.json"; - // - // openFileDialog_Atlas - // - openFileDialog_Atlas.AddExtension = false; - openFileDialog_Atlas.AddToRecent = false; - openFileDialog_Atlas.Filter = "atlas 文件 (*.atlas)|*.atlas"; + toolTip.ShowAlways = true; // // MainForm // @@ -395,8 +389,8 @@ ((System.ComponentModel.ISupportInitialize)splitContainer_Config).EndInit(); splitContainer_Config.ResumeLayout(false); groupBox_SkelConfig.ResumeLayout(false); + groupBox_PreviewConfig.ResumeLayout(false); groupBox_Preview.ResumeLayout(false); - panel_PreviewContainer.ResumeLayout(false); panel_MainForm.ResumeLayout(false); ResumeLayout(false); PerformLayout(); @@ -424,12 +418,12 @@ private ToolStripMenuItem toolStripMenuItem_About; private ToolStripMenuItem toolStripMenuItem_BatchOpen; private GroupBox groupBox_Preview; - private Panel panel_Preview; - private Panel panel_PreviewContainer; private OpenFileDialog openFileDialog_Skel; private OpenFileDialog openFileDialog_Atlas; - private ToolTip toolTip1; + private ToolTip toolTip; private PropertyGrid propertyGrid_Skel; private SpineListView spineListView; + private PropertyGrid propertyGrid_Previewer; + private SpinePreviewer spinePreviewer; } } diff --git a/SpineViewer/src/MainForm.cs b/SpineViewer/src/MainForm.cs index 48a1269..5b1caad 100644 --- a/SpineViewer/src/MainForm.cs +++ b/SpineViewer/src/MainForm.cs @@ -10,7 +10,11 @@ namespace SpineViewer public MainForm() { InitializeComponent(); + propertyGrid_Previewer.SelectedObject = spinePreviewer.Property; + InitializeLogConfiguration(); + spinePreviewer.Property.Resolution = new(1280, 720); + spinePreviewer.StartPreview(); } /// @@ -31,10 +35,10 @@ namespace SpineViewer }; rtbTarget.WordColoringRules.Add(new("[D]", "Gray", "Empty", FontStyle.Bold)); - rtbTarget.WordColoringRules.Add(new("[I]", "Gray", "Empty", FontStyle.Bold)); + rtbTarget.WordColoringRules.Add(new("[I]", "DimGray", "Empty", FontStyle.Bold)); rtbTarget.WordColoringRules.Add(new("[W]", "DarkOrange", "Empty", FontStyle.Bold)); rtbTarget.WordColoringRules.Add(new("[E]", "Red", "Empty", FontStyle.Bold)); - rtbTarget.WordColoringRules.Add(new("[F]", "Red", "Empty", FontStyle.Bold)); + rtbTarget.WordColoringRules.Add(new("[F]", "DarkRed", "Empty", FontStyle.Bold)); LogManager.Configuration.AddTarget(rtbTarget); LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Fatal, rtbTarget); @@ -55,8 +59,11 @@ namespace SpineViewer private void toolStripMenuItem_Export_Click(object sender, EventArgs e) { - var a = new SizeF(10, 100); - spineListView.Spines[0].Position = spineListView.Spines[0].Position + a; + Program.Logger.Debug("Debug Test"); + Program.Logger.Info("Info Test"); + Program.Logger.Warn("Warn Test"); + Program.Logger.Error("Error Test"); + Program.Logger.Fatal("Fatal Test"); } private void toolStripMenuItem_Exit_Click(object sender, EventArgs e) @@ -70,6 +77,26 @@ namespace SpineViewer #endregion #region Ԥ + + private void spinePreviewer_RenderFrame(object sender, RenderFrameEventArgs e) + { + var target = e.RenderTarget; + var delta = e.Delta; + Spine.Spine[] spines = null; + + // Ҫڿؼ߳õdz + if (spineListView.InvokeRequired) + spineListView.Invoke(() => spines = spineListView.Spines.ToArray()); + else + spines = spineListView.Spines.ToArray(); + + foreach (var spine in spines.Reverse()) + { + spine.Update(delta); + target.Draw(spine); + } + } + #endregion #region @@ -83,7 +110,12 @@ namespace SpineViewer { ActiveControl = null; } - + + private void propertyGrid_PropertyValueChanged(object sender, PropertyValueChangedEventArgs e) + { + (sender as PropertyGrid).Refresh(); + } + #endregion } } diff --git a/SpineViewer/src/MainForm.resx b/SpineViewer/src/MainForm.resx index a73caa7..0df7782 100644 --- a/SpineViewer/src/MainForm.resx +++ b/SpineViewer/src/MainForm.resx @@ -120,14 +120,8 @@ 17, 17 - - 156, 25 - - - 404, 23 - - - 651, 23 + + 187, 20 89 diff --git a/SpineViewer/src/Spine/Spine.cs b/SpineViewer/src/Spine/Spine.cs index 4d87178..3307bd8 100644 --- a/SpineViewer/src/Spine/Spine.cs +++ b/SpineViewer/src/Spine/Spine.cs @@ -157,33 +157,33 @@ namespace SpineViewer.Spine /// /// 缩放比例 /// - [Category("空间变换"), DisplayName("缩放比例")] + [Category("变换"), DisplayName("缩放比例")] public abstract float Scale { get; set; } /// /// 位置 /// [TypeConverter(typeof(PointFTypeConverter))] - [Category("空间变换"), DisplayName("位置")] + [Category("变换"), DisplayName("位置")] public abstract PointF Position { get; set; } /// /// 水平翻转 /// - [Category("空间变换"), DisplayName("水平翻转")] + [Category("变换"), DisplayName("水平翻转")] public abstract bool FlipX { get; set; } /// /// 垂直翻转 /// - [Category("空间变换"), DisplayName("垂直翻转")] + [Category("变换"), DisplayName("垂直翻转")] public abstract bool FlipY { get; set; } /// /// 是否使用预乘Alpha /// - [Category("其他"), DisplayName("预乘Alpha通道")] - public bool UsePremultipliedAlpha { get; set; } + [Category("画面"), DisplayName("预乘Alpha通道")] + public bool UsePremultipliedAlpha { get; set; } = true; /// /// 包含的所有动画名称 @@ -202,9 +202,15 @@ namespace SpineViewer.Spine /// 当前动画名称 /// [TypeConverter(typeof(AnimationTypeConverter))] - [Category("其他"), DisplayName("当前播放动画"), PropertyTab()] + [Category("动画"), DisplayName("当前动画")] public abstract string CurrentAnimation { get; set; } + /// + /// 当前动画时长 + /// + [Category("动画"), DisplayName("当前动画时长")] + public float CurrentAnimationDuration { get => GetAnimationDuration(CurrentAnimation); } + /// /// 骨骼包围盒 /// diff --git a/SpineViewer/src/SpinePreviewer.Designer.cs b/SpineViewer/src/SpinePreviewer.Designer.cs new file mode 100644 index 0000000..0e26d8f --- /dev/null +++ b/SpineViewer/src/SpinePreviewer.Designer.cs @@ -0,0 +1,67 @@ +namespace SpineViewer +{ + partial class SpinePreviewer + { + /// + /// 必需的设计器变量。 + /// + private System.ComponentModel.IContainer components = null; + + /// + /// 清理所有正在使用的资源。 + /// + /// 如果应释放托管资源,为 true;否则为 false。 + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region 组件设计器生成的代码 + + /// + /// 设计器支持所需的方法 - 不要修改 + /// 使用代码编辑器修改此方法的内容。 + /// + private void InitializeComponent() + { + panel = new Panel(); + backgroundWorker = new System.ComponentModel.BackgroundWorker(); + SuspendLayout(); + // + // panel + // + panel.BackColor = SystemColors.ControlDark; + panel.Location = new Point(115, 75); + panel.Margin = new Padding(0); + panel.Name = "panel"; + panel.Size = new Size(513, 446); + panel.TabIndex = 1; + panel.MouseDown += panel_MouseDown; + panel.MouseMove += panel_MouseMove; + panel.MouseUp += panel_MouseUp; + // + // backgroundWorker + // + backgroundWorker.DoWork += backgroundWorker_DoWork; + // + // SpinePreviewer + // + AutoScaleDimensions = new SizeF(11F, 24F); + AutoScaleMode = AutoScaleMode.Font; + Controls.Add(panel); + Name = "SpinePreviewer"; + Size = new Size(637, 534); + SizeChanged += SpinePreviewer_SizeChanged; + ResumeLayout(false); + } + + #endregion + + private Panel panel; + private System.ComponentModel.BackgroundWorker backgroundWorker; + } +} diff --git a/SpineViewer/src/SpinePreviewer.cs b/SpineViewer/src/SpinePreviewer.cs new file mode 100644 index 0000000..23bca08 --- /dev/null +++ b/SpineViewer/src/SpinePreviewer.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Security.Policy; + +namespace SpineViewer +{ + public partial class SpinePreviewer : UserControl + { + public class PreviewerProperty + { + public const float ZOOM_MAX = 1000f; + public const float ZOOM_MIN = 0.001f; + + private readonly SFML.Graphics.RenderWindow RenderWindow; + private readonly Control ContainerControl; + + public PreviewerProperty(SFML.Graphics.RenderWindow renderWindow, Control containerControl) + { + RenderWindow = renderWindow; + ContainerControl = containerControl; + } + + /// + /// 导出画面分辨率 + /// + [TypeConverter(typeof(SizeTypeConverter))] + [Category("属性"), DisplayName("分辨率")] + public Size Resolution + { + get => resolution; + set + { + if (value.Width <= 0) value.Width = 100; + if (value.Height <= 0) value.Height = 100; + + float parentX = ContainerControl.Width; + float parentY = ContainerControl.Height; + float sizeX = value.Width; + float sizeY = value.Height; + + if ((sizeY / sizeX) < (parentY / parentX)) + { + // 相同的 X, 子窗口 Y 更小 + sizeY = parentX * sizeY / sizeX; + sizeX = parentX; + } + else + { + // 相同的 Y, 子窗口 X 更小 + sizeX = parentY * sizeX / sizeY; + sizeY = parentY; + } + + // 必须通过 SFML 的方法调整窗口 + RenderWindow.Position = new((int)(parentX - sizeX) / 2, (int)(parentY - sizeY) / 2); + RenderWindow.Size = new((uint)sizeX, (uint)sizeY); + + // 将 view 的大小设置成于 resolution 相同的大小, 其余属性都不变 + var view = RenderWindow.GetView(); + var signX = Math.Sign(view.Size.X); + var signY = Math.Sign(view.Size.Y); + view.Size = new(value.Width * signX, value.Height * signY); + RenderWindow.SetView(view); + + resolution = value; + } + } + private Size resolution = new(0, 0); + + [TypeConverter(typeof(PointFTypeConverter))] + [Category("属性"), DisplayName("画面中心点")] + public PointF Center + { + get + { + var center = RenderWindow.GetView().Center; + return new(center.X, center.Y); + } + set + { + var view = RenderWindow.GetView(); + view.Center = new(value.X, value.Y); + RenderWindow.SetView(view); + } + } + + /// + /// 画面缩放 + /// + [Category("属性"), DisplayName("缩放")] + public float Zoom + { + get => resolution.Width / Math.Abs(RenderWindow.GetView().Size.X); + set + { + value = Math.Clamp(value, ZOOM_MIN, ZOOM_MAX); + var view = RenderWindow.GetView(); + var signX = Math.Sign(view.Size.X); + var signY = Math.Sign(view.Size.Y); + view.Size = new(resolution.Width / value * signX, resolution.Height / value * signY); + RenderWindow.SetView(view); + } + } + + /// + /// 画面旋转 + /// + [Category("属性"), DisplayName("旋转")] + public float Rotation + { + get => RenderWindow.GetView().Rotation; + set + { + var view = RenderWindow.GetView(); + view.Rotation = value; + RenderWindow.SetView(view); + } + } + + /// + /// 画面旋转 + /// + [Category("属性"), DisplayName("水平翻转")] + public bool FlipX + { + get => RenderWindow.GetView().Size.X < 0; + set + { + 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); + } + } + + /// + /// 画面旋转 + /// + [Category("属性"), DisplayName("垂直翻转")] + public bool FlipY + { + get => RenderWindow.GetView().Size.Y < 0; + set + { + 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); + } + } + } + + /// + /// 帧渲染事件 + /// + [Category("自定义"), Description("帧渲染事件")] + public event EventHandler? RenderFrame; + private void OnRenderFrame(float delta) { RenderFrame?.Invoke(this, new(RenderWindow, delta)); } + + private readonly SFML.Graphics.RenderWindow RenderWindow; + private readonly SFML.System.Clock Clock = new(); + private readonly SFML.Graphics.Color BackgroundColor = SFML.Graphics.Color.Green; + + [Browsable(false)] + public PreviewerProperty Property { get; } + + public SpinePreviewer() + { + InitializeComponent(); + RenderWindow = new(panel.Handle); + RenderWindow.SetFramerateLimit(30); + RenderWindow.SetActive(false); + Property = new(RenderWindow, this) + { + Resolution = new(1280, 720), + Center = new(0, 0), + FlipY = true + }; + } + + public void StartPreview() + { + if (!backgroundWorker.IsBusy) + backgroundWorker.RunWorkerAsync(); + } + + public void StopPreview() + { + if (backgroundWorker.IsBusy) + backgroundWorker.CancelAsync(); + } + + private void SpinePreviewer_SizeChanged(object sender, EventArgs e) + { + if (RenderWindow is null) + return; + + float parentX = Width; + float parentY = Height; + float sizeX = panel.Width; + float sizeY = panel.Height; + + if ((sizeY / sizeX) < (parentY / parentX)) + { + // 相同的 X, 子窗口 Y 更小 + sizeY = parentX * sizeY / sizeX; + sizeX = parentX; + } + else + { + // 相同的 Y, 子窗口 X 更小 + sizeX = parentY * sizeX / sizeY; + sizeY = parentY; + } + + // 必须通过 SFML 的方法调整窗口 + RenderWindow.Position = new((int)(parentX - sizeX) / 2, (int)(parentY - sizeY) / 2); + RenderWindow.Size = new((uint)sizeX, (uint)sizeY); + } + + private void panel_MouseDown(object sender, MouseEventArgs e) + { + + } + + private void panel_MouseMove(object sender, MouseEventArgs e) + { + + } + + private void panel_MouseUp(object sender, MouseEventArgs e) + { + + } + + private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) + { + RenderWindow.SetActive(true); + + while (!backgroundWorker.CancellationPending) + { + var delta = Clock.ElapsedTime.AsSeconds(); + Clock.Restart(); + + // TODO: 绘制网格线 + RenderWindow.Clear(BackgroundColor); + + OnRenderFrame(delta); + RenderWindow.Display(); + } + + RenderWindow.SetActive(false); + } + } + + /// + /// RenderFrame 事件参数 + /// + public class RenderFrameEventArgs : EventArgs + { + /// + /// 渲染目标 + /// + public SFML.Graphics.RenderTarget RenderTarget { get; } + + /// + /// 距离上一帧经过的时间, 单位秒 + /// + public float Delta { get; } + + public RenderFrameEventArgs(SFML.Graphics.RenderTarget renderTarget, float delta) + { + RenderTarget = renderTarget; + Delta = delta; + } + } +} diff --git a/SpineViewer/src/SpinePreviewer.resx b/SpineViewer/src/SpinePreviewer.resx new file mode 100644 index 0000000..60ff075 --- /dev/null +++ b/SpineViewer/src/SpinePreviewer.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file