diff --git a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs index 857212d..6385575 100644 --- a/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs +++ b/SpineViewer/ViewModels/MainWindow/SFMLRendererViewModel.cs @@ -50,7 +50,7 @@ namespace SpineViewer.ViewModels.MainWindow /// /// 坐标轴顶点缓冲区 /// - private readonly SFML.Graphics.VertexArray _axisVertices = new(SFML.Graphics.PrimitiveType.Lines, 2); // XXX: 暂时未使用 Dispose 释放 + private readonly SFML.Graphics.VertexArray _axisVertices = new(SFML.Graphics.PrimitiveType.Lines, 4); // XXX: 暂时未使用 Dispose 释放 /// /// 帧间隔计时器 @@ -87,6 +87,12 @@ namespace SpineViewer.ViewModels.MainWindow _models = _vmMain.SpineObjects; _renderer = _vmMain.SFMLRenderer; _wallpaperRenderer = _vmMain.WallpaperRenderer; + + // 画一个很长的坐标轴, 用 1e9 比较合适 + _axisVertices[0] = new(new(-1e9f, 0), _axisColor); + _axisVertices[1] = new(new(1e9f, 0), _axisColor); + _axisVertices[2] = new(new(0, -1e9f), _axisColor); + _axisVertices[3] = new(new(0, 1e9f), _axisColor); } /// @@ -194,7 +200,7 @@ namespace SpineViewer.ViewModels.MainWindow /// private SFML.Graphics.Color _axisColor = SFML.Graphics.Color.White; - public string BackgroundImagePath + public string? BackgroundImagePath { get => _backgroundImagePath; set => SetProperty(_backgroundImagePath, value, v => @@ -243,7 +249,7 @@ namespace SpineViewer.ViewModels.MainWindow } }); } - private string _backgroundImagePath; + private string? _backgroundImagePath; public Stretch BackgroundImageMode { @@ -475,118 +481,14 @@ namespace SpineViewer.ViewModels.MainWindow _wallpaperRenderer.SetActive(true); _renderer.SetActive(true); - float frameDelta; - float updateDelta; + float delta; while (!_cancelToken?.IsCancellationRequested ?? false) { - updateDelta = frameDelta = _clock.ElapsedTime.AsSeconds(); + delta = _clock.ElapsedTime.AsSeconds(); _clock.Restart(); - // 计算实时帧率, 1 秒刷新一次 - _accumFpsCount++; - _accumFpsTime += frameDelta; - if (_accumFpsTime > 1f) - { - _realTimeFps = _accumFpsCount / _accumFpsTime; - _accumFpsTime = 0f; - _accumFpsCount = 0; - OnPropertyChanged(nameof(RealTimeFps)); - } - - // 停止更新的时候只是时间不前进, 但是坐标变换还是要更新, 否则无法移动对象 - if (!_isUpdating) updateDelta = 0; - - // 加上要快进的量 - lock (_forwardDeltaLock) - { - updateDelta += _forwardDelta; - _forwardDelta = 0; - } - - using var view = _renderer.GetView(); - _wallpaperRenderer.SetView(view); - - if (_vmMain.IsVisible) _renderer.Clear(_backgroundColor); - if (_wallpaperView) _wallpaperRenderer.Clear(_backgroundColor); - - // 渲染背景 - lock (_bgLock) - { - if (_backgroundImageSprite is not null) - { - var bg = _backgroundImageSprite; - var viewSize = view.Size; - var bgSize = bg.Texture.Size; - var scaleX = Math.Abs(viewSize.X / bgSize.X); - var scaleY = Math.Abs(viewSize.Y / bgSize.Y); - var signX = Math.Sign(viewSize.X); - var signY = Math.Sign(viewSize.Y); - if (_backgroundImageMode == Stretch.None) - { - scaleX = scaleY = 1f / _renderer.Zoom; - } - else if (_backgroundImageMode == Stretch.Uniform) - { - scaleX = scaleY = Math.Min(scaleX, scaleY); - } - else if (_backgroundImageMode == Stretch.UniformToFill) - { - scaleX = scaleY = Math.Max(scaleX, scaleY); - } - bg.Scale = new(signX * scaleX, signY * scaleY); - bg.Position = view.Center; - bg.Rotation = view.Rotation; - - if (_vmMain.IsVisible) _renderer.Draw(bg); - if (_wallpaperView) _wallpaperRenderer.Draw(bg); - } - } - - if (_showAxis && _vmMain.IsVisible) - { - // 画一个很长的坐标轴, 用 1e9 比较合适 - _axisVertices[0] = new(new(-1e9f, 0), _axisColor); - _axisVertices[1] = new(new(1e9f, 0), _axisColor); - _renderer.Draw(_axisVertices); - _axisVertices[0] = new(new(0, -1e9f), _axisColor); - _axisVertices[1] = new(new(0, 1e9f), _axisColor); - _renderer.Draw(_axisVertices); - } - - // 渲染 Spine - lock (_models.Lock) - { - foreach (var sp in _models.Where(sp => sp.IsShown && (!_renderSelectedOnly || sp.IsSelected)).Reverse()) - { - if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止 - - sp.Update(0); // 避免物理效果出现问题 - sp.Update(updateDelta * _speed); - - if (_vmMain.IsVisible) - { - // 为选中对象绘制一个半透明背景 - if (sp.IsSelected) - { - var rc = sp.GetCurrentBounds().ToFloatRect(); - _selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor); - _selectedBackgroundVertices[1] = new(new(rc.Left + rc.Width, rc.Top), _selectedBackgroundColor); - _selectedBackgroundVertices[2] = new(new(rc.Left + rc.Width, rc.Top + rc.Height), _selectedBackgroundColor); - _selectedBackgroundVertices[3] = new(new(rc.Left, rc.Top + rc.Height), _selectedBackgroundColor); - _renderer.Draw(_selectedBackgroundVertices); - } - - // 仅在预览画面临时启用调试模式 - sp.EnableDebug = true; - _renderer.Draw(sp); - sp.EnableDebug = false; - } - if (_wallpaperView) _wallpaperRenderer.Draw(sp); - } - } - - if (_vmMain.IsVisible) _renderer.Display(); - if (_wallpaperView) _wallpaperRenderer.Display(); + UpdateLogicFrame(delta); + UpdateRenderFrame(); } } catch (Exception ex) @@ -602,6 +504,136 @@ namespace SpineViewer.ViewModels.MainWindow } } + private void UpdateLogicFrame(float delta) + { + // 计算实时帧率, 1 秒刷新一次 + _accumFpsCount++; + _accumFpsTime += delta; + if (_accumFpsTime > 1f) + { + _realTimeFps = _accumFpsCount / _accumFpsTime; + _accumFpsTime = 0f; + _accumFpsCount = 0; + OnPropertyChanged(nameof(RealTimeFps)); + } + + // 停止更新的时候只是时间不前进, 但是坐标变换还是要更新, 否则无法移动对象 + if (!_isUpdating) delta = 0; + + // 加上要快进的量 + lock (_forwardDeltaLock) + { + delta += _forwardDelta; + _forwardDelta = 0; + } + + // 更新模型对象时间 + lock (_models.Lock) + { + foreach (var sp in _models.Where(sp => sp.IsShown && (!_renderSelectedOnly || sp.IsSelected)).Reverse()) + { + if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止 + + sp.Update(0); // 避免物理效果出现问题 + sp.Update(delta * _speed); + } + } + } + + private void UpdateRenderFrame() + { + // 同步视图 + if (_wallpaperView) + { + using var view = _renderer.GetView(); + _wallpaperRenderer.SetView(view); + } + + // 更新背景图位置和缩放 + lock (_bgLock) + { + if (_backgroundImageSprite is not null) + { + using var view = _renderer.GetView(); + var bg = _backgroundImageSprite; + var viewSize = view.Size; + var bgSize = bg.Texture.Size; + var scaleX = Math.Abs(viewSize.X / bgSize.X); + var scaleY = Math.Abs(viewSize.Y / bgSize.Y); + var signX = Math.Sign(viewSize.X); + var signY = Math.Sign(viewSize.Y); + if (_backgroundImageMode == Stretch.None) + { + scaleX = scaleY = 1f / _renderer.Zoom; + } + else if (_backgroundImageMode == Stretch.Uniform) + { + scaleX = scaleY = Math.Min(scaleX, scaleY); + } + else if (_backgroundImageMode == Stretch.UniformToFill) + { + scaleX = scaleY = Math.Max(scaleX, scaleY); + } + bg.Scale = new(signX * scaleX, signY * scaleY); + bg.Position = view.Center; + bg.Rotation = view.Rotation; + } + } + + // 清除背景 + if (_vmMain.IsVisible) _renderer.Clear(_backgroundColor); + if (_wallpaperView) _wallpaperRenderer.Clear(_backgroundColor); + + // 渲染背景 + lock (_bgLock) + { + if (_backgroundImageSprite is not null) + { + if (_vmMain.IsVisible) _renderer.Draw(_backgroundImageSprite); + if (_wallpaperView) _wallpaperRenderer.Draw(_backgroundImageSprite); + } + } + + // 渲染坐标轴 + if (_showAxis && _vmMain.IsVisible) + { + _renderer.Draw(_axisVertices); + } + + // 渲染 Spine + lock (_models.Lock) + { + foreach (var sp in _models.Where(sp => sp.IsShown && (!_renderSelectedOnly || sp.IsSelected)).Reverse()) + { + if (_cancelToken?.IsCancellationRequested ?? true) break; // 提前中止 + + if (_vmMain.IsVisible) + { + // 为选中对象绘制一个半透明背景 + if (sp.IsSelected) + { + var rc = sp.GetCurrentBounds().ToFloatRect(); + _selectedBackgroundVertices[0] = new(new(rc.Left, rc.Top), _selectedBackgroundColor); + _selectedBackgroundVertices[1] = new(new(rc.Left + rc.Width, rc.Top), _selectedBackgroundColor); + _selectedBackgroundVertices[2] = new(new(rc.Left + rc.Width, rc.Top + rc.Height), _selectedBackgroundColor); + _selectedBackgroundVertices[3] = new(new(rc.Left, rc.Top + rc.Height), _selectedBackgroundColor); + _renderer.Draw(_selectedBackgroundVertices); + } + + // 仅在预览画面临时启用调试模式 + sp.EnableDebug = true; + _renderer.Draw(sp); + sp.EnableDebug = false; + } + if (_wallpaperView) _wallpaperRenderer.Draw(sp); + } + } + + // 显示内容 + if (_vmMain.IsVisible) _renderer.Display(); + if (_wallpaperView) _wallpaperRenderer.Display(); + } + public RendererWorkspaceConfigModel WorkspaceConfig { get