Compare commits
184 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
249b930602 | ||
|
|
6472f378b7 | ||
|
|
8672f0571c | ||
|
|
e7a990c1bd | ||
|
|
6727fa8e8f | ||
|
|
66d8c489b5 | ||
|
|
1931c4713a | ||
|
|
f19f172e7c | ||
|
|
092fa76124 | ||
|
|
a0b7db0a70 | ||
|
|
6438b46ea0 | ||
|
|
2bf73db9d3 | ||
|
|
03c4974c9f | ||
|
|
760fa3a451 | ||
|
|
018d8f5330 | ||
|
|
c9730e1a11 | ||
|
|
1f6e19e544 | ||
|
|
a1a0777791 | ||
|
|
887e3f76d2 | ||
|
|
8b622050fa | ||
|
|
20369aaf43 | ||
|
|
07c0e84b7d | ||
|
|
6770acaffd | ||
|
|
6201ccc7d1 | ||
|
|
965d1c469e | ||
|
|
b448ca8cb0 | ||
|
|
2204eb6c75 | ||
|
|
0abe063899 | ||
|
|
6f9b357473 | ||
|
|
152d842043 | ||
|
|
d16f97d574 | ||
|
|
d28eabaca5 | ||
|
|
b730f677be | ||
|
|
8f8806417a | ||
|
|
06694c9e89 | ||
|
|
e9b0ce3db2 | ||
|
|
4c72608398 | ||
|
|
7e99882fbf | ||
|
|
0d72d8749a | ||
|
|
d5b7a74520 | ||
|
|
0202027edb | ||
|
|
d1d32b6292 | ||
|
|
b32485e122 | ||
|
|
36d578f4d4 | ||
|
|
42bd5c2830 | ||
|
|
44548618e8 | ||
|
|
681b1be360 | ||
|
|
30dee9978c | ||
|
|
47aafc7948 | ||
|
|
1d8e2efdff | ||
|
|
dd504d32ca | ||
|
|
267c7b81c3 | ||
|
|
c4a6fd9d86 | ||
|
|
6e46152e4c | ||
|
|
f2f296e494 | ||
|
|
34f9eeff2c | ||
|
|
1278fefea2 | ||
|
|
48d46afcff | ||
|
|
6742dacaf2 | ||
|
|
3337ecc03a | ||
|
|
b9eaacd1f7 | ||
|
|
a0ada51325 | ||
|
|
8e03911957 | ||
|
|
0b3db0fd0d | ||
|
|
bb2862ed4f | ||
|
|
8c3be98b54 | ||
|
|
b76224c010 | ||
|
|
bd9f8d714a | ||
|
|
6900968555 | ||
|
|
741d334a92 | ||
|
|
b583108afa | ||
|
|
f7ace4dfe9 | ||
|
|
7ce2bd5629 | ||
|
|
41716df7b2 | ||
|
|
fe9b9829e2 | ||
|
|
4365c2a008 | ||
|
|
7896e072e7 | ||
|
|
940397c673 | ||
|
|
d57ea781f0 | ||
|
|
b74f2811a7 | ||
|
|
66223f952b | ||
|
|
0443d5e3d5 | ||
|
|
0a0b6a08e9 | ||
|
|
63af4a19e6 | ||
|
|
51f0446c18 | ||
|
|
e965223284 | ||
|
|
dbc15952cc | ||
|
|
93b70509ec | ||
|
|
798883d4e0 | ||
|
|
3e88e65bbd | ||
|
|
0906817cd3 | ||
|
|
37235fa7d0 | ||
|
|
72935d8f2b | ||
|
|
8a4095dae1 | ||
|
|
b59f228f2e | ||
|
|
a28cb3f424 | ||
|
|
05bb797a91 | ||
|
|
eb0029a877 | ||
|
|
ef0bfa85aa | ||
|
|
b5721e30a0 | ||
|
|
2c3b076b58 | ||
|
|
01e12f4524 | ||
|
|
a814d3d99a | ||
|
|
6a4508dceb | ||
|
|
b7d7274a5a | ||
|
|
71359a4328 | ||
|
|
3a3691bcca | ||
|
|
3d649e36cc | ||
|
|
a24db3c447 | ||
|
|
699a055707 | ||
|
|
1f8ed1c31c | ||
|
|
e2fc27663c | ||
|
|
b3cd0b9349 | ||
|
|
1c545b8c37 | ||
|
|
d660dd1c4a | ||
|
|
9c0acf7302 | ||
|
|
415df555c7 | ||
|
|
5ef13239da | ||
|
|
13ef873650 | ||
|
|
78b9834f6c | ||
|
|
672a695b20 | ||
|
|
e9951ed79a | ||
|
|
0c16b2f104 | ||
|
|
7628075420 | ||
|
|
6f896bdaad | ||
|
|
98930db4b6 | ||
|
|
c7493372e9 | ||
|
|
707aa7f570 | ||
|
|
99ff6f9f0a | ||
|
|
be8193e235 | ||
|
|
21b6dbee4c | ||
|
|
f60418fecb | ||
|
|
1180c735c8 | ||
|
|
3d8f6547e0 | ||
|
|
99ec2704fe | ||
|
|
dbd2cef766 | ||
|
|
212ecc2ff3 | ||
|
|
7806f9298d | ||
|
|
3bc57a8990 | ||
|
|
67c9ea9291 | ||
|
|
f404acc834 | ||
|
|
8e1f586d4f | ||
|
|
5dd1b84943 | ||
|
|
ad190d8952 | ||
|
|
ddb11808a7 | ||
|
|
9b1e26b2ac | ||
|
|
c8e35a9aed | ||
|
|
4786b0434c | ||
|
|
40bde84648 | ||
|
|
ebb2593526 | ||
|
|
6a74204ba1 | ||
|
|
78c6c47300 | ||
|
|
746a3decc8 | ||
|
|
6dfd25b760 | ||
|
|
a697ccc923 | ||
|
|
5bfa625868 | ||
|
|
0762a90fa9 | ||
|
|
a4926d905b | ||
|
|
b08b6752cd | ||
|
|
50b1c66e27 | ||
|
|
65508782c6 | ||
|
|
d02ab536b6 | ||
|
|
db3700bda3 | ||
|
|
6dea656e5e | ||
|
|
7bc82ab318 | ||
|
|
3eb9b1d008 | ||
|
|
eca59dc67b | ||
|
|
93b806dccd | ||
|
|
89c31d7c77 | ||
|
|
0a5432bb30 | ||
|
|
2eded25c03 | ||
|
|
fa00f0064e | ||
|
|
ddd3e94698 | ||
|
|
d7ee88f7f6 | ||
|
|
1d7a402749 | ||
|
|
86bcb079b0 | ||
|
|
390416df06 | ||
|
|
1344b34d08 | ||
|
|
497103bdb6 | ||
|
|
04953d13b6 | ||
|
|
b272d9802e | ||
|
|
bd5a537058 | ||
|
|
64a3caf938 | ||
|
|
ca34494483 |
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: 问题报告/Bug report
|
||||
about: 报告可能的程序错误/Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## 问题描述/Describe the bug
|
||||
清晰完整的描述问题是什么以及如何发生的。/A clear and concise description of what the bug is.
|
||||
|
||||
## 复现方式(可选)/To Reproduce (Optional)
|
||||
|
||||
## 截图(可选)/Screenshots (Optional)**
|
||||
如果有必要,提供报错时的有关截图。/If applicable, add screenshots to help explain your problem.
|
||||
|
||||
## 附件(可选)/Attachments (Optional)
|
||||
请将会**出现问题的文件**以及**日志文件**打包成一个 ZIP 后作为附件贴在 issue 内,日志文件位于程序目录下的 `logs` 文件夹内。/Please compress the problematic files and the log files into a single ZIP archive and attach it to this issue. The log files are located in the `logs` folder under the program directory.
|
||||
3
.github/workflows/dotnet-desktop.yml
vendored
3
.github/workflows/dotnet-desktop.yml
vendored
@@ -13,6 +13,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
PROJECT_NAME: SpineViewer
|
||||
PROJ_CLI_NAME: SpineViewerCLI
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -54,11 +55,13 @@ jobs:
|
||||
shell: pwsh
|
||||
run: |
|
||||
dotnet publish "$env:PROJECT_NAME\$env:PROJECT_NAME.csproj" -c Release -r win-x64 --sc false -o "publish\$env:PROJECT_NAME-$env:VERSION"
|
||||
dotnet publish "$env:PROJ_CLI_NAME\$env:PROJ_CLI_NAME.csproj" -c Release -r win-x64 --sc false -o "publish\$env:PROJECT_NAME-$env:VERSION"
|
||||
|
||||
- name: Publish SelfContained version
|
||||
shell: pwsh
|
||||
run: |
|
||||
dotnet publish "$env:PROJECT_NAME\$env:PROJECT_NAME.csproj" -c Release -r win-x64 --sc true -o "publish\$env:PROJECT_NAME-$env:VERSION-SelfContained"
|
||||
dotnet publish "$env:PROJ_CLI_NAME\$env:PROJ_CLI_NAME.csproj" -c Release -r win-x64 --sc true -o "publish\$env:PROJECT_NAME-$env:VERSION-SelfContained"
|
||||
|
||||
- name: Create release directory
|
||||
shell: pwsh
|
||||
|
||||
119
CHANGELOG.md
119
CHANGELOG.md
@@ -1,5 +1,124 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.16.6
|
||||
|
||||
- 修复控件尺寸为0时导致的画面缩放错误
|
||||
|
||||
## v0.16.5
|
||||
|
||||
- 修复对于无 size 行的旧 atlas 格式读取错误
|
||||
- 修复托盘化之后无法联动显示窗口的问题
|
||||
|
||||
## v0.16.4
|
||||
|
||||
- 增加 apng 导出格式
|
||||
- 增加颜色拾取器面板
|
||||
- 增加程序皮肤(主题颜色)首选项
|
||||
- 优化部分使用体验
|
||||
|
||||
## v0.16.3
|
||||
|
||||
- 修复加载工作区时的顺序错误
|
||||
- 调整部分调试渲染的逻辑
|
||||
- 完善命中检测逻辑
|
||||
|
||||
## v0.16.2
|
||||
|
||||
- 修复批量添加时的添加顺序错误
|
||||
- 增加精确命中检测和插槽输出功能
|
||||
- 部分代码重构
|
||||
|
||||
## v0.16.1
|
||||
|
||||
- 修复 3.4 版本存在的附件残留问题
|
||||
|
||||
## v0.16.0
|
||||
|
||||
- 增加最小化至托盘图标功能
|
||||
- 调整部分参数项的顺序
|
||||
- 增加开机自启和自启文件设置
|
||||
- 切换桌面投影时自动设置预览分辨率为主屏幕分辨率
|
||||
- 修复 3.4 版本下可能存在的附件残留问题
|
||||
|
||||
## v0.15.19
|
||||
|
||||
- 模型重载后选中最后一个重载模型
|
||||
- 修复 3.4 版本可能的奇数顶点数组导致的越界崩溃问题
|
||||
- 移除参数自动记录中的背景图片路径
|
||||
- 增加测试性桌面投影功能
|
||||
|
||||
## v0.15.18
|
||||
|
||||
- 完善窗口日志颜色标记
|
||||
- 修复预览图背景颜色为透明
|
||||
- 修复面板高度首次还原错误
|
||||
- 增加托盘图标
|
||||
- 增加可选预览背景画面和填充模式
|
||||
- 增强支持的纹理格式(例如 webp)
|
||||
|
||||
## v0.15.17
|
||||
|
||||
- 修改图标配色
|
||||
|
||||
## v0.15.16
|
||||
|
||||
- 修改模型添加顺序, 每次向顶层添加
|
||||
- 添加模型后自动选中最近添加的模型S
|
||||
- 点击预览画面或者选中项发生变化时转移焦点至列表
|
||||
- 增加移除全部菜单项
|
||||
- 增加单例模式和命令行文件参数
|
||||
- 增加文件关联设置
|
||||
|
||||
## v0.15.15
|
||||
|
||||
- 增加报错信息
|
||||
- 导入后自动选中最后一项
|
||||
|
||||
## v0.15.14
|
||||
|
||||
- 将预览画面的首选项移动至上一次状态参数中
|
||||
- 增加预览画面像素的自动保存和恢复
|
||||
- 增加日志启动时的版本号输出
|
||||
|
||||
## v0.15.13
|
||||
|
||||
- 增加程序布局自动存储和还原
|
||||
- 增加部分预览画面首选项
|
||||
|
||||
## v0.15.12
|
||||
|
||||
- 增加单个模型和单个轨道的时间因子
|
||||
- 增加单个轨道的 Alpha 混合参数
|
||||
- 调整轨道清除命令至右键菜单
|
||||
- 设置默认标签页为模型
|
||||
- 完善导入时的报错信息
|
||||
|
||||
## v0.15.11
|
||||
|
||||
- 修复自定义导出中参数构造错误
|
||||
- 增加 mov 格式及参数说明
|
||||
|
||||
## v0.15.10
|
||||
|
||||
- 增加插槽可见性参数, 允许任何情况下对插槽启用和禁用对插槽的渲染
|
||||
|
||||
## v0.15.9
|
||||
|
||||
- 添加 V34 和 V35 版本支持
|
||||
|
||||
## v0.15.8
|
||||
|
||||
- 修复渲染纹理过程中可能的 null 错误
|
||||
|
||||
## v0.15.7
|
||||
|
||||
- 合并社区 CLI 功能项目
|
||||
|
||||
## v0.15.6
|
||||
|
||||
- 修复导出单个的时长错误
|
||||
- 修改默认导出背景色为不透明黑色
|
||||
|
||||
## v0.15.5
|
||||
|
||||
- 修复自定义导出时的画面错误
|
||||
|
||||
41
CONTRIBUTING.md
Normal file
41
CONTRIBUTING.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# CONTRIBUTING
|
||||
|
||||
## 仓库分支
|
||||
|
||||
仓库目前包含 4 个分支:
|
||||
|
||||
- `main`: 默认分支, 也是项目最新版的发布用分支
|
||||
- `dev/wpf`: WPF 版本开发分支
|
||||
- `release/wf`: Winforms 旧版本发布分支 (已弃用, 仅进行 bug 修复)
|
||||
- `dev/wf`: Winforms 旧版本开发分支 (已弃用, 仅进行 bug 修复)
|
||||
|
||||
仓库的每个发布分支都有对应的开发分支 `dev/*`, **在进行贡献和推送时请在开发分支上进行**, 待开发分支上审核完毕进行必要的确认 (例如版本号的更新) 后, 再从开发分支向对应的发布分支发起 pr, 合并后将会通过 Actions 进行自动生成和发布.
|
||||
|
||||
## 仓库结构
|
||||
|
||||
仓库目前包含两个可执行文件项目, 分别是:
|
||||
|
||||
- `SpineViewer.csproj`
|
||||
- `SpineViewerCLI.csproj`
|
||||
|
||||
前者为仓库主要项目, 提供一个预览操作 Spine 模型文件的 UI 界面, 后者基于社区贡献进行开发, 提供一些便捷的 CLI 功能, 从而可以对模型文件进行一些批量操作.
|
||||
|
||||
除此之外其余项目均为一些基础功能库, 为以上两个项目提供必要的功能支持. 原则上 UI 项目和 CLI 项目二者独立互不引用, 仅引用相同的基础功能库, 以保证整个仓库的层次结构清晰便于维护.
|
||||
|
||||
## 如何贡献
|
||||
|
||||
对于一些小改动, 例如:
|
||||
|
||||
- 某些文件内的 bug 修复 (例如一些逻辑上的错误)
|
||||
- 已有功能的扩展性增强 (例如在已有代码逻辑结构上扩充某些功能字段)
|
||||
- 其他可能的对**已有功能**的修复改进
|
||||
|
||||
可以直接 fork 修改后向开发分支发起 pr, 经 review 无问题后可直接合并.
|
||||
|
||||
对于较大的改动, 例如:
|
||||
|
||||
- 新增某些代码文件 (例如需要添加一些全新的类)
|
||||
- 添加一些全新的逻辑或者功能代码 (例如在自行车上加装发动机)
|
||||
- 其他可能影响项目代码逻辑结构的改动
|
||||
|
||||
这些改动请先提 Issue, 进行必要性讨论, 以及确认新功能的引入方式, 请不要直接将这些可能的破坏性改动发起 pr.
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@@ -7,7 +7,7 @@
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<Version>0.15.4</Version>
|
||||
<Version>0.16.0</Version>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -1,92 +1,53 @@
|
||||
//
|
||||
// Copyright (c) 2004-2011 Jaroslaw Kowalski <jaak@jkowalski.net>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of Jaroslaw Kowalski nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
// THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
using NLog;
|
||||
using NLog.Conditions;
|
||||
using NLog.Config;
|
||||
using NLog;
|
||||
using System.ComponentModel;
|
||||
using NLog.Layouts;
|
||||
using System.Windows;
|
||||
|
||||
namespace NLog.Windows.Wpf
|
||||
{
|
||||
[NLogConfigurationItem]
|
||||
public class RichTextBoxRowColoringRule
|
||||
{
|
||||
static RichTextBoxRowColoringRule()
|
||||
{
|
||||
Default = new RichTextBoxRowColoringRule();
|
||||
}
|
||||
|
||||
public RichTextBoxRowColoringRule()
|
||||
: this(null, "Empty", "Empty", FontStyles.Normal, FontWeights.Normal)
|
||||
{
|
||||
}
|
||||
|
||||
public RichTextBoxRowColoringRule(string condition, string fontColor, string backColor, FontStyle fontStyle, FontWeight fontWeight)
|
||||
{
|
||||
Condition = condition;
|
||||
FontColor = fontColor;
|
||||
BackgroundColor = backColor;
|
||||
Style = fontStyle;
|
||||
Weight = fontWeight;
|
||||
}
|
||||
|
||||
public RichTextBoxRowColoringRule(string condition, string fontColor, string backColor)
|
||||
{
|
||||
Condition = condition;
|
||||
FontColor = fontColor;
|
||||
BackgroundColor = backColor;
|
||||
Style = FontStyles.Normal;
|
||||
Weight = FontWeights.Normal;
|
||||
}
|
||||
|
||||
public static RichTextBoxRowColoringRule Default { get; private set; }
|
||||
|
||||
[RequiredParameter]
|
||||
public ConditionExpression Condition { get; set; }
|
||||
|
||||
[DefaultValue("Empty")]
|
||||
public string FontColor { get; set; }
|
||||
public Layout FontColor { get; set; }
|
||||
public Layout BackgroundColor { get; set; }
|
||||
|
||||
[DefaultValue("Empty")]
|
||||
public string BackgroundColor { get; set; }
|
||||
public FontStyle FontStyle { get; set; }
|
||||
public FontWeight FontWeight { get; set; }
|
||||
|
||||
public FontStyle Style { get; set; }
|
||||
static RichTextBoxRowColoringRule()
|
||||
{
|
||||
RichTextBoxRowColoringRule.Default = new RichTextBoxRowColoringRule();
|
||||
}
|
||||
|
||||
public FontWeight Weight { get; set; }
|
||||
public RichTextBoxRowColoringRule() : this(null, "Empty", "Empty", FontStyles.Normal, FontWeights.Normal) { }
|
||||
|
||||
public RichTextBoxRowColoringRule(string condition, string fontColor, string backColor)
|
||||
{
|
||||
this.Condition = (ConditionExpression)condition;
|
||||
this.FontColor = Layout.FromString(fontColor);
|
||||
this.BackgroundColor = Layout.FromString(backColor);
|
||||
this.FontStyle = FontStyles.Normal;
|
||||
this.FontWeight = FontWeights.Normal;
|
||||
}
|
||||
|
||||
public RichTextBoxRowColoringRule(string condition, string fontColor, string backColor, FontStyle fontStyle, FontWeight fontWeight)
|
||||
{
|
||||
this.Condition = (ConditionExpression)condition;
|
||||
this.FontColor = Layout.FromString(fontColor);
|
||||
this.BackgroundColor = Layout.FromString(backColor);
|
||||
this.FontStyle = fontStyle;
|
||||
this.FontWeight = fontWeight;
|
||||
}
|
||||
|
||||
public bool CheckCondition(LogEventInfo logEvent)
|
||||
{
|
||||
return true.Equals(Condition.Evaluate(logEvent));
|
||||
return true.Equals(this.Condition.Evaluate(logEvent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
using NLog.Config;
|
||||
using NLog.Layouts;
|
||||
using NLog;
|
||||
using NLog.Common;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using System.Windows;
|
||||
|
||||
namespace NLog.Windows.Wpf
|
||||
{
|
||||
// TODO: 完善日志实现
|
||||
[Target("RichTextBox")]
|
||||
public sealed class RichTextBoxTarget : TargetWithLayout
|
||||
{
|
||||
private int lineCount;
|
||||
private int _width = 500;
|
||||
private int _height = 500;
|
||||
private static readonly TypeConverter colorConverter = new ColorConverter();
|
||||
public static ReadOnlyCollection<RichTextBoxRowColoringRule> DefaultRowColoringRules { get; } = CreateDefaultColoringRules();
|
||||
|
||||
static RichTextBoxTarget()
|
||||
private static ReadOnlyCollection<RichTextBoxRowColoringRule> CreateDefaultColoringRules()
|
||||
{
|
||||
var rules = new List<RichTextBoxRowColoringRule>()
|
||||
return new List<RichTextBoxRowColoringRule>()
|
||||
{
|
||||
new RichTextBoxRowColoringRule("level == LogLevel.Fatal", "White", "Red", FontStyles.Normal, FontWeights.Bold),
|
||||
new RichTextBoxRowColoringRule("level == LogLevel.Error", "Red", "Empty", FontStyles.Italic, FontWeights.Bold),
|
||||
@@ -36,221 +29,253 @@ namespace NLog.Windows.Wpf
|
||||
new RichTextBoxRowColoringRule("level == LogLevel.Info", "Black", "Empty"),
|
||||
new RichTextBoxRowColoringRule("level == LogLevel.Debug", "Gray", "Empty"),
|
||||
new RichTextBoxRowColoringRule("level == LogLevel.Trace", "DarkGray", "Empty", FontStyles.Italic, FontWeights.Normal),
|
||||
};
|
||||
|
||||
DefaultRowColoringRules = rules.AsReadOnly();
|
||||
}.AsReadOnly();
|
||||
}
|
||||
|
||||
public RichTextBoxTarget()
|
||||
{
|
||||
WordColoringRules = new List<RichTextBoxWordColoringRule>();
|
||||
RowColoringRules = new List<RichTextBoxRowColoringRule>();
|
||||
ToolWindow = true;
|
||||
}
|
||||
|
||||
private delegate void DelSendTheMessageToRichTextBox(string logMessage, RichTextBoxRowColoringRule rule);
|
||||
|
||||
private delegate void FormCloseDelegate();
|
||||
|
||||
public static ReadOnlyCollection<RichTextBoxRowColoringRule> DefaultRowColoringRules { get; private set; }
|
||||
public RichTextBoxTarget() { }
|
||||
|
||||
public string ControlName { get; set; }
|
||||
|
||||
public string FormName { get; set; }
|
||||
public string WindowName { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
public bool UseDefaultRowColoringRules { get; set; }
|
||||
|
||||
[ArrayParameter(typeof(RichTextBoxRowColoringRule), "row-coloring")]
|
||||
public IList<RichTextBoxRowColoringRule> RowColoringRules { get; private set; }
|
||||
|
||||
[ArrayParameter(typeof(RichTextBoxWordColoringRule), "word-coloring")]
|
||||
public IList<RichTextBoxWordColoringRule> WordColoringRules { get; private set; }
|
||||
|
||||
[DefaultValue(true)]
|
||||
public bool ToolWindow { get; set; }
|
||||
|
||||
public bool ShowMinimized { get; set; }
|
||||
|
||||
public int Width
|
||||
{
|
||||
get { return _width; }
|
||||
set { _width = value; }
|
||||
}
|
||||
|
||||
public int Height
|
||||
{
|
||||
get { return _height; }
|
||||
set { _height = value; }
|
||||
}
|
||||
|
||||
public bool AutoScroll { get; set; }
|
||||
|
||||
public int MaxLines { get; set; }
|
||||
|
||||
internal Window TargetForm { get; set; }
|
||||
[ArrayParameter(typeof(RichTextBoxRowColoringRule), "row-coloring")]
|
||||
public IList<RichTextBoxRowColoringRule> RowColoringRules { get; } = new List<RichTextBoxRowColoringRule>();
|
||||
|
||||
internal RichTextBox TargetRichTextBox { get; set; }
|
||||
[ArrayParameter(typeof(RichTextBoxWordColoringRule), "word-coloring")]
|
||||
public IList<RichTextBoxWordColoringRule> WordColoringRules { get; } = new List<RichTextBoxWordColoringRule>();
|
||||
|
||||
internal bool CreatedForm { get; set; }
|
||||
[NLogConfigurationIgnoreProperty]
|
||||
public Window TargetWindow { get; set; }
|
||||
|
||||
[NLogConfigurationIgnoreProperty]
|
||||
public RichTextBox TargetRichTextBox { get; set; }
|
||||
|
||||
protected override void InitializeTarget()
|
||||
{
|
||||
TargetRichTextBox = Application.Current.MainWindow.FindName(ControlName) as RichTextBox;
|
||||
base.InitializeTarget();
|
||||
if (TargetRichTextBox != null)
|
||||
return;
|
||||
|
||||
if (TargetRichTextBox != null) return;
|
||||
//this.TargetForm = FormHelper.CreateForm(this.FormName, this.Width, this.Height, false, this.ShowMinimized, this.ToolWindow);
|
||||
//this.CreatedForm = true;
|
||||
|
||||
var openFormByName = Application.Current.Windows.Cast<Window>().FirstOrDefault(x => x.GetType().Name == FormName);
|
||||
if (openFormByName != null)
|
||||
if (WindowName == null)
|
||||
{
|
||||
TargetForm = openFormByName;
|
||||
if (string.IsNullOrEmpty(ControlName))
|
||||
{
|
||||
// throw new NLogConfigurationException("Rich text box control name must be specified for " + GetType().Name + ".");
|
||||
Trace.WriteLine("Rich text box control name must be specified for " + GetType().Name + ".");
|
||||
}
|
||||
|
||||
CreatedForm = false;
|
||||
TargetRichTextBox = TargetForm.FindName(ControlName) as RichTextBox;
|
||||
|
||||
if (TargetRichTextBox == null)
|
||||
{
|
||||
// throw new NLogConfigurationException("Rich text box control '" + ControlName + "' cannot be found on form '" + FormName + "'.");
|
||||
Trace.WriteLine("Rich text box control '" + ControlName + "' cannot be found on form '" + FormName + "'.");
|
||||
}
|
||||
HandleError("WindowName should be specified for {0}.{1}", GetType().Name, Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TargetRichTextBox == null)
|
||||
if (string.IsNullOrEmpty(ControlName))
|
||||
{
|
||||
TargetForm = new Window
|
||||
{
|
||||
Name = FormName,
|
||||
Width = Width,
|
||||
Height = Height,
|
||||
WindowStyle = ToolWindow ? WindowStyle.ToolWindow : WindowStyle.None,
|
||||
WindowState = ShowMinimized ? WindowState.Minimized : WindowState.Normal,
|
||||
Title = "NLog Messages"
|
||||
};
|
||||
TargetForm.Show();
|
||||
|
||||
TargetRichTextBox = new RichTextBox { Name = ControlName };
|
||||
var style = new Style(typeof(Paragraph));
|
||||
TargetRichTextBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
|
||||
style.Setters.Add(new Setter(Block.MarginProperty, new Thickness(0, 0, 0, 0)));
|
||||
TargetRichTextBox.Resources.Add(typeof(Paragraph), style);
|
||||
TargetForm.Content = TargetRichTextBox;
|
||||
|
||||
CreatedForm = true;
|
||||
HandleError("Rich text box control name must be specified for {0}.{1}", GetType().Name, Name);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetWindow = Application.Current.Windows.OfType<Window>().FirstOrDefault(w => w.Name == WindowName);
|
||||
if (targetWindow == null)
|
||||
{
|
||||
InternalLogger.Info("{0}: WindowName '{1}' not found", this, WindowName);
|
||||
return;
|
||||
}
|
||||
|
||||
var targetControl = targetWindow.FindName(ControlName) as RichTextBox;
|
||||
if (targetControl == null)
|
||||
{
|
||||
InternalLogger.Info("{0}: WIndowName '{1}' does not contain ControlName '{2}'", this, WindowName, ControlName);
|
||||
return;
|
||||
}
|
||||
|
||||
AttachToControl(targetWindow, targetControl);
|
||||
}
|
||||
|
||||
private static void HandleError(string message, params object[] args)
|
||||
{
|
||||
if (LogManager.ThrowExceptions)
|
||||
{
|
||||
throw new NLogConfigurationException(string.Format(message, args));
|
||||
}
|
||||
InternalLogger.Error(message, args);
|
||||
}
|
||||
|
||||
private void AttachToControl(Window window, RichTextBox textboxControl)
|
||||
{
|
||||
InternalLogger.Info("{0}: Attaching target to textbox {1}.{2}", this, window.Name, textboxControl.Name);
|
||||
DetachFromControl();
|
||||
TargetWindow = window;
|
||||
TargetRichTextBox = textboxControl;
|
||||
}
|
||||
|
||||
private void DetachFromControl()
|
||||
{
|
||||
TargetWindow = null;
|
||||
TargetRichTextBox = null;
|
||||
}
|
||||
|
||||
protected override void CloseTarget()
|
||||
{
|
||||
if (CreatedForm)
|
||||
{
|
||||
try
|
||||
{
|
||||
TargetForm.Dispatcher.Invoke(() =>
|
||||
{
|
||||
TargetForm.Close();
|
||||
TargetForm = null;
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
DetachFromControl();
|
||||
}
|
||||
|
||||
protected override void Write(LogEventInfo logEvent)
|
||||
{
|
||||
RichTextBoxRowColoringRule matchingRule = RowColoringRules.FirstOrDefault(rr => rr.CheckCondition(logEvent));
|
||||
|
||||
if (UseDefaultRowColoringRules && matchingRule == null)
|
||||
RichTextBox textbox = TargetRichTextBox;
|
||||
if (textbox == null || textbox.Dispatcher.HasShutdownStarted || textbox.Dispatcher.HasShutdownFinished)
|
||||
{
|
||||
foreach (var rr in DefaultRowColoringRules.Where(rr => rr.CheckCondition(logEvent)))
|
||||
{
|
||||
matchingRule = rr;
|
||||
break;
|
||||
}
|
||||
//no last logged textbox
|
||||
InternalLogger.Trace("{0}: Attached Textbox is {1}, skipping logging", this, textbox == null ? "null" : "disposed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (matchingRule == null)
|
||||
{
|
||||
matchingRule = RichTextBoxRowColoringRule.Default;
|
||||
}
|
||||
|
||||
var logMessage = Layout.Render(logEvent);
|
||||
|
||||
if (Application.Current == null) return;
|
||||
string logMessage = RenderLogEvent(Layout, logEvent);
|
||||
RichTextBoxRowColoringRule matchingRule = FindMatchingRule(logEvent);
|
||||
_ = DoSendMessageToTextbox(logMessage, matchingRule, logEvent);
|
||||
}
|
||||
|
||||
private bool DoSendMessageToTextbox(string logMessage, RichTextBoxRowColoringRule rule, LogEventInfo logEvent)
|
||||
{
|
||||
RichTextBox textbox = TargetRichTextBox;
|
||||
try
|
||||
{
|
||||
if (Application.Current.Dispatcher.CheckAccess() == false)
|
||||
if (textbox != null && !textbox.Dispatcher.HasShutdownStarted && !textbox.Dispatcher.HasShutdownFinished)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() => SendTheMessageToRichTextBox(logMessage, matchingRule));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendTheMessageToRichTextBox(logMessage, matchingRule);
|
||||
if (!textbox.Dispatcher.CheckAccess())
|
||||
{
|
||||
textbox.Dispatcher.BeginInvoke(() => SendTheMessageToRichTextBox(textbox, logMessage, rule, logEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
SendTheMessageToRichTextBox(textbox, logMessage, rule, logEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
InternalLogger.Warn(ex, "{0}: Failed to append RichTextBox", this);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static Color GetColorFromString(string color, Brush defaultColor)
|
||||
{
|
||||
|
||||
if (color == "Empty")
|
||||
{
|
||||
return defaultColor is SolidColorBrush solidBrush ? solidBrush.Color : Colors.White;
|
||||
}
|
||||
|
||||
return (Color)colorConverter.ConvertFromString(color);
|
||||
}
|
||||
|
||||
|
||||
private void SendTheMessageToRichTextBox(string logMessage, RichTextBoxRowColoringRule rule)
|
||||
{
|
||||
RichTextBox rtbx = TargetRichTextBox;
|
||||
|
||||
var tr = new TextRange(rtbx.Document.ContentEnd, rtbx.Document.ContentEnd);
|
||||
tr.Text = logMessage + "\n";
|
||||
tr.ApplyPropertyValue(TextElement.ForegroundProperty,
|
||||
new SolidColorBrush(GetColorFromString(rule.FontColor, (Brush)tr.GetPropertyValue(TextElement.ForegroundProperty)))
|
||||
);
|
||||
tr.ApplyPropertyValue(TextElement.BackgroundProperty,
|
||||
new SolidColorBrush(GetColorFromString(rule.BackgroundColor, (Brush)tr.GetPropertyValue(TextElement.BackgroundProperty)))
|
||||
);
|
||||
tr.ApplyPropertyValue(TextElement.FontStyleProperty, rule.Style);
|
||||
tr.ApplyPropertyValue(TextElement.FontWeightProperty, rule.Weight);
|
||||
|
||||
|
||||
if (MaxLines > 0)
|
||||
{
|
||||
lineCount++;
|
||||
if (lineCount > MaxLines)
|
||||
if (LogManager.ThrowExceptions)
|
||||
{
|
||||
tr = new TextRange(rtbx.Document.ContentStart, rtbx.Document.ContentEnd);
|
||||
tr.Text.Remove(0, tr.Text.IndexOf('\n'));
|
||||
lineCount--;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private RichTextBoxRowColoringRule FindMatchingRule(LogEventInfo logEvent)
|
||||
{
|
||||
//custom rules first
|
||||
if (RowColoringRules.Count > 0)
|
||||
{
|
||||
foreach (RichTextBoxRowColoringRule coloringRule in RowColoringRules)
|
||||
{
|
||||
if (coloringRule.CheckCondition(logEvent))
|
||||
{
|
||||
return coloringRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (UseDefaultRowColoringRules && DefaultRowColoringRules != null)
|
||||
{
|
||||
foreach (RichTextBoxRowColoringRule coloringRule in DefaultRowColoringRules)
|
||||
{
|
||||
if (coloringRule.CheckCondition(logEvent))
|
||||
{
|
||||
return coloringRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RichTextBoxRowColoringRule.Default;
|
||||
}
|
||||
|
||||
private void SendTheMessageToRichTextBox(RichTextBox textBox, string logMessage, RichTextBoxRowColoringRule rule, LogEventInfo logEvent)
|
||||
{
|
||||
if (textBox == null) return;
|
||||
|
||||
var document = textBox.Document;
|
||||
|
||||
// 插入文本(带换行)
|
||||
var tr = new TextRange(document.ContentEnd, document.ContentEnd)
|
||||
{
|
||||
Text = logMessage + Environment.NewLine
|
||||
};
|
||||
|
||||
// 设置行级样式
|
||||
var fgColor = rule.FontColor?.Render(logEvent);
|
||||
var bgColor = rule.BackgroundColor?.Render(logEvent);
|
||||
|
||||
tr.ApplyPropertyValue(TextElement.ForegroundProperty,
|
||||
string.IsNullOrEmpty(fgColor) || fgColor == "Empty"
|
||||
? textBox.Foreground
|
||||
: new SolidColorBrush((Color)ColorConverter.ConvertFromString(fgColor)));
|
||||
|
||||
tr.ApplyPropertyValue(TextElement.BackgroundProperty,
|
||||
string.IsNullOrEmpty(bgColor) || bgColor == "Empty"
|
||||
? Brushes.Transparent
|
||||
: new SolidColorBrush((Color)ColorConverter.ConvertFromString(bgColor)));
|
||||
|
||||
tr.ApplyPropertyValue(TextElement.FontStyleProperty, rule.FontStyle);
|
||||
tr.ApplyPropertyValue(TextElement.FontWeightProperty, rule.FontWeight);
|
||||
|
||||
// Word coloring(在刚插入的范围内做匹配)
|
||||
if (WordColoringRules.Count > 0)
|
||||
{
|
||||
foreach (var wordRule in WordColoringRules)
|
||||
{
|
||||
var pattern = wordRule.Regex?.Render(logEvent) ?? string.Empty;
|
||||
var text = wordRule.Text?.Render(logEvent) ?? string.Empty;
|
||||
var wholeWords = wordRule.WholeWords.RenderValue(logEvent);
|
||||
var ignoreCase = wordRule.IgnoreCase.RenderValue(logEvent);
|
||||
|
||||
var regex = wordRule.ResolveRegEx(pattern, text, wholeWords, ignoreCase);
|
||||
var matches = regex.Matches(tr.Text);
|
||||
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
// 匹配到的部分范围
|
||||
var start = tr.Start.GetPositionAtOffset(match.Index, LogicalDirection.Forward);
|
||||
var endPos = tr.Start.GetPositionAtOffset(match.Index + match.Length, LogicalDirection.Backward);
|
||||
if (start == null || endPos == null) continue;
|
||||
|
||||
var wordRange = new TextRange(start, endPos);
|
||||
|
||||
var wordFg = wordRule.FontColor?.Render(logEvent);
|
||||
var wordBg = wordRule.BackgroundColor?.Render(logEvent);
|
||||
|
||||
wordRange.ApplyPropertyValue(TextElement.ForegroundProperty,
|
||||
string.IsNullOrEmpty(wordFg) || wordFg == "Empty"
|
||||
? tr.GetPropertyValue(TextElement.ForegroundProperty)
|
||||
: new SolidColorBrush((Color)ColorConverter.ConvertFromString(wordFg)));
|
||||
|
||||
wordRange.ApplyPropertyValue(TextElement.BackgroundProperty,
|
||||
string.IsNullOrEmpty(wordBg) || wordBg == "Empty"
|
||||
? tr.GetPropertyValue(TextElement.BackgroundProperty)
|
||||
: new SolidColorBrush((Color)ColorConverter.ConvertFromString(wordBg)));
|
||||
|
||||
wordRange.ApplyPropertyValue(TextElement.FontStyleProperty, wordRule.FontStyle);
|
||||
wordRange.ApplyPropertyValue(TextElement.FontWeightProperty, wordRule.FontWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 限制最大行数
|
||||
if (MaxLines > 0)
|
||||
{
|
||||
while (document.Blocks.Count > MaxLines)
|
||||
{
|
||||
document.Blocks.Remove(document.Blocks.FirstBlock);
|
||||
}
|
||||
}
|
||||
|
||||
// 自动滚动到最后
|
||||
if (AutoScroll)
|
||||
{
|
||||
rtbx.ScrollToEnd();
|
||||
textBox.ScrollToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +1,59 @@
|
||||
//
|
||||
// Copyright (c) 2004-2011 Jaroslaw Kowalski <jaak@jkowalski.net>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of Jaroslaw Kowalski nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
// THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
using NLog.Config;
|
||||
using NLog.Layouts;
|
||||
using System.ComponentModel;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using NLog.Config;
|
||||
|
||||
namespace NLog.Windows.Wpf
|
||||
{
|
||||
[NLogConfigurationItem]
|
||||
[NLogConfigurationItem]
|
||||
public class RichTextBoxWordColoringRule
|
||||
{
|
||||
private Regex compiledRegex;
|
||||
public Layout Regex { get; set; }
|
||||
public Layout Text { get; set; }
|
||||
public Layout<bool> WholeWords { get; set; }
|
||||
public Layout<bool> IgnoreCase { get; set; }
|
||||
|
||||
public RichTextBoxWordColoringRule()
|
||||
public Layout FontColor { get; set; }
|
||||
public Layout BackgroundColor { get; set; }
|
||||
|
||||
public FontStyle FontStyle { get; set; }
|
||||
public FontWeight FontWeight { get; set; }
|
||||
|
||||
internal Regex ResolveRegEx(string pattern, string text, bool wholeWords, bool ignoreCase)
|
||||
{
|
||||
FontColor = "Empty";
|
||||
BackgroundColor = "Empty";
|
||||
if (string.IsNullOrEmpty(pattern) && text != null)
|
||||
{
|
||||
pattern = System.Text.RegularExpressions.Regex.Escape(text);
|
||||
if (wholeWords)
|
||||
pattern = "\b" + pattern + "\b";
|
||||
}
|
||||
|
||||
RegexOptions options = RegexOptions.None;
|
||||
if (ignoreCase)
|
||||
options |= RegexOptions.IgnoreCase;
|
||||
|
||||
return new Regex(pattern, options); // RegEx-Cache
|
||||
}
|
||||
|
||||
public RichTextBoxWordColoringRule() : this(null, "Empty", "Empty", FontStyles.Normal, FontWeights.Normal) { }
|
||||
|
||||
public RichTextBoxWordColoringRule(string text, string fontColor, string backgroundColor)
|
||||
{
|
||||
Text = text;
|
||||
FontColor = fontColor;
|
||||
BackgroundColor = backgroundColor;
|
||||
Style = FontStyles.Normal;
|
||||
Weight = FontWeights.Normal;
|
||||
this.Text = text;
|
||||
this.FontColor = Layout.FromString(fontColor);
|
||||
this.BackgroundColor = Layout.FromString(backgroundColor);
|
||||
this.FontStyle = FontStyles.Normal;
|
||||
this.FontWeight = FontWeights.Normal;
|
||||
}
|
||||
|
||||
public RichTextBoxWordColoringRule(string text, string textColor, string backgroundColor, FontStyle fontStyle, FontWeight fontWeight)
|
||||
{
|
||||
Text = text;
|
||||
FontColor = textColor;
|
||||
BackgroundColor = backgroundColor;
|
||||
Style = fontStyle;
|
||||
Weight = fontWeight;
|
||||
this.Text = text;
|
||||
this.FontColor = Layout.FromString(textColor);
|
||||
this.BackgroundColor = Layout.FromString(backgroundColor);
|
||||
this.FontStyle = fontStyle;
|
||||
this.FontWeight = fontWeight;
|
||||
}
|
||||
|
||||
public string Regex { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
public bool WholeWords { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
public bool IgnoreCase { get; set; }
|
||||
|
||||
public FontStyle Style { get; set; }
|
||||
|
||||
public FontWeight Weight { get; set; }
|
||||
|
||||
public Regex CompiledRegex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (compiledRegex == null)
|
||||
{
|
||||
string regexpression = Regex;
|
||||
if (regexpression == null && Text != null)
|
||||
{
|
||||
regexpression = System.Text.RegularExpressions.Regex.Escape(Text);
|
||||
if (WholeWords)
|
||||
{
|
||||
regexpression = "\b" + regexpression + "\b";
|
||||
}
|
||||
}
|
||||
|
||||
RegexOptions regexOptions = RegexOptions.Compiled;
|
||||
if (IgnoreCase)
|
||||
{
|
||||
regexOptions |= RegexOptions.IgnoreCase;
|
||||
}
|
||||
|
||||
compiledRegex = new Regex(regexpression, regexOptions);
|
||||
}
|
||||
|
||||
return compiledRegex;
|
||||
}
|
||||
}
|
||||
|
||||
[DefaultValue("Empty")]
|
||||
public string FontColor { get; set; }
|
||||
|
||||
[DefaultValue("Empty")]
|
||||
public string BackgroundColor { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
84
README.en.md
84
README.en.md
@@ -8,30 +8,40 @@
|
||||
|
||||
A simple and user-friendly Spine file viewer and exporter with multi-language support (Chinese/English/Japanese).
|
||||
|
||||

|
||||

|
||||
|
||||
https://github.com/user-attachments/assets/37b6b730-088a-4352-827a-c338127a16f0
|
||||
|
||||
## Features
|
||||
|
||||
* Supports multiple versions of Spine files.
|
||||
* Batch open files via drag-and-drop or copy-paste.
|
||||
* Batch preview functionality.
|
||||
* List-based multi-skeleton viewing and render order management.
|
||||
* Batch adjustment of skeleton parameters using multi-selection.
|
||||
* Multi-track animation settings.
|
||||
* Skin and custom slot attachment settings.
|
||||
* Debug rendering support.
|
||||
* Fullscreen preview mode.
|
||||
* Export to single frame/image sequence/animated GIF/video formats.
|
||||
* Automatic resolution batch export.
|
||||
* FFmpeg custom export support.
|
||||
* Program parameter saving.
|
||||
* ...
|
||||
- Multiple versions of Spine files
|
||||
- Batch file opening via drag-and-drop or copy-paste
|
||||
- Batch preview
|
||||
- List-based multi-skeleton viewing and render order management
|
||||
- Multi-selection in lists for batch skeleton parameter settings
|
||||
- Multi-track animation settings
|
||||
- Skin and custom slot attachment settings
|
||||
- Custom slot visibility
|
||||
- Debug rendering
|
||||
- Playback speed adjustment for view/model/track timelines
|
||||
- Track alpha blending parameter settings
|
||||
- Fullscreen preview
|
||||
- Export to single frame, image sequence, animated GIF, or video file
|
||||
- Automatic resolution batch export
|
||||
- Custom export with FFmpeg
|
||||
- Program parameter saving
|
||||
- File extension association
|
||||
- Texture images in formats other than PNG
|
||||
- Launch at startup with persistent dynamic wallpaper
|
||||
- ......
|
||||
|
||||
### Supported Spine Versions
|
||||
|
||||
| Version | View & Export |
|
||||
| :-----: | :------------------: |
|
||||
| `2.1.x` | :white\_check\_mark: |
|
||||
| `3.4.x` | :white\_check\_mark: |
|
||||
| `3.5.x` | :white\_check\_mark: |
|
||||
| `3.6.x` | :white\_check\_mark: |
|
||||
| `3.7.x` | :white\_check\_mark: |
|
||||
| `3.8.x` | :white\_check\_mark: |
|
||||
@@ -71,14 +81,14 @@ In the menu, go to "File" -> "Preferences..." -> "Language," select your desired
|
||||
|
||||
The program is organized into a left-right layout:
|
||||
|
||||
* **Left Panel:** Functionality panel.
|
||||
* **Right Panel:** Preview display.
|
||||
- **Left Panel:** Functionality panel.
|
||||
- **Right Panel:** Preview display.
|
||||
|
||||
The left panel includes three sub-panels:
|
||||
|
||||
* **Browse:** Preview the content of a specified folder without importing files into the program. This panel allows generating `.webp` previews for models or importing selected models.
|
||||
* **Model:** Lists imported models for rendering. Parameters and rendering order can be adjusted here, along with other model-related functionalities.
|
||||
* **Display:** Adjust parameters for the right-side preview display.
|
||||
- **Browse:** Preview the content of a specified folder without importing files into the program. This panel allows generating `.webp` previews for models or importing selected models.
|
||||
- **Model:** Lists imported models for rendering. Parameters and rendering order can be adjusted here, along with other model-related functionalities.
|
||||
- **Display:** Adjust parameters for the right-side preview display.
|
||||
|
||||
Hover your mouse over buttons, labels, or input fields to see help text for most UI elements.
|
||||
|
||||
@@ -94,10 +104,10 @@ The Model panel supports right-click menus, some shortcuts, and batch adjustment
|
||||
|
||||
For preview display adjustments:
|
||||
|
||||
* **Left-click:** Select and drag models. Hold `Ctrl` for multi-selection, synchronized with the left-side list.
|
||||
* **Right-click:** Drag the entire display.
|
||||
* **Scroll wheel:** Zoom in/out. Hold `Ctrl` to scale selected models.
|
||||
* **Render selected-only mode:** In this mode, the preview only shows selected models, and selection status can only be changed via the left-side list.
|
||||
- **Left-click:** Select and drag models. Hold `Ctrl` for multi-selection, synchronized with the left-side list.
|
||||
- **Right-click:** Drag the entire display.
|
||||
- **Scroll wheel:** Zoom in/out. Hold `Ctrl` to scale selected models.
|
||||
- **Render selected-only mode:** In this mode, the preview only shows selected models, and selection status can only be changed via the left-side list.
|
||||
|
||||
The buttons below the preview display allow time adjustments, serving as a simple playback control.
|
||||
|
||||
@@ -109,9 +119,17 @@ Use the right-click menu in the Model panel to export selected items.
|
||||
|
||||
Key export parameters include:
|
||||
|
||||
* **Output folder:** Optional. When not specified, output is saved to the respective model folder; otherwise, all output is saved to the provided folder.
|
||||
* **Export single:** By default, each model is exported independently. Selecting "Export single" renders all selected models in a single frame, producing a unified output.
|
||||
* **Auto resolution:** Ignores the preview resolution and viewport parameters, exporting output at the actual size of the content. For animations/videos, the output matches the size required for full visibility.
|
||||
- **Output folder:** Optional. When not specified, output is saved to the respective model folder; otherwise, all output is saved to the provided folder.
|
||||
- **Export single:** By default, each model is exported independently. Selecting "Export single" renders all selected models in a single frame, producing a unified output.
|
||||
- **Auto resolution:** Ignores the preview resolution and viewport parameters, exporting output at the actual size of the content. For animations/videos, the output matches the size required for full visibility.
|
||||
|
||||
### Dynamic Wallpaper
|
||||
|
||||
Dynamic wallpaper is implemented through desktop projection, allowing the content of the current preview to be projected onto the desktop in real time.
|
||||
|
||||
You can enable or disable desktop projection from the program preferences or the right-click menu of the tray icon. After adjusting the model and display parameters, you can save the current configuration as a workspace file for convenient restoration later.
|
||||
|
||||
If you want the wallpaper to stay active after startup, you can enable auto-start in the preferences and specify which workspace file should be loaded when the program launches.
|
||||
|
||||
### More Information
|
||||
|
||||
@@ -119,12 +137,12 @@ For detailed usage and documentation, see the [Wiki](https://github.com/ww-rm/Sp
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
* [spine-runtimes](https://github.com/EsotericSoftware/spine-runtimes)
|
||||
* [SFML.Net](https://github.com/SFML/SFML.Net)
|
||||
* [FFMpegCore](https://github.com/rosenbjerg/FFMpegCore)
|
||||
* [HandyControl](https://github.com/HandyOrg/HandyControl)
|
||||
* [NLog](https://github.com/NLog/NLog)
|
||||
* [SkiaSharp](https://github.com/mono/SkiaSharp)
|
||||
- [spine-runtimes](https://github.com/EsotericSoftware/spine-runtimes)
|
||||
- [SFML.Net](https://github.com/SFML/SFML.Net)
|
||||
- [FFMpegCore](https://github.com/rosenbjerg/FFMpegCore)
|
||||
- [HandyControl](https://github.com/HandyOrg/HandyControl)
|
||||
- [NLog](https://github.com/NLog/NLog)
|
||||
- [SkiaSharp](https://github.com/mono/SkiaSharp)
|
||||
|
||||
---
|
||||
|
||||
|
||||
26
README.md
26
README.md
@@ -4,11 +4,15 @@
|
||||
[](https://github.com/ww-rm/SpineViewer/releases)
|
||||
[](https://github.com/ww-rm/SpineViewer/releases)
|
||||
|
||||

|
||||
|
||||
[中文](README.md) | [English](README.en.md)
|
||||
|
||||
一个简单好用的 Spine 文件查看&导出程序, 支持中/英/日多语言界面.
|
||||
Spine 文件查看&导出程序, 同时也是支持 Spine 的动态壁纸程序.
|
||||
|
||||

|
||||

|
||||
|
||||
https://github.com/user-attachments/assets/37b6b730-088a-4352-827a-c338127a16f0
|
||||
|
||||
## 功能
|
||||
|
||||
@@ -19,19 +23,27 @@
|
||||
- 支持列表多选批量设置骨骼参数
|
||||
- 支持多轨道动画设置
|
||||
- 支持皮肤/自定义插槽附件设置
|
||||
- 支持自定义插槽可见性
|
||||
- 支持调试渲染
|
||||
- 支持画面/模型/轨道时间倍速设置
|
||||
- 支持设置轨道 Alpha 混合参数
|
||||
- 支持全屏预览
|
||||
- 支持单帧/动图/视频文件导出
|
||||
- 支持自动分辨率批量导出
|
||||
- 支持 FFmpeg 自定义导出
|
||||
- 支持程序参数保存
|
||||
- ...
|
||||
- 支持文件后缀关联
|
||||
- 支持非 png 格式的纹理图片格式
|
||||
- 支持开机自启常驻动态壁纸
|
||||
- ......
|
||||
|
||||
### Spine 版本支持
|
||||
|
||||
| 版本 | 查看&导出 |
|
||||
| :---: | :---: |
|
||||
| `2.1.x` | :white_check_mark: |
|
||||
| `3.4.x` | :white_check_mark: |
|
||||
| `3.5.x` | :white_check_mark: |
|
||||
| `3.6.x` | :white_check_mark: |
|
||||
| `3.7.x` | :white_check_mark: |
|
||||
| `3.8.x` | :white_check_mark: |
|
||||
@@ -110,6 +122,14 @@
|
||||
- 导出单个. 默认是每个模型独立导出, 即对模型列表进行批量操作, 如果选择仅导出单个, 那么被导出的所有模型将在同一个画面上被渲染, 输出产物只有一份.
|
||||
- 自动分辨率. 该模式会忽略预览画面的分辨率和视区参数, 导出产物的分辨率与被导出内容的实际大小一致, 如果是动图或者视频则会与完整显示动画的必需大小一致.
|
||||
|
||||
### 动态壁纸
|
||||
|
||||
动态壁纸通过桌面投影实现, 可以将当前预览画面上的内容实时投影至桌面.
|
||||
|
||||
在程序首选项或者托盘图标右键菜单中可以进行桌面投影的启用与否, 模型和画面参数调整完成后, 可以将当前参数保存为工作区文件, 方便之后恢复该配置.
|
||||
|
||||
如果希望开机自启常驻壁纸, 也可以在首选项中启用开机自启, 并且设置启动后需要加载的工作区文件.
|
||||
|
||||
### 更多
|
||||
|
||||
更为详细的使用方法和说明见 [Wiki](https://github.com/ww-rm/SpineViewer/wiki), 有使用上的问题或者 BUG 可以提个 [Issue](https://github.com/ww-rm/SpineViewer/issues).
|
||||
|
||||
@@ -64,10 +64,10 @@ namespace SFMLRenderer
|
||||
hs?.Dispose();
|
||||
}
|
||||
|
||||
private nint HwndMessageHook(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
|
||||
private IntPtr HwndMessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
_renderWindow?.DispatchEvents();
|
||||
return nint.Zero;
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,8 +240,8 @@ namespace SFMLRenderer
|
||||
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 renderW = _resolution.X;
|
||||
float renderH = _resolution.Y;
|
||||
float scale = Math.Min(parentW / renderW, parentH / renderH); // 两方向取较小值, 保证 parent 覆盖 render
|
||||
renderW *= scale;
|
||||
renderH *= scale;
|
||||
|
||||
171
SFMLRenderer/SFMLRenderWindow.cs
Normal file
171
SFMLRenderer/SFMLRenderWindow.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
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;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace SFMLRenderer
|
||||
{
|
||||
public class SFMLRenderWindow : RenderWindow, ISFMLRenderer
|
||||
{
|
||||
private readonly DispatcherTimer _timer = new() { Interval = TimeSpan.FromMilliseconds(10) };
|
||||
|
||||
public SFMLRenderWindow(VideoMode mode, string title, Styles style) : base(mode, title, style)
|
||||
{
|
||||
SetActive(false);
|
||||
_timer.Tick += (s, e) => DispatchEvents();
|
||||
_timer.Start();
|
||||
RendererCreated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler? RendererCreated;
|
||||
|
||||
public event EventHandler? RendererDisposing
|
||||
{
|
||||
add => throw new NotImplementedException();
|
||||
remove => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public event EventHandler<MouseMoveEventArgs>? CanvasMouseMove
|
||||
{
|
||||
add { MouseMoved += value; }
|
||||
remove { MouseMoved -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasMouseButtonPressed
|
||||
{
|
||||
add { MouseButtonPressed += value; }
|
||||
remove { MouseButtonPressed -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasMouseButtonReleased
|
||||
{
|
||||
add { MouseButtonReleased += value; }
|
||||
remove { MouseButtonReleased -= value; }
|
||||
}
|
||||
|
||||
public event EventHandler<MouseWheelScrollEventArgs>? CanvasMouseWheelScrolled
|
||||
{
|
||||
add { MouseWheelScrolled += value; }
|
||||
remove { MouseWheelScrolled -= value; }
|
||||
}
|
||||
|
||||
public Vector2u Resolution
|
||||
{
|
||||
get => Size;
|
||||
set => Size = value;
|
||||
}
|
||||
|
||||
public Vector2f Center
|
||||
{
|
||||
get
|
||||
{
|
||||
using var view = GetView();
|
||||
return view.Center;
|
||||
}
|
||||
set
|
||||
{
|
||||
using var view = GetView();
|
||||
view.Center = value;
|
||||
SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get
|
||||
{
|
||||
using var view = GetView();
|
||||
return Math.Abs(Size.X / view.Size.X); // XXX: 仅使用宽度进行缩放计算
|
||||
}
|
||||
set
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
if (value <= 0) return;
|
||||
using var view = GetView();
|
||||
var signX = Math.Sign(view.Size.X);
|
||||
var signY = Math.Sign(view.Size.Y);
|
||||
var resolution = Size;
|
||||
view.Size = new(resolution.X / value * signX, resolution.Y / value * signY);
|
||||
SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
using var view = GetView();
|
||||
return view.Rotation;
|
||||
}
|
||||
set
|
||||
{
|
||||
using var view = GetView();
|
||||
view.Rotation = value;
|
||||
SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FlipX
|
||||
{
|
||||
get
|
||||
{
|
||||
using var view = GetView();
|
||||
return view.Size.X < 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
using var view = GetView();
|
||||
var size = view.Size;
|
||||
if (size.X > 0 && value || size.X < 0 && !value)
|
||||
size.X *= -1;
|
||||
view.Size = size;
|
||||
SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public bool FlipY
|
||||
{
|
||||
get
|
||||
{
|
||||
using var view = GetView();
|
||||
return view.Size.Y < 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
using var view = GetView();
|
||||
var size = view.Size;
|
||||
if (size.Y > 0 && value || size.Y < 0 && !value)
|
||||
size.Y *= -1;
|
||||
view.Size = size;
|
||||
SetView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public uint MaxFps
|
||||
{
|
||||
get => _maxFps;
|
||||
set
|
||||
{
|
||||
SetFramerateLimit(value);
|
||||
_maxFps = value;
|
||||
}
|
||||
}
|
||||
private uint _maxFps = 0;
|
||||
|
||||
public bool VerticalSync
|
||||
{
|
||||
get => _verticalSync;
|
||||
set
|
||||
{
|
||||
SetVerticalSyncEnabled(value);
|
||||
_verticalSync = value;
|
||||
}
|
||||
}
|
||||
private bool _verticalSync = false;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<Version>0.15.4</Version>
|
||||
<Version>0.16.6</Version>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -82,12 +82,12 @@ namespace Spine.Exporters
|
||||
_backgroundColorPma = bcPma;
|
||||
}
|
||||
}
|
||||
protected Color _backgroundColor = Color.Transparent;
|
||||
protected Color _backgroundColor = Color.Black;
|
||||
|
||||
/// <summary>
|
||||
/// 预乘后的背景颜色
|
||||
/// </summary>
|
||||
protected Color _backgroundColorPma = Color.Transparent;
|
||||
protected Color _backgroundColorPma = Color.Black;
|
||||
|
||||
/// <summary>
|
||||
/// 画面分辨率
|
||||
|
||||
@@ -60,8 +60,9 @@ namespace Spine.Exporters
|
||||
if (!string.IsNullOrEmpty(_codec)) options.WithVideoCodec(_codec);
|
||||
if (!string.IsNullOrEmpty(_pixelFormat)) options.ForcePixelFormat(_pixelFormat);
|
||||
if (!string.IsNullOrEmpty(_bitrate)) options.WithCustomArgument($"-b:v {_bitrate}");
|
||||
if (!string.IsNullOrEmpty(_filter)) options.WithCustomArgument($"-vf unpremultiply=inplace=1, {_customArgs}");
|
||||
if (!string.IsNullOrEmpty(_filter)) options.WithCustomArgument($"-vf \"unpremultiply=inplace=1, {_filter}\"");
|
||||
else options.WithCustomArgument("-vf unpremultiply=inplace=1");
|
||||
if (!string.IsNullOrEmpty(_customArgs)) options.WithCustomArgument($"{_customArgs}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -28,9 +28,11 @@ namespace Spine.Exporters
|
||||
{
|
||||
Gif,
|
||||
Webp,
|
||||
Apng,
|
||||
Mp4,
|
||||
Webm,
|
||||
Mkv,
|
||||
Mov,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -40,29 +42,41 @@ namespace Spine.Exporters
|
||||
private VideoFormat _format = VideoFormat.Mp4;
|
||||
|
||||
/// <summary>
|
||||
/// 动图是否循环
|
||||
/// [Gif/Webp/Apng] 动图是否循环
|
||||
/// </summary>
|
||||
public bool Loop { get => _loop; set => _loop = value; }
|
||||
private bool _loop = true;
|
||||
|
||||
/// <summary>
|
||||
/// 质量
|
||||
/// [Webp] 质量
|
||||
/// </summary>
|
||||
public int Quality { get => _quality; set => _quality = Math.Clamp(value, 0, 100); }
|
||||
private int _quality = 75;
|
||||
|
||||
/// <summary>
|
||||
/// 无损压缩 (Webp)
|
||||
/// [Webp] 无损压缩
|
||||
/// </summary>
|
||||
public bool Lossless { get => _lossless; set => _lossless = value; }
|
||||
private bool _lossless = false;
|
||||
|
||||
/// <summary>
|
||||
/// CRF
|
||||
/// [Apng] 预测器算法, 取值范围 0-5, 分别对应 none, sub, up, avg, paeth, mixed
|
||||
/// </summary>
|
||||
public int ApngPred { get => _apngPred; set => _apngPred = Math.Clamp(value, 0, 5); }
|
||||
private int _apngPred = 5;
|
||||
|
||||
/// <summary>
|
||||
/// [Mp4/Webm/Mkv] CRF
|
||||
/// </summary>
|
||||
public int Crf { get => _crf; set => _crf = Math.Clamp(value, 0, 63); }
|
||||
private int _crf = 23;
|
||||
|
||||
/// <summary>
|
||||
/// [Mov] prores_ks 编码器的配置等级, -1 是自动, 越高质量越好, 只有 4 及以上才有透明通道
|
||||
/// </summary>
|
||||
public int Profile { get => _profile; set => _profile = Math.Clamp(value, -1, 5); }
|
||||
private int _profile = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 获取的一帧, 结果是预乘的
|
||||
/// </summary>
|
||||
@@ -86,9 +100,11 @@ namespace Spine.Exporters
|
||||
{
|
||||
VideoFormat.Gif => SetGifOptions,
|
||||
VideoFormat.Webp => SetWebpOptions,
|
||||
VideoFormat.Apng => SetApngOptions,
|
||||
VideoFormat.Mp4 => SetMp4Options,
|
||||
VideoFormat.Webm => SetWebmOptions,
|
||||
VideoFormat.Mkv => SetMkvOptions,
|
||||
VideoFormat.Mov => SetMovOptions,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
@@ -110,8 +126,8 @@ namespace Spine.Exporters
|
||||
{
|
||||
// Gif 固定使用 256 调色板和 128 透明度阈值
|
||||
var v = "split [s0][s1]";
|
||||
var s0 = "[s0] palettegen=reserve_transparent=1:max_colors=256 [p]";
|
||||
var s1 = "[s1][p] paletteuse=dither=bayer:alpha_threshold=128";
|
||||
var s0 = "[s0] palettegen=max_colors=256 [p]";
|
||||
var s1 = "[s1][p] paletteuse=alpha_threshold=128";
|
||||
var customArgs = $"-vf \"unpremultiply=inplace=1, {v};{s0};{s1}\" -loop {(_loop ? 0 : -1)}";
|
||||
options.ForceFormat("gif")
|
||||
.WithCustomArgument(customArgs);
|
||||
@@ -124,6 +140,13 @@ namespace Spine.Exporters
|
||||
.WithCustomArgument(customArgs);
|
||||
}
|
||||
|
||||
private void SetApngOptions(FFMpegArgumentOptions options)
|
||||
{
|
||||
var customArgs = $"-vf unpremultiply=inplace=1 -plays {(_loop ? 0 : 1)} -pred {_apngPred}";
|
||||
options.ForceFormat("apng").WithVideoCodec("apng").ForcePixelFormat("rgba")
|
||||
.WithCustomArgument(customArgs);
|
||||
}
|
||||
|
||||
private void SetMp4Options(FFMpegArgumentOptions options)
|
||||
{
|
||||
// XXX: windows 默认播放器在播放 MP4 格式时对于 libx264 编码器只支持 yuv420p 的像素格式
|
||||
@@ -151,5 +174,13 @@ namespace Spine.Exporters
|
||||
.WithCustomArgument(customArgs);
|
||||
}
|
||||
|
||||
private void SetMovOptions(FFMpegArgumentOptions options)
|
||||
{
|
||||
var customArgs = "-vf unpremultiply=inplace=1";
|
||||
options.ForceFormat("mov").WithVideoCodec("prores_ks").ForcePixelFormat("yuva444p10le")
|
||||
.WithFastStart()
|
||||
.WithCustomArgument($"-profile {_profile}")
|
||||
.WithCustomArgument(customArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
using System;
|
||||
using NLog;
|
||||
using SFML.Graphics;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.SpineWrappers
|
||||
namespace Spine.Implementations
|
||||
{
|
||||
/// <summary>
|
||||
/// 实现不同版本的 TextureLoader
|
||||
/// </summary>
|
||||
public class TextureLoader :
|
||||
SpineRuntime21.TextureLoader,
|
||||
SpineRuntime34.TextureLoader,
|
||||
SpineRuntime35.TextureLoader,
|
||||
SpineRuntime36.TextureLoader,
|
||||
SpineRuntime37.TextureLoader,
|
||||
SpineRuntime38.TextureLoader,
|
||||
@@ -18,6 +23,8 @@ namespace Spine.SpineWrappers
|
||||
SpineRuntime41.TextureLoader,
|
||||
SpineRuntime42.TextureLoader
|
||||
{
|
||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
/// <summary>
|
||||
/// 默认的全局纹理加载器
|
||||
/// </summary>
|
||||
@@ -38,37 +45,38 @@ namespace Spine.SpineWrappers
|
||||
/// </summary>
|
||||
public bool ForceMipmap { get; set; }
|
||||
|
||||
private SFML.Graphics.Texture ReadTexture(string path)
|
||||
private Texture ReadTexture(string path)
|
||||
{
|
||||
if (ForcePremul)
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
using var image = new SFML.Graphics.Image(path);
|
||||
var width = image.Size.X;
|
||||
var height = image.Size.Y;
|
||||
var pixels = image.Pixels;
|
||||
var size = width * height * 4;
|
||||
for (int i = 0; i < size; i += 4)
|
||||
{
|
||||
byte a = pixels[i + 3];
|
||||
if (a == 0)
|
||||
{
|
||||
pixels[i + 0] = 0;
|
||||
pixels[i + 1] = 0;
|
||||
pixels[i + 2] = 0;
|
||||
}
|
||||
else if (a != 255)
|
||||
{
|
||||
float f = a / 255f;
|
||||
pixels[i + 0] = (byte)(pixels[i + 0] * f);
|
||||
pixels[i + 1] = (byte)(pixels[i + 1] * f);
|
||||
pixels[i + 2] = (byte)(pixels[i + 2] * f);
|
||||
}
|
||||
}
|
||||
var tex = new SFML.Graphics.Texture(width, height);
|
||||
tex.Update(pixels);
|
||||
return tex;
|
||||
_logger.Error($"Texture file not found, {path}");
|
||||
throw new FileNotFoundException("Texture file not found", path);
|
||||
}
|
||||
return new(path);
|
||||
|
||||
using var codec = SKCodec.Create(path, out var result);
|
||||
if (codec is null || result != SKCodecResult.Success)
|
||||
{
|
||||
_logger.Error($"Failed to create codec '{path}', {result}");
|
||||
throw new InvalidOperationException($"Failed to create codec '{path}', {result}");
|
||||
}
|
||||
|
||||
var width = codec.Info.Width;
|
||||
var height = codec.Info.Height;
|
||||
|
||||
// 判断是否需要强制预乘
|
||||
var alphaType = ForcePremul ? SKAlphaType.Premul : SKAlphaType.Unpremul;
|
||||
var info = new SKImageInfo(width, height, SKColorType.Rgba8888, alphaType);
|
||||
|
||||
result = codec.GetPixels(info, out var pixels);
|
||||
if (result != SKCodecResult.Success)
|
||||
{
|
||||
_logger.Error($"Failed to decode image '{path}', {result}");
|
||||
throw new InvalidOperationException($"Failed to decode image '{path}', {result}");
|
||||
}
|
||||
|
||||
Texture tex = new((uint)width, (uint)height);
|
||||
tex.Update(pixels);
|
||||
return tex;
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime21.AtlasPage page, string path)
|
||||
@@ -104,6 +112,100 @@ namespace Spine.SpineWrappers
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime34.AtlasPage page, string path)
|
||||
{
|
||||
var texture = ReadTexture(path);
|
||||
|
||||
if (page.magFilter == SpineRuntime34.TextureFilter.Linear)
|
||||
{
|
||||
texture.Smooth = true;
|
||||
}
|
||||
if (page.uWrap == SpineRuntime34.TextureWrap.Repeat && page.vWrap == SpineRuntime34.TextureWrap.Repeat)
|
||||
{
|
||||
texture.Repeated = true;
|
||||
}
|
||||
switch (page.minFilter)
|
||||
{
|
||||
case SpineRuntime34.TextureFilter.Linear:
|
||||
texture.Smooth = true;
|
||||
break;
|
||||
case SpineRuntime34.TextureFilter.MipMap:
|
||||
case SpineRuntime34.TextureFilter.MipMapNearestNearest:
|
||||
texture.GenerateMipmap();
|
||||
break;
|
||||
case SpineRuntime34.TextureFilter.MipMapLinearNearest:
|
||||
case SpineRuntime34.TextureFilter.MipMapNearestLinear:
|
||||
case SpineRuntime34.TextureFilter.MipMapLinearLinear:
|
||||
texture.Smooth = true;
|
||||
texture.GenerateMipmap();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ForceNearest) texture.Smooth = false;
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime35.AtlasPage page, string path)
|
||||
{
|
||||
var texture = ReadTexture(path);
|
||||
|
||||
if (page.magFilter == SpineRuntime35.TextureFilter.Linear)
|
||||
{
|
||||
texture.Smooth = true;
|
||||
}
|
||||
if (page.uWrap == SpineRuntime35.TextureWrap.Repeat && page.vWrap == SpineRuntime35.TextureWrap.Repeat)
|
||||
{
|
||||
texture.Repeated = true;
|
||||
}
|
||||
switch (page.minFilter)
|
||||
{
|
||||
case SpineRuntime35.TextureFilter.Linear:
|
||||
texture.Smooth = true;
|
||||
break;
|
||||
case SpineRuntime35.TextureFilter.MipMap:
|
||||
case SpineRuntime35.TextureFilter.MipMapNearestNearest:
|
||||
texture.GenerateMipmap();
|
||||
break;
|
||||
case SpineRuntime35.TextureFilter.MipMapLinearNearest:
|
||||
case SpineRuntime35.TextureFilter.MipMapNearestLinear:
|
||||
case SpineRuntime35.TextureFilter.MipMapLinearLinear:
|
||||
texture.Smooth = true;
|
||||
texture.GenerateMipmap();
|
||||
break;
|
||||
}
|
||||
|
||||
if (ForceNearest) texture.Smooth = false;
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime36.AtlasPage page, string path)
|
||||
@@ -139,6 +241,14 @@ namespace Spine.SpineWrappers
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime37.AtlasPage page, string path)
|
||||
@@ -174,6 +284,14 @@ namespace Spine.SpineWrappers
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime38.AtlasPage page, string path)
|
||||
@@ -210,9 +328,13 @@ namespace Spine.SpineWrappers
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 似乎是不需要设置的, 因为存在某些 png 和 atlas 大小不同的情况, 一般是有一些缩放, 如果设置了反而渲染异常
|
||||
// page.width = (int)texture.Size.X;
|
||||
// page.height = (int)texture.Size.Y;
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime40.AtlasPage page, string path)
|
||||
@@ -248,6 +370,14 @@ namespace Spine.SpineWrappers
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime41.AtlasPage page, string path)
|
||||
@@ -283,6 +413,14 @@ namespace Spine.SpineWrappers
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(SpineRuntime42.AtlasPage page, string path)
|
||||
@@ -318,11 +456,19 @@ namespace Spine.SpineWrappers
|
||||
if (ForceMipmap) texture.GenerateMipmap();
|
||||
|
||||
page.rendererObject = texture;
|
||||
|
||||
// 有些旧的 atlas 会省略 size 行, 这时需要在读取纹理时赋值
|
||||
if (page.width <= 0 || page.height <= 0)
|
||||
{
|
||||
var texSize = texture.Size;
|
||||
page.width = (int)texSize.X;
|
||||
page.height = (int)texSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Unload(object texture)
|
||||
{
|
||||
((SFML.Graphics.Texture)texture).Dispose();
|
||||
((Texture)texture).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime21;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
internal sealed class Animation21(Animation innerObject) : IAnimation
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime21;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
internal sealed class AnimationState21(AnimationState innerObject, SpineObjectData21 data) : IAnimationState
|
||||
{
|
||||
@@ -35,7 +35,7 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
@@ -64,7 +64,7 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
@@ -93,7 +93,7 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime21;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
namespace Spine.Implementations.V21.Attachments
|
||||
{
|
||||
internal abstract class Attachment21(Attachment innerObject) : IAttachment
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V21;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime21;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
namespace Spine.Implementations.V21.Attachments
|
||||
{
|
||||
internal sealed class BoundingBoxAttachment21(BoundingBoxAttachment innerObject) :
|
||||
Attachment21(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
|
||||
public override BoundingBoxAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot21 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V21;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime21;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
namespace Spine.Implementations.V21.Attachments
|
||||
{
|
||||
internal sealed class MeshAttachment21(MeshAttachment innerObject) :
|
||||
Attachment21(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
|
||||
public override MeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot21 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V21;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime21;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
namespace Spine.Implementations.V21.Attachments
|
||||
{
|
||||
internal sealed class RegionAttachment21(RegionAttachment innerObject) :
|
||||
Attachment21(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
|
||||
public override RegionAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot21 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V21;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime21;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
namespace Spine.Implementations.V21.Attachments
|
||||
{
|
||||
internal sealed class SkinnedMeshAttachment21(SkinnedMeshAttachment innerObject) :
|
||||
Attachment21(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V21.Attachments
|
||||
|
||||
public override SkinnedMeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot21 st)
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime21;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
internal sealed class Bone21(Bone innerObject, Bone21? parent = null) : IBone
|
||||
{
|
||||
@@ -5,10 +5,10 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime21;
|
||||
using Spine.Interfaces;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
internal sealed class Skeleton21 : ISkeleton
|
||||
{
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
@@ -95,11 +96,6 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
public void SetSlotsToSetupPose() => _o.SetSlotsToSetupPose();
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void GetBounds(out float x, out float y, out float w, out float h)
|
||||
{
|
||||
_o.GetBounds(out x, out y, out w, out h);
|
||||
}
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime21;
|
||||
using System;
|
||||
@@ -8,7 +8,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
internal sealed class SkeletonClipping21 : ISkeletonClipping
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime21;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
internal sealed class Skin21 : ISkin
|
||||
{
|
||||
@@ -5,10 +5,11 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime21;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
internal sealed class Slot21 : ISlot
|
||||
{
|
||||
@@ -39,7 +40,7 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public IBone Bone => _bone;
|
||||
|
||||
public Spine.SpineWrappers.Attachments.IAttachment? Attachment
|
||||
public IAttachment? Attachment
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -66,6 +67,8 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
}
|
||||
}
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime21;
|
||||
using Spine.Implementations.SpineWrappers.V21.Attachments;
|
||||
using Spine.Implementations.V21.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
[SpineImplementation(2, 1)]
|
||||
internal sealed class SpineObjectData21 : SpineObjectData
|
||||
@@ -26,12 +26,19 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
private readonly ImmutableArray<IAnimation> _animations;
|
||||
private readonly FrozenDictionary<string, IAnimation> _animationsByName;
|
||||
|
||||
public SpineObjectData21(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader)
|
||||
public SpineObjectData21(string skelPath, string atlasPath, TextureLoader textureLoader)
|
||||
: base(skelPath, atlasPath, textureLoader)
|
||||
{
|
||||
// 加载 atlas
|
||||
try { _atlas = new Atlas(atlasPath, textureLoader); }
|
||||
catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); }
|
||||
try
|
||||
{
|
||||
_atlas = new Atlas(atlasPath, textureLoader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -41,8 +48,9 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
@@ -52,8 +60,9 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +70,8 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
catch (Exception ex)
|
||||
{
|
||||
_atlas.Dispose();
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}", ex);
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
|
||||
}
|
||||
|
||||
// 加载动画数据
|
||||
@@ -1,4 +1,4 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime21;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V21
|
||||
namespace Spine.Implementations.V21
|
||||
{
|
||||
internal sealed class TrackEntry21(TrackEntry innerObject, AnimationState21 animationState, SpineObjectData21 data): ITrackEntry
|
||||
{
|
||||
@@ -34,7 +34,7 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
@@ -63,7 +63,7 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
@@ -92,7 +92,7 @@ namespace Spine.Implementations.SpineWrappers.V21
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
23
Spine/Implementations/V34/Animation34.cs
Normal file
23
Spine/Implementations/V34/Animation34.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime34;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
internal sealed class Animation34(Animation innerObject) : IAnimation
|
||||
{
|
||||
private readonly Animation _o = innerObject;
|
||||
|
||||
public Animation InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public float Duration => _o.Duration;
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
180
Spine/Implementations/V34/AnimationState34.cs
Normal file
180
Spine/Implementations/V34/AnimationState34.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
internal sealed class AnimationState34(AnimationState innerObject, SpineObjectData34 data) : IAnimationState
|
||||
{
|
||||
private readonly AnimationState _o = innerObject;
|
||||
private readonly SpineObjectData34 _data = data;
|
||||
|
||||
private readonly Dictionary<TrackEntry, TrackEntry34> _trackEntryPool = [];
|
||||
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, AnimationState.TrackEntryDelegate> _eventMapping = [];
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, int> _eventCount = [];
|
||||
|
||||
public AnimationState InnerObject => _o;
|
||||
|
||||
#pragma warning disable CS0067
|
||||
|
||||
// NOTE: 3.4 以下没有这两个事件
|
||||
public event IAnimationState.TrackEntryDelegate? Interrupt;
|
||||
public event IAnimationState.TrackEntryDelegate? Dispose;
|
||||
|
||||
#pragma warning restore CS0067
|
||||
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Start
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Start -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? End
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.End -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Complete
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Complete -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float TimeScale { get => _o.TimeScale; set => _o.TimeScale = value; }
|
||||
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void Apply(ISkeleton skeleton)
|
||||
{
|
||||
if (skeleton is Skeleton34 skel)
|
||||
{
|
||||
_o.Apply(skel.InnerObject);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {skeleton.GetType().Name}", nameof(skeleton));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 <see cref="ITrackEntry"/> 对象, 不存在则创建
|
||||
/// </summary>
|
||||
public ITrackEntry GetTrackEntry(TrackEntry trackEntry)
|
||||
{
|
||||
if (!_trackEntryPool.TryGetValue(trackEntry, out var tr))
|
||||
_trackEntryPool[trackEntry] = tr = new(trackEntry, this, _data);
|
||||
return tr;
|
||||
}
|
||||
|
||||
public IEnumerable<ITrackEntry?> IterTracks() => _o.Tracks.Select(t => t is null ? null : GetTrackEntry(t));
|
||||
|
||||
public ITrackEntry? GetCurrent(int index) { var t = _o.GetCurrent(index); return t is null ? null : GetTrackEntry(t); }
|
||||
|
||||
public void ClearTrack(int index) => _o.ClearTrack(index);
|
||||
|
||||
public void ClearTracks() => _o.ClearTracks();
|
||||
|
||||
public ITrackEntry SetAnimation(int trackIndex, string animationName, bool loop)
|
||||
=> GetTrackEntry(_o.SetAnimation(trackIndex, animationName, loop));
|
||||
|
||||
public ITrackEntry SetAnimation(int trackIndex, IAnimation animation, bool loop)
|
||||
{
|
||||
if (animation is Animation34 anime)
|
||||
return GetTrackEntry(_o.SetAnimation(trackIndex, anime.InnerObject, loop));
|
||||
throw new ArgumentException($"Received {animation.GetType().Name}", nameof(animation));
|
||||
}
|
||||
|
||||
public ITrackEntry SetEmptyAnimation(int trackIndex, float mixDuration) => GetTrackEntry(_o.SetEmptyAnimation(trackIndex, mixDuration));
|
||||
|
||||
public void SetEmptyAnimations(float mixDuration) => _o.SetEmptyAnimations(mixDuration);
|
||||
|
||||
public ITrackEntry AddAnimation(int trackIndex, string animationName, bool loop, float delay)
|
||||
=> GetTrackEntry(_o.AddAnimation(trackIndex, animationName, loop, delay));
|
||||
|
||||
public ITrackEntry AddAnimation(int trackIndex, IAnimation animation, bool loop, float delay)
|
||||
{
|
||||
if (animation is Animation34 anime)
|
||||
return GetTrackEntry(_o.AddAnimation(trackIndex, anime.InnerObject, loop, delay));
|
||||
throw new ArgumentException($"Received {animation.GetType().Name}", nameof(animation));
|
||||
}
|
||||
|
||||
public ITrackEntry AddEmptyAnimation(int trackIndex, float mixDuration, float delay)
|
||||
=> GetTrackEntry(_o.AddEmptyAnimation(trackIndex, mixDuration, delay));
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
24
Spine/Implementations/V34/Attachments/Attachment34.cs
Normal file
24
Spine/Implementations/V34/Attachments/Attachment34.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.V34.Attachments
|
||||
{
|
||||
internal abstract class Attachment34(Attachment innerObject) : IAttachment
|
||||
{
|
||||
private readonly Attachment _o = innerObject;
|
||||
|
||||
public virtual Attachment InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public abstract int ComputeWorldVertices(ISlot slot, ref float[] worldVertices);
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V34;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.V34.Attachments
|
||||
{
|
||||
internal sealed class BoundingBoxAttachment34(BoundingBoxAttachment innerObject) :
|
||||
Attachment34(innerObject),
|
||||
IBoundingBoxAttachment
|
||||
{
|
||||
private readonly BoundingBoxAttachment _o = innerObject;
|
||||
|
||||
public override BoundingBoxAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot34 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot34)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Spine/Implementations/V34/Attachments/MeshAttachment34.cs
Normal file
46
Spine/Implementations/V34/Attachments/MeshAttachment34.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V34;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.V34.Attachments
|
||||
{
|
||||
internal sealed class MeshAttachment34(MeshAttachment innerObject) :
|
||||
Attachment34(innerObject),
|
||||
IMeshAttachment
|
||||
{
|
||||
private readonly MeshAttachment _o = innerObject;
|
||||
|
||||
public override MeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot34 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot34)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
|
||||
public SFML.Graphics.Texture RendererObject => (SFML.Graphics.Texture)((AtlasRegion)_o.RendererObject).page.rendererObject;
|
||||
|
||||
public float[] UVs => _o.UVs;
|
||||
|
||||
public int[] Triangles => _o.Triangles;
|
||||
|
||||
public int HullLength => _o.HullLength;
|
||||
}
|
||||
}
|
||||
33
Spine/Implementations/V34/Attachments/PathAttachment34.cs
Normal file
33
Spine/Implementations/V34/Attachments/PathAttachment34.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V34;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.V34.Attachments
|
||||
{
|
||||
internal sealed class PathAttachment34(PathAttachment innerObject) :
|
||||
Attachment34(innerObject),
|
||||
IPathAttachment
|
||||
{
|
||||
private readonly PathAttachment _o = innerObject;
|
||||
|
||||
public override PathAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot34 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot34)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Spine/Implementations/V34/Attachments/RegionAttachment34.cs
Normal file
41
Spine/Implementations/V34/Attachments/RegionAttachment34.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V34;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.V34.Attachments
|
||||
{
|
||||
internal sealed class RegionAttachment34(RegionAttachment innerObject) :
|
||||
Attachment34(innerObject),
|
||||
IRegionAttachment
|
||||
{
|
||||
private readonly RegionAttachment _o = innerObject;
|
||||
|
||||
public override RegionAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot34 st)
|
||||
{
|
||||
if (worldVertices.Length < 8) worldVertices = new float[8];
|
||||
_o.ComputeWorldVertices(st.InnerObject.Bone, worldVertices);
|
||||
return 8;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot34)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
|
||||
public SFML.Graphics.Texture RendererObject => (SFML.Graphics.Texture)((AtlasRegion)_o.RendererObject).page.rendererObject;
|
||||
|
||||
public float[] UVs => _o.UVs;
|
||||
}
|
||||
}
|
||||
33
Spine/Implementations/V34/Bone34.cs
Normal file
33
Spine/Implementations/V34/Bone34.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
internal sealed class Bone34(Bone innerObject, Bone34? parent = null) : IBone
|
||||
{
|
||||
private readonly Bone _o = innerObject;
|
||||
private readonly Bone34? _parent = parent;
|
||||
|
||||
public Bone InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public int Index => _o.Data.Index;
|
||||
|
||||
public IBone? Parent => _parent;
|
||||
public bool Active => true; // NOTE: 3.7 及以下没有 Active 属性, 此处总是返回 true
|
||||
public float Length => _o.Data.Length;
|
||||
public float WorldX => _o.WorldX;
|
||||
public float WorldY => _o.WorldY;
|
||||
public float A => _o.A;
|
||||
public float B => _o.B;
|
||||
public float C => _o.C;
|
||||
public float D => _o.D;
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
101
Spine/Implementations/V34/Skeleton34.cs
Normal file
101
Spine/Implementations/V34/Skeleton34.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using SpineRuntime34;
|
||||
using Spine.Interfaces;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
internal sealed class Skeleton34 : ISkeleton
|
||||
{
|
||||
private readonly Skeleton _o;
|
||||
private readonly SpineObjectData34 _data;
|
||||
|
||||
private readonly ImmutableArray<IBone> _bones;
|
||||
private readonly FrozenDictionary<string, IBone> _bonesByName;
|
||||
private readonly ImmutableArray<ISlot> _slots;
|
||||
private readonly FrozenDictionary<string, ISlot> _slotsByName;
|
||||
|
||||
private Skin34? _skin;
|
||||
|
||||
public Skeleton34(Skeleton innerObject, SpineObjectData34 data)
|
||||
{
|
||||
_o = innerObject;
|
||||
_data = data;
|
||||
|
||||
List<Bone34> bones = [];
|
||||
Dictionary<string, IBone> bonesByName = [];
|
||||
foreach (var b in _o.Bones)
|
||||
{
|
||||
var bone = new Bone34(b, b.Parent is null ? null : bones[b.Parent.Data.Index]);
|
||||
bones.Add(bone);
|
||||
bonesByName[bone.Name] = bone;
|
||||
}
|
||||
_bones = bones.Cast<IBone>().ToImmutableArray();
|
||||
_bonesByName = bonesByName.ToFrozenDictionary();
|
||||
|
||||
List<Slot34> slots = [];
|
||||
Dictionary<string, ISlot> slotsByName = [];
|
||||
foreach (var s in _o.Slots)
|
||||
{
|
||||
var slot = new Slot34(s, _data, bones[s.Bone.Data.Index]);
|
||||
slots.Add(slot);
|
||||
slotsByName[slot.Name] = slot;
|
||||
}
|
||||
_slots = slots.Cast<ISlot>().ToImmutableArray();
|
||||
_slotsByName = slotsByName.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public float X { get => _o.X; set => _o.X = value; }
|
||||
public float Y { get => _o.Y; set => _o.Y = value; }
|
||||
public float ScaleX { get => _o.ScaleX; set => _o.ScaleX = value; }
|
||||
public float ScaleY { get => _o.ScaleY; set => _o.ScaleY = value; }
|
||||
|
||||
public ImmutableArray<IBone> Bones => _bones;
|
||||
public FrozenDictionary<string, IBone> BonesByName => _bonesByName;
|
||||
public ImmutableArray<ISlot> Slots => _slots;
|
||||
public FrozenDictionary<string, ISlot> SlotsByName => _slotsByName;
|
||||
|
||||
public ISkin? Skin
|
||||
{
|
||||
get => _skin;
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_o.Skin = null;
|
||||
_skin = null;
|
||||
return;
|
||||
}
|
||||
if (value is Skin34 sk)
|
||||
{
|
||||
_o.Skin = sk.InnerObject;
|
||||
_skin = sk;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {value.GetType().Name}", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ISlot> IterDrawOrder() => _o.DrawOrder.Select(s => _slots[s.Data.Index]);
|
||||
public void UpdateCache() => _o.UpdateCache();
|
||||
public void UpdateWorldTransform(ISkeleton.Physics physics) => _o.UpdateWorldTransform();
|
||||
public void SetToSetupPose() => _o.SetToSetupPose();
|
||||
public void SetBonesToSetupPose() => _o.SetBonesToSetupPose();
|
||||
public void SetSlotsToSetupPose() => _o.SetSlotsToSetupPose();
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
42
Spine/Implementations/V34/SkeletonClipping34.cs
Normal file
42
Spine/Implementations/V34/SkeletonClipping34.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime34;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
internal sealed class SkeletonClipping34 : ISkeletonClipping
|
||||
{
|
||||
public bool IsClipping => false;
|
||||
|
||||
public float[] ClippedVertices { get; private set; } = [];
|
||||
|
||||
public int ClippedVerticesLength { get; private set; } = 0;
|
||||
|
||||
public int[] ClippedTriangles { get; private set; } = [];
|
||||
|
||||
public int ClippedTrianglesLength { get; private set; } = 0;
|
||||
|
||||
public float[] ClippedUVs { get; private set; } = [];
|
||||
|
||||
public void ClipEnd(ISlot slot) { }
|
||||
|
||||
public void ClipEnd() { }
|
||||
|
||||
public void ClipStart(ISlot slot, IClippingAttachment clippingAttachment) { }
|
||||
|
||||
public void ClipTriangles(float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs)
|
||||
{
|
||||
ClippedVertices = vertices.ToArray();
|
||||
ClippedVerticesLength = verticesLength;
|
||||
ClippedTriangles = triangles.ToArray();
|
||||
ClippedTrianglesLength = trianglesLength;
|
||||
ClippedUVs = uvs.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Spine/Implementations/V34/Skin34.cs
Normal file
45
Spine/Implementations/V34/Skin34.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime34;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
internal sealed class Skin34 : ISkin
|
||||
{
|
||||
private readonly Skin _o;
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定名字创建空皮肤
|
||||
/// </summary>
|
||||
public Skin34(string name) => _o = new(name);
|
||||
|
||||
/// <summary>
|
||||
/// 包装已有皮肤对象
|
||||
/// </summary>
|
||||
public Skin34(Skin innerObject) => _o = innerObject;
|
||||
|
||||
public Skin InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public void AddSkin(ISkin skin)
|
||||
{
|
||||
if (skin is Skin34 sk)
|
||||
{
|
||||
// NOTE: 3.7 及以下不支持 AddSkin
|
||||
foreach (var (k, v) in sk._o.Attachments)
|
||||
_o.AddAttachment(k.slotIndex, k.name, v);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {skin.GetType().Name}", nameof(skin));
|
||||
}
|
||||
|
||||
public void Clear() => _o.Attachments.Clear();
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
81
Spine/Implementations/V34/Slot34.cs
Normal file
81
Spine/Implementations/V34/Slot34.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime34;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
internal sealed class Slot34 : ISlot
|
||||
{
|
||||
private readonly Slot _o;
|
||||
private readonly SpineObjectData34 _data;
|
||||
|
||||
private readonly Bone34 _bone;
|
||||
private readonly SFML.Graphics.BlendMode _blendMode;
|
||||
|
||||
public Slot34(Slot innerObject, SpineObjectData34 data, Bone34 bone)
|
||||
{
|
||||
_o = innerObject;
|
||||
_data = data;
|
||||
|
||||
_bone = bone;
|
||||
_blendMode = _o.Data.BlendMode switch
|
||||
{
|
||||
BlendMode.normal => SFMLBlendMode.NormalPma,
|
||||
BlendMode.additive => SFMLBlendMode.AdditivePma,
|
||||
BlendMode.multiply => SFMLBlendMode.MultiplyPma,
|
||||
BlendMode.screen => SFMLBlendMode.ScreenPma,
|
||||
_ => throw new NotImplementedException($"{_o.Data.BlendMode}"),
|
||||
};
|
||||
}
|
||||
|
||||
public Slot InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public int Index => _o.Data.Index;
|
||||
public SFML.Graphics.BlendMode Blend => _blendMode;
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public IBone Bone => _bone;
|
||||
|
||||
public IAttachment? Attachment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_o.Attachment is Attachment att)
|
||||
{
|
||||
return _data.SlotAttachments[Name][att.Name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_o.Attachment = null;
|
||||
return;
|
||||
}
|
||||
if (value is Attachments.Attachment34 att)
|
||||
{
|
||||
_o.Attachment = att.InnerObject;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {value.GetType().Name}", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
146
Spine/Implementations/V34/SpineObjectData34.cs
Normal file
146
Spine/Implementations/V34/SpineObjectData34.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime34;
|
||||
using Spine.Implementations.V34.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
[SpineImplementation(3, 4)]
|
||||
internal sealed class SpineObjectData34 : SpineObjectData
|
||||
{
|
||||
private readonly Atlas _atlas;
|
||||
private readonly SkeletonData _skeletonData;
|
||||
private readonly AnimationStateData _animationStateData;
|
||||
|
||||
private readonly ImmutableArray<ISkin> _skins;
|
||||
private readonly FrozenDictionary<string, ISkin> _skinsByName;
|
||||
private readonly FrozenDictionary<string, FrozenDictionary<string, IAttachment>> _slotAttachments;
|
||||
private readonly ImmutableArray<IAnimation> _animations;
|
||||
private readonly FrozenDictionary<string, IAnimation> _animationsByName;
|
||||
|
||||
public SpineObjectData34(string skelPath, string atlasPath, TextureLoader textureLoader)
|
||||
: base(skelPath, atlasPath, textureLoader)
|
||||
{
|
||||
// 加载 atlas
|
||||
try
|
||||
{
|
||||
_atlas = new Atlas(atlasPath, textureLoader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Utf8Validator.IsUtf8(skelPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_atlas.Dispose();
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
|
||||
}
|
||||
|
||||
// 加载动画数据
|
||||
_animationStateData = new AnimationStateData(_skeletonData);
|
||||
|
||||
// 整理皮肤和附件
|
||||
Dictionary<string, Dictionary<string, IAttachment>> slotAttachments = [];
|
||||
List<ISkin> skins = [];
|
||||
Dictionary<string, ISkin> skinsByName = [];
|
||||
foreach (var s in _skeletonData.Skins)
|
||||
{
|
||||
var skin = new Skin34(s);
|
||||
skins.Add(skin);
|
||||
skinsByName[s.Name] = skin;
|
||||
foreach (var (k, att) in s.Attachments)
|
||||
{
|
||||
var slotName = _skeletonData.Slots.Items[k.slotIndex].Name;
|
||||
if (!slotAttachments.TryGetValue(slotName, out var attachments))
|
||||
slotAttachments[slotName] = attachments = [];
|
||||
|
||||
attachments[att.Name] = att switch
|
||||
{
|
||||
RegionAttachment regionAtt => new RegionAttachment34(regionAtt),
|
||||
MeshAttachment meshAtt => new MeshAttachment34(meshAtt),
|
||||
BoundingBoxAttachment bbAtt => new BoundingBoxAttachment34(bbAtt),
|
||||
PathAttachment pathAtt => new PathAttachment34(pathAtt),
|
||||
_ => throw new InvalidOperationException($"Unrecognized attachment type {att.GetType().FullName}")
|
||||
};
|
||||
}
|
||||
}
|
||||
_slotAttachments = slotAttachments.ToFrozenDictionary(it => it.Key, it => it.Value.ToFrozenDictionary());
|
||||
_skins = skins.ToImmutableArray();
|
||||
_skinsByName = skinsByName.ToFrozenDictionary();
|
||||
|
||||
// 整理所有动画数据
|
||||
List<IAnimation> animations = [];
|
||||
Dictionary<string, IAnimation> animationsByName = [];
|
||||
foreach (var a in _skeletonData.Animations)
|
||||
{
|
||||
var anime = new Animation34(a);
|
||||
animations.Add(anime);
|
||||
animationsByName[anime.Name] = anime;
|
||||
}
|
||||
_animations = animations.ToImmutableArray();
|
||||
_animationsByName = animationsByName.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public override string SkeletonVersion => _skeletonData.Version;
|
||||
|
||||
public override ImmutableArray<ISkin> Skins => _skins;
|
||||
|
||||
public override FrozenDictionary<string, ISkin> SkinsByName => _skinsByName;
|
||||
|
||||
public override FrozenDictionary<string, FrozenDictionary<string, IAttachment>> SlotAttachments => _slotAttachments;
|
||||
|
||||
public override float DefaultMix { get => _animationStateData.DefaultMix; set => _animationStateData.DefaultMix = value; }
|
||||
|
||||
public override ImmutableArray<IAnimation> Animations => _animations;
|
||||
|
||||
public override FrozenDictionary<string, IAnimation> AnimationsByName => _animationsByName;
|
||||
|
||||
protected override void DisposeAtlas() => _atlas.Dispose();
|
||||
|
||||
public override ISkeleton CreateSkeleton() => new Skeleton34(new(_skeletonData), this);
|
||||
|
||||
public override IAnimationState CreateAnimationState() => new AnimationState34(new(_animationStateData), this);
|
||||
|
||||
public override ISkeletonClipping CreateSkeletonClipping() => new SkeletonClipping34();
|
||||
|
||||
public override ISkin CreateSkin(string name) => new Skin34(name);
|
||||
}
|
||||
}
|
||||
135
Spine/Implementations/V34/TrackEntry34.cs
Normal file
135
Spine/Implementations/V34/TrackEntry34.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime34;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.V34
|
||||
{
|
||||
internal sealed class TrackEntry34(TrackEntry innerObject, AnimationState34 animationState, SpineObjectData34 data): ITrackEntry
|
||||
{
|
||||
private readonly TrackEntry _o = innerObject;
|
||||
private readonly AnimationState34 _animationState = animationState;
|
||||
private readonly SpineObjectData34 _data = data;
|
||||
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, AnimationState.TrackEntryDelegate> _eventMapping = [];
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, int> _eventCount = [];
|
||||
|
||||
public TrackEntry InnerObject => _o;
|
||||
|
||||
#pragma warning disable CS0067
|
||||
|
||||
// 3.4 及以下没有这两个事件
|
||||
public event IAnimationState.TrackEntryDelegate? Interrupt;
|
||||
public event IAnimationState.TrackEntryDelegate? Dispose;
|
||||
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Start
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Start -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? End
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.End -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Complete
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Complete -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int TrackIndex { get => _o.TrackIndex; }
|
||||
|
||||
public IAnimation Animation { get => _data.AnimationsByName[_o.Animation.Name]; }
|
||||
|
||||
public ITrackEntry? Next { get { var t = _o.Next; return t is null ? null : _animationState.GetTrackEntry(t); } }
|
||||
|
||||
public bool Loop { get => _o.Loop; set => _o.Loop = value; }
|
||||
|
||||
public float TrackTime { get => _o.Time; set => _o.Time = value; }
|
||||
|
||||
public float TimeScale { get => _o.TimeScale; set => _o.TimeScale = value; }
|
||||
|
||||
public float Alpha { get => _o.Mix; set => _o.Mix = value; }
|
||||
|
||||
public float MixDuration { get => _o.MixDuration; set => _o.MixDuration = value; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
23
Spine/Implementations/V35/Animation35.cs
Normal file
23
Spine/Implementations/V35/Animation35.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime35;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
internal sealed class Animation35(Animation innerObject) : IAnimation
|
||||
{
|
||||
private readonly Animation _o = innerObject;
|
||||
|
||||
public Animation InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public float Duration => _o.Duration;
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
229
Spine/Implementations/V35/AnimationState35.cs
Normal file
229
Spine/Implementations/V35/AnimationState35.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
internal sealed class AnimationState35(AnimationState innerObject, SpineObjectData35 data) : IAnimationState
|
||||
{
|
||||
private readonly AnimationState _o = innerObject;
|
||||
private readonly SpineObjectData35 _data = data;
|
||||
|
||||
private readonly Dictionary<TrackEntry, TrackEntry35> _trackEntryPool = [];
|
||||
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, AnimationState.TrackEntryDelegate> _eventMapping = [];
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, int> _eventCount = [];
|
||||
|
||||
public AnimationState InnerObject => _o;
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Start
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Start -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Interrupt
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Interrupt -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? End
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.End -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Complete
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Complete -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Dispose
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Dispose -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float TimeScale { get => _o.TimeScale; set => _o.TimeScale = value; }
|
||||
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void Apply(ISkeleton skeleton)
|
||||
{
|
||||
if (skeleton is Skeleton35 skel)
|
||||
{
|
||||
_o.Apply(skel.InnerObject);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {skeleton.GetType().Name}", nameof(skeleton));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 <see cref="ITrackEntry"/> 对象, 不存在则创建
|
||||
/// </summary>
|
||||
public ITrackEntry GetTrackEntry(TrackEntry trackEntry)
|
||||
{
|
||||
if (!_trackEntryPool.TryGetValue(trackEntry, out var tr))
|
||||
_trackEntryPool[trackEntry] = tr = new(trackEntry, this, _data);
|
||||
return tr;
|
||||
}
|
||||
|
||||
public IEnumerable<ITrackEntry?> IterTracks() => _o.Tracks.Select(t => t is null ? null : GetTrackEntry(t));
|
||||
|
||||
public ITrackEntry? GetCurrent(int index) { var t = _o.GetCurrent(index); return t is null ? null : GetTrackEntry(t); }
|
||||
|
||||
public void ClearTrack(int index) => _o.ClearTrack(index);
|
||||
|
||||
public void ClearTracks() => _o.ClearTracks();
|
||||
|
||||
public ITrackEntry SetAnimation(int trackIndex, string animationName, bool loop)
|
||||
=> GetTrackEntry(_o.SetAnimation(trackIndex, animationName, loop));
|
||||
|
||||
public ITrackEntry SetAnimation(int trackIndex, IAnimation animation, bool loop)
|
||||
{
|
||||
if (animation is Animation35 anime)
|
||||
return GetTrackEntry(_o.SetAnimation(trackIndex, anime.InnerObject, loop));
|
||||
throw new ArgumentException($"Received {animation.GetType().Name}", nameof(animation));
|
||||
}
|
||||
|
||||
public ITrackEntry SetEmptyAnimation(int trackIndex, float mixDuration) => GetTrackEntry(_o.SetEmptyAnimation(trackIndex, mixDuration));
|
||||
|
||||
public void SetEmptyAnimations(float mixDuration) => _o.SetEmptyAnimations(mixDuration);
|
||||
|
||||
public ITrackEntry AddAnimation(int trackIndex, string animationName, bool loop, float delay)
|
||||
=> GetTrackEntry(_o.AddAnimation(trackIndex, animationName, loop, delay));
|
||||
|
||||
public ITrackEntry AddAnimation(int trackIndex, IAnimation animation, bool loop, float delay)
|
||||
{
|
||||
if (animation is Animation35 anime)
|
||||
return GetTrackEntry(_o.AddAnimation(trackIndex, anime.InnerObject, loop, delay));
|
||||
throw new ArgumentException($"Received {animation.GetType().Name}", nameof(animation));
|
||||
}
|
||||
|
||||
public ITrackEntry AddEmptyAnimation(int trackIndex, float mixDuration, float delay)
|
||||
=> GetTrackEntry(_o.AddEmptyAnimation(trackIndex, mixDuration, delay));
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
24
Spine/Implementations/V35/Attachments/Attachment35.cs
Normal file
24
Spine/Implementations/V35/Attachments/Attachment35.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35.Attachments
|
||||
{
|
||||
internal abstract class Attachment35(Attachment innerObject) : IAttachment
|
||||
{
|
||||
private readonly Attachment _o = innerObject;
|
||||
|
||||
public virtual Attachment InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public abstract int ComputeWorldVertices(ISlot slot, ref float[] worldVertices);
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V35;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35.Attachments
|
||||
{
|
||||
internal sealed class BoundingBoxAttachment35(BoundingBoxAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IBoundingBoxAttachment
|
||||
{
|
||||
private readonly BoundingBoxAttachment _o = innerObject;
|
||||
|
||||
public override BoundingBoxAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V35;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35.Attachments
|
||||
{
|
||||
internal sealed class ClippingAttachment35(ClippingAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IClippingAttachment
|
||||
{
|
||||
private readonly ClippingAttachment _o = innerObject;
|
||||
|
||||
public override ClippingAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Spine/Implementations/V35/Attachments/MeshAttachment35.cs
Normal file
46
Spine/Implementations/V35/Attachments/MeshAttachment35.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V35;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35.Attachments
|
||||
{
|
||||
internal sealed class MeshAttachment35(MeshAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IMeshAttachment
|
||||
{
|
||||
private readonly MeshAttachment _o = innerObject;
|
||||
|
||||
public override MeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
|
||||
public SFML.Graphics.Texture RendererObject => (SFML.Graphics.Texture)((AtlasRegion)_o.RendererObject).page.rendererObject;
|
||||
|
||||
public float[] UVs => _o.UVs;
|
||||
|
||||
public int[] Triangles => _o.Triangles;
|
||||
|
||||
public int HullLength => _o.HullLength;
|
||||
}
|
||||
}
|
||||
33
Spine/Implementations/V35/Attachments/PathAttachment35.cs
Normal file
33
Spine/Implementations/V35/Attachments/PathAttachment35.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V35;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35.Attachments
|
||||
{
|
||||
internal sealed class PathAttachment35(PathAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IPathAttachment
|
||||
{
|
||||
private readonly PathAttachment _o = innerObject;
|
||||
|
||||
public override PathAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
var length = _o.WorldVerticesLength;
|
||||
if (worldVertices.Length < length) worldVertices = new float[length];
|
||||
_o.ComputeWorldVertices(st.InnerObject, worldVertices);
|
||||
return length;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Spine/Implementations/V35/Attachments/PointAttachment35.cs
Normal file
32
Spine/Implementations/V35/Attachments/PointAttachment35.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V35;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35.Attachments
|
||||
{
|
||||
internal sealed class PointAttachment35(PointAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IPointAttachment
|
||||
{
|
||||
private readonly PointAttachment _o = innerObject;
|
||||
|
||||
public override PointAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
if (worldVertices.Length < 2) worldVertices = new float[2];
|
||||
_o.ComputeWorldPosition(st.InnerObject.Bone, out worldVertices[0], out worldVertices[1]);
|
||||
return 2;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Spine/Implementations/V35/Attachments/RegionAttachment35.cs
Normal file
41
Spine/Implementations/V35/Attachments/RegionAttachment35.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Implementations.V35;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35.Attachments
|
||||
{
|
||||
internal sealed class RegionAttachment35(RegionAttachment innerObject) :
|
||||
Attachment35(innerObject),
|
||||
IRegionAttachment
|
||||
{
|
||||
private readonly RegionAttachment _o = innerObject;
|
||||
|
||||
public override RegionAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
if (worldVertices.Length < 8) worldVertices = new float[8];
|
||||
_o.ComputeWorldVertices(st.InnerObject.Bone, worldVertices, 0);
|
||||
return 8;
|
||||
}
|
||||
throw new ArgumentException($"Invalid slot type. Expected {nameof(Slot35)}, but received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
|
||||
public SFML.Graphics.Texture RendererObject => (SFML.Graphics.Texture)((AtlasRegion)_o.RendererObject).page.rendererObject;
|
||||
|
||||
public float[] UVs => _o.UVs;
|
||||
}
|
||||
}
|
||||
33
Spine/Implementations/V35/Bone35.cs
Normal file
33
Spine/Implementations/V35/Bone35.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
internal sealed class Bone35(Bone innerObject, Bone35? parent = null) : IBone
|
||||
{
|
||||
private readonly Bone _o = innerObject;
|
||||
private readonly Bone35? _parent = parent;
|
||||
|
||||
public Bone InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public int Index => _o.Data.Index;
|
||||
|
||||
public IBone? Parent => _parent;
|
||||
public bool Active => true; // NOTE: 3.7 及以下没有 Active 属性, 此处总是返回 true
|
||||
public float Length => _o.Data.Length;
|
||||
public float WorldX => _o.WorldX;
|
||||
public float WorldY => _o.WorldY;
|
||||
public float A => _o.A;
|
||||
public float B => _o.B;
|
||||
public float C => _o.C;
|
||||
public float D => _o.D;
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
101
Spine/Implementations/V35/Skeleton35.cs
Normal file
101
Spine/Implementations/V35/Skeleton35.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using SpineRuntime35;
|
||||
using Spine.Interfaces;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
internal sealed class Skeleton35 : ISkeleton
|
||||
{
|
||||
private readonly Skeleton _o;
|
||||
private readonly SpineObjectData35 _data;
|
||||
|
||||
private readonly ImmutableArray<IBone> _bones;
|
||||
private readonly FrozenDictionary<string, IBone> _bonesByName;
|
||||
private readonly ImmutableArray<ISlot> _slots;
|
||||
private readonly FrozenDictionary<string, ISlot> _slotsByName;
|
||||
|
||||
private Skin35? _skin;
|
||||
|
||||
public Skeleton35(Skeleton innerObject, SpineObjectData35 data)
|
||||
{
|
||||
_o = innerObject;
|
||||
_data = data;
|
||||
|
||||
List<Bone35> bones = [];
|
||||
Dictionary<string, IBone> bonesByName = [];
|
||||
foreach (var b in _o.Bones)
|
||||
{
|
||||
var bone = new Bone35(b, b.Parent is null ? null : bones[b.Parent.Data.Index]);
|
||||
bones.Add(bone);
|
||||
bonesByName[bone.Name] = bone;
|
||||
}
|
||||
_bones = bones.Cast<IBone>().ToImmutableArray();
|
||||
_bonesByName = bonesByName.ToFrozenDictionary();
|
||||
|
||||
List<Slot35> slots = [];
|
||||
Dictionary<string, ISlot> slotsByName = [];
|
||||
foreach (var s in _o.Slots)
|
||||
{
|
||||
var slot = new Slot35(s, _data, bones[s.Bone.Data.Index]);
|
||||
slots.Add(slot);
|
||||
slotsByName[slot.Name] = slot;
|
||||
}
|
||||
_slots = slots.Cast<ISlot>().ToImmutableArray();
|
||||
_slotsByName = slotsByName.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public float X { get => _o.X; set => _o.X = value; }
|
||||
public float Y { get => _o.Y; set => _o.Y = value; }
|
||||
public float ScaleX { get => _o.ScaleX; set => _o.ScaleX = value; }
|
||||
public float ScaleY { get => _o.ScaleY; set => _o.ScaleY = value; }
|
||||
|
||||
public ImmutableArray<IBone> Bones => _bones;
|
||||
public FrozenDictionary<string, IBone> BonesByName => _bonesByName;
|
||||
public ImmutableArray<ISlot> Slots => _slots;
|
||||
public FrozenDictionary<string, ISlot> SlotsByName => _slotsByName;
|
||||
|
||||
public ISkin? Skin
|
||||
{
|
||||
get => _skin;
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_o.Skin = null;
|
||||
_skin = null;
|
||||
return;
|
||||
}
|
||||
if (value is Skin35 sk)
|
||||
{
|
||||
_o.Skin = sk.InnerObject;
|
||||
_skin = sk;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {value.GetType().Name}", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ISlot> IterDrawOrder() => _o.DrawOrder.Select(s => _slots[s.Data.Index]);
|
||||
public void UpdateCache() => _o.UpdateCache();
|
||||
public void UpdateWorldTransform(ISkeleton.Physics physics) => _o.UpdateWorldTransform();
|
||||
public void SetToSetupPose() => _o.SetToSetupPose();
|
||||
public void SetBonesToSetupPose() => _o.SetBonesToSetupPose();
|
||||
public void SetSlotsToSetupPose() => _o.SetSlotsToSetupPose();
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
56
Spine/Implementations/V35/SkeletonClipping35.cs
Normal file
56
Spine/Implementations/V35/SkeletonClipping35.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime35;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
internal sealed class SkeletonClipping35 : ISkeletonClipping
|
||||
{
|
||||
private readonly SkeletonClipping _o = new();
|
||||
|
||||
public bool IsClipping => _o.IsClipping();
|
||||
|
||||
public float[] ClippedVertices => _o.ClippedVertices.Items;
|
||||
|
||||
public int ClippedVerticesLength => _o.ClippedVertices.Count;
|
||||
|
||||
public int[] ClippedTriangles => _o.ClippedTriangles.Items;
|
||||
|
||||
public int ClippedTrianglesLength => _o.ClippedTriangles.Count;
|
||||
|
||||
public float[] ClippedUVs => _o.ClippedUVs.Items;
|
||||
|
||||
public void ClipTriangles(float[] vertices, int verticesLength, int[] triangles, int trianglesLength, float[] uvs)
|
||||
=> _o.ClipTriangles(vertices, verticesLength, triangles, trianglesLength, uvs);
|
||||
|
||||
public void ClipStart(ISlot slot, IClippingAttachment clippingAttachment)
|
||||
{
|
||||
if (slot is Slot35 st && clippingAttachment is Attachments.ClippingAttachment35 att)
|
||||
{
|
||||
_o.ClipStart(st.InnerObject, att.InnerObject);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {slot.GetType().Name} {clippingAttachment.GetType().Name}");
|
||||
}
|
||||
|
||||
public void ClipEnd(ISlot slot)
|
||||
{
|
||||
if (slot is Slot35 st)
|
||||
{
|
||||
_o.ClipEnd(st.InnerObject);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {slot.GetType().Name}", nameof(slot));
|
||||
}
|
||||
|
||||
public void ClipEnd() => _o.ClipEnd();
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
45
Spine/Implementations/V35/Skin35.cs
Normal file
45
Spine/Implementations/V35/Skin35.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime35;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
internal sealed class Skin35 : ISkin
|
||||
{
|
||||
private readonly Skin _o;
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定名字创建空皮肤
|
||||
/// </summary>
|
||||
public Skin35(string name) => _o = new(name);
|
||||
|
||||
/// <summary>
|
||||
/// 包装已有皮肤对象
|
||||
/// </summary>
|
||||
public Skin35(Skin innerObject) => _o = innerObject;
|
||||
|
||||
public Skin InnerObject => _o;
|
||||
|
||||
public string Name => _o.Name;
|
||||
|
||||
public void AddSkin(ISkin skin)
|
||||
{
|
||||
if (skin is Skin35 sk)
|
||||
{
|
||||
// NOTE: 3.7 及以下不支持 AddSkin
|
||||
foreach (var (k, v) in sk._o.Attachments)
|
||||
_o.AddAttachment(k.slotIndex, k.name, v);
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {skin.GetType().Name}", nameof(skin));
|
||||
}
|
||||
|
||||
public void Clear() => _o.Attachments.Clear();
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
81
Spine/Implementations/V35/Slot35.cs
Normal file
81
Spine/Implementations/V35/Slot35.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime35;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
internal sealed class Slot35 : ISlot
|
||||
{
|
||||
private readonly Slot _o;
|
||||
private readonly SpineObjectData35 _data;
|
||||
|
||||
private readonly Bone35 _bone;
|
||||
private readonly SFML.Graphics.BlendMode _blendMode;
|
||||
|
||||
public Slot35(Slot innerObject, SpineObjectData35 data, Bone35 bone)
|
||||
{
|
||||
_o = innerObject;
|
||||
_data = data;
|
||||
|
||||
_bone = bone;
|
||||
_blendMode = _o.Data.BlendMode switch
|
||||
{
|
||||
BlendMode.Normal => SFMLBlendMode.NormalPma,
|
||||
BlendMode.Additive => SFMLBlendMode.AdditivePma,
|
||||
BlendMode.Multiply => SFMLBlendMode.MultiplyPma,
|
||||
BlendMode.Screen => SFMLBlendMode.ScreenPma,
|
||||
_ => throw new NotImplementedException($"{_o.Data.BlendMode}"),
|
||||
};
|
||||
}
|
||||
|
||||
public Slot InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public int Index => _o.Data.Index;
|
||||
public SFML.Graphics.BlendMode Blend => _blendMode;
|
||||
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public IBone Bone => _bone;
|
||||
|
||||
public IAttachment? Attachment
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_o.Attachment is Attachment att)
|
||||
{
|
||||
return _data.SlotAttachments[Name][att.Name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
_o.Attachment = null;
|
||||
return;
|
||||
}
|
||||
if (value is Attachments.Attachment35 att)
|
||||
{
|
||||
_o.Attachment = att.InnerObject;
|
||||
return;
|
||||
}
|
||||
throw new ArgumentException($"Received {value.GetType().Name}", nameof(value));
|
||||
}
|
||||
}
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
148
Spine/Implementations/V35/SpineObjectData35.cs
Normal file
148
Spine/Implementations/V35/SpineObjectData35.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime35;
|
||||
using Spine.Implementations.V35.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
[SpineImplementation(3, 5)]
|
||||
internal sealed class SpineObjectData35 : SpineObjectData
|
||||
{
|
||||
private readonly Atlas _atlas;
|
||||
private readonly SkeletonData _skeletonData;
|
||||
private readonly AnimationStateData _animationStateData;
|
||||
|
||||
private readonly ImmutableArray<ISkin> _skins;
|
||||
private readonly FrozenDictionary<string, ISkin> _skinsByName;
|
||||
private readonly FrozenDictionary<string, FrozenDictionary<string, IAttachment>> _slotAttachments;
|
||||
private readonly ImmutableArray<IAnimation> _animations;
|
||||
private readonly FrozenDictionary<string, IAnimation> _animationsByName;
|
||||
|
||||
public SpineObjectData35(string skelPath, string atlasPath, TextureLoader textureLoader)
|
||||
: base(skelPath, atlasPath, textureLoader)
|
||||
{
|
||||
// 加载 atlas
|
||||
try
|
||||
{
|
||||
_atlas = new Atlas(atlasPath, textureLoader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Utf8Validator.IsUtf8(skelPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_atlas.Dispose();
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
|
||||
}
|
||||
|
||||
// 加载动画数据
|
||||
_animationStateData = new AnimationStateData(_skeletonData);
|
||||
|
||||
// 整理皮肤和附件
|
||||
Dictionary<string, Dictionary<string, IAttachment>> slotAttachments = [];
|
||||
List<ISkin> skins = [];
|
||||
Dictionary<string, ISkin> skinsByName = [];
|
||||
foreach (var s in _skeletonData.Skins)
|
||||
{
|
||||
var skin = new Skin35(s);
|
||||
skins.Add(skin);
|
||||
skinsByName[s.Name] = skin;
|
||||
foreach (var (k, att) in s.Attachments)
|
||||
{
|
||||
var slotName = _skeletonData.Slots.Items[k.slotIndex].Name;
|
||||
if (!slotAttachments.TryGetValue(slotName, out var attachments))
|
||||
slotAttachments[slotName] = attachments = [];
|
||||
|
||||
attachments[att.Name] = att switch
|
||||
{
|
||||
RegionAttachment regionAtt => new RegionAttachment35(regionAtt),
|
||||
MeshAttachment meshAtt => new MeshAttachment35(meshAtt),
|
||||
ClippingAttachment clipAtt => new ClippingAttachment35(clipAtt),
|
||||
BoundingBoxAttachment bbAtt => new BoundingBoxAttachment35(bbAtt),
|
||||
PathAttachment pathAtt => new PathAttachment35(pathAtt),
|
||||
PointAttachment ptAtt => new PointAttachment35(ptAtt),
|
||||
_ => throw new InvalidOperationException($"Unrecognized attachment type {att.GetType().FullName}")
|
||||
};
|
||||
}
|
||||
}
|
||||
_slotAttachments = slotAttachments.ToFrozenDictionary(it => it.Key, it => it.Value.ToFrozenDictionary());
|
||||
_skins = skins.ToImmutableArray();
|
||||
_skinsByName = skinsByName.ToFrozenDictionary();
|
||||
|
||||
// 整理所有动画数据
|
||||
List<IAnimation> animations = [];
|
||||
Dictionary<string, IAnimation> animationsByName = [];
|
||||
foreach (var a in _skeletonData.Animations)
|
||||
{
|
||||
var anime = new Animation35(a);
|
||||
animations.Add(anime);
|
||||
animationsByName[anime.Name] = anime;
|
||||
}
|
||||
_animations = animations.ToImmutableArray();
|
||||
_animationsByName = animationsByName.ToFrozenDictionary();
|
||||
}
|
||||
|
||||
public override string SkeletonVersion => _skeletonData.Version;
|
||||
|
||||
public override ImmutableArray<ISkin> Skins => _skins;
|
||||
|
||||
public override FrozenDictionary<string, ISkin> SkinsByName => _skinsByName;
|
||||
|
||||
public override FrozenDictionary<string, FrozenDictionary<string, IAttachment>> SlotAttachments => _slotAttachments;
|
||||
|
||||
public override float DefaultMix { get => _animationStateData.DefaultMix; set => _animationStateData.DefaultMix = value; }
|
||||
|
||||
public override ImmutableArray<IAnimation> Animations => _animations;
|
||||
|
||||
public override FrozenDictionary<string, IAnimation> AnimationsByName => _animationsByName;
|
||||
|
||||
protected override void DisposeAtlas() => _atlas.Dispose();
|
||||
|
||||
public override ISkeleton CreateSkeleton() => new Skeleton35(new(_skeletonData), this);
|
||||
|
||||
public override IAnimationState CreateAnimationState() => new AnimationState35(new(_animationStateData), this);
|
||||
|
||||
public override ISkeletonClipping CreateSkeletonClipping() => new SkeletonClipping35();
|
||||
|
||||
public override ISkin CreateSkin(string name) => new Skin35(name);
|
||||
}
|
||||
}
|
||||
185
Spine/Implementations/V35/TrackEntry35.cs
Normal file
185
Spine/Implementations/V35/TrackEntry35.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime35;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.V35
|
||||
{
|
||||
internal sealed class TrackEntry35(TrackEntry innerObject, AnimationState35 animationState, SpineObjectData35 data): ITrackEntry
|
||||
{
|
||||
private readonly TrackEntry _o = innerObject;
|
||||
private readonly AnimationState35 _animationState = animationState;
|
||||
private readonly SpineObjectData35 _data = data;
|
||||
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, AnimationState.TrackEntryDelegate> _eventMapping = [];
|
||||
private readonly Dictionary<IAnimationState.TrackEntryDelegate, int> _eventCount = [];
|
||||
|
||||
public TrackEntry InnerObject => _o;
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Start
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Start -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Interrupt
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Interrupt -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? End
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.End -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Complete
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Complete -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event IAnimationState.TrackEntryDelegate? Dispose
|
||||
{
|
||||
add
|
||||
{
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
_eventCount[value]++;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (value is null) return;
|
||||
if (_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_o.Dispose -= f;
|
||||
_eventCount[value]--;
|
||||
if (_eventCount[value] <= 0)
|
||||
{
|
||||
_eventMapping.Remove(value);
|
||||
_eventCount.Remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int TrackIndex { get => _o.TrackIndex; }
|
||||
|
||||
public IAnimation Animation { get => _data.AnimationsByName[_o.Animation.Name]; }
|
||||
|
||||
public ITrackEntry? Next { get { var t = _o.Next; return t is null ? null : _animationState.GetTrackEntry(t); } }
|
||||
|
||||
public bool Loop { get => _o.Loop; set => _o.Loop = value; }
|
||||
|
||||
public float TrackTime { get => _o.TrackTime; set => _o.TrackTime = value; }
|
||||
|
||||
public float TimeScale { get => _o.TimeScale; set => _o.TimeScale = value; }
|
||||
|
||||
public float Alpha { get => _o.Alpha; set => _o.Alpha = value; }
|
||||
|
||||
public float MixDuration { get => _o.MixDuration; set => _o.MixDuration = value; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime36;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
internal sealed class Animation36(Animation innerObject) : IAnimation
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
internal sealed class AnimationState36(AnimationState innerObject, SpineObjectData36 data) : IAnimationState
|
||||
{
|
||||
@@ -27,7 +27,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
@@ -56,7 +56,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
@@ -85,7 +85,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
@@ -114,7 +114,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
@@ -143,7 +143,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
namespace Spine.Implementations.V36.Attachments
|
||||
{
|
||||
internal abstract class Attachment36(Attachment innerObject) : IAttachment
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V36;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
namespace Spine.Implementations.V36.Attachments
|
||||
{
|
||||
internal sealed class BoundingBoxAttachment36(BoundingBoxAttachment innerObject) :
|
||||
Attachment36(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
|
||||
public override BoundingBoxAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot36 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V36;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
namespace Spine.Implementations.V36.Attachments
|
||||
{
|
||||
internal sealed class ClippingAttachment36(ClippingAttachment innerObject) :
|
||||
Attachment36(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
|
||||
public override ClippingAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot36 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V36;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
namespace Spine.Implementations.V36.Attachments
|
||||
{
|
||||
internal sealed class MeshAttachment36(MeshAttachment innerObject) :
|
||||
Attachment36(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
|
||||
public override MeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot36 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V36;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
namespace Spine.Implementations.V36.Attachments
|
||||
{
|
||||
internal sealed class PathAttachment36(PathAttachment innerObject) :
|
||||
Attachment36(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
|
||||
public override PathAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot36 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V36;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
namespace Spine.Implementations.V36.Attachments
|
||||
{
|
||||
internal sealed class PointAttachment36(PointAttachment innerObject) :
|
||||
Attachment36(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
|
||||
public override PointAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot36 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V36;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
namespace Spine.Implementations.V36.Attachments
|
||||
{
|
||||
internal sealed class RegionAttachment36(RegionAttachment innerObject) :
|
||||
Attachment36(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V36.Attachments
|
||||
|
||||
public override RegionAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot36 st)
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
internal sealed class Bone36(Bone innerObject, Bone36? parent = null) : IBone
|
||||
{
|
||||
@@ -5,10 +5,10 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime36;
|
||||
using Spine.Interfaces;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
internal sealed class Skeleton36 : ISkeleton
|
||||
{
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
@@ -95,12 +96,6 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
public void SetSlotsToSetupPose() => _o.SetSlotsToSetupPose();
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void GetBounds(out float x, out float y, out float w, out float h)
|
||||
{
|
||||
float[] _ = [];
|
||||
_o.GetBounds(out x, out y, out w, out h, ref _);
|
||||
}
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime36;
|
||||
using System;
|
||||
@@ -8,7 +8,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
internal sealed class SkeletonClipping36 : ISkeletonClipping
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime36;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
internal sealed class Skin36 : ISkin
|
||||
{
|
||||
@@ -5,10 +5,11 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime36;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
internal sealed class Slot36 : ISlot
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public IBone Bone => _bone;
|
||||
|
||||
public Spine.SpineWrappers.Attachments.IAttachment? Attachment
|
||||
public IAttachment? Attachment
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -73,6 +74,8 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
}
|
||||
}
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime36;
|
||||
using Spine.Implementations.SpineWrappers.V36.Attachments;
|
||||
using Spine.Implementations.V36.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
[SpineImplementation(3, 6)]
|
||||
internal sealed class SpineObjectData36 : SpineObjectData
|
||||
@@ -26,12 +26,19 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
private readonly ImmutableArray<IAnimation> _animations;
|
||||
private readonly FrozenDictionary<string, IAnimation> _animationsByName;
|
||||
|
||||
public SpineObjectData36(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader)
|
||||
public SpineObjectData36(string skelPath, string atlasPath, TextureLoader textureLoader)
|
||||
: base(skelPath, atlasPath, textureLoader)
|
||||
{
|
||||
// 加载 atlas
|
||||
try { _atlas = new Atlas(atlasPath, textureLoader); }
|
||||
catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); }
|
||||
try
|
||||
{
|
||||
_atlas = new Atlas(atlasPath, textureLoader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -41,8 +48,9 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
@@ -52,8 +60,9 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +70,8 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
catch (Exception ex)
|
||||
{
|
||||
_atlas.Dispose();
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}", ex);
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
|
||||
}
|
||||
|
||||
// 加载动画数据
|
||||
@@ -1,4 +1,4 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime36;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V36
|
||||
namespace Spine.Implementations.V36
|
||||
{
|
||||
internal sealed class TrackEntry36(TrackEntry innerObject, AnimationState36 animationState, SpineObjectData36 data): ITrackEntry
|
||||
{
|
||||
@@ -26,7 +26,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
@@ -55,7 +55,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
@@ -84,7 +84,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
@@ -113,7 +113,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
@@ -142,7 +142,7 @@ namespace Spine.Implementations.SpineWrappers.V36
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
@@ -1,4 +1,4 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime37;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
internal sealed class Animation37(Animation innerObject) : IAnimation
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
internal sealed class AnimationState37(AnimationState innerObject, SpineObjectData37 data) : IAnimationState
|
||||
{
|
||||
@@ -27,7 +27,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
@@ -56,7 +56,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
@@ -85,7 +85,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
@@ -114,7 +114,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
@@ -143,7 +143,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
namespace Spine.Implementations.V37.Attachments
|
||||
{
|
||||
internal abstract class Attachment37(Attachment innerObject) : IAttachment
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V37;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
namespace Spine.Implementations.V37.Attachments
|
||||
{
|
||||
internal sealed class BoundingBoxAttachment37(BoundingBoxAttachment innerObject) :
|
||||
Attachment37(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
|
||||
public override BoundingBoxAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot37 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V37;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
namespace Spine.Implementations.V37.Attachments
|
||||
{
|
||||
internal sealed class ClippingAttachment37(ClippingAttachment innerObject) :
|
||||
Attachment37(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
|
||||
public override ClippingAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot37 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V37;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
namespace Spine.Implementations.V37.Attachments
|
||||
{
|
||||
internal sealed class MeshAttachment37(MeshAttachment innerObject) :
|
||||
Attachment37(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
|
||||
public override MeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot37 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V37;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
namespace Spine.Implementations.V37.Attachments
|
||||
{
|
||||
internal sealed class PathAttachment37(PathAttachment innerObject) :
|
||||
Attachment37(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
|
||||
public override PathAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot37 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V37;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
namespace Spine.Implementations.V37.Attachments
|
||||
{
|
||||
internal sealed class PointAttachment37(PointAttachment innerObject) :
|
||||
Attachment37(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
|
||||
public override PointAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot37 st)
|
||||
{
|
||||
@@ -3,10 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V37;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
namespace Spine.Implementations.V37.Attachments
|
||||
{
|
||||
internal sealed class RegionAttachment37(RegionAttachment innerObject) :
|
||||
Attachment37(innerObject),
|
||||
@@ -16,7 +18,7 @@ namespace Spine.Implementations.SpineWrappers.V37.Attachments
|
||||
|
||||
public override RegionAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot37 st)
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
internal sealed class Bone37(Bone innerObject, Bone37? parent = null) : IBone
|
||||
{
|
||||
@@ -5,10 +5,10 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Immutable;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime37;
|
||||
using Spine.Interfaces;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
internal sealed class Skeleton37 : ISkeleton
|
||||
{
|
||||
@@ -52,6 +52,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
|
||||
public Skeleton InnerObject => _o;
|
||||
|
||||
public string Name => _o.Data.Name;
|
||||
public float R { get => _o.R; set => _o.R = value; }
|
||||
public float G { get => _o.G; set => _o.G = value; }
|
||||
public float B { get => _o.B; set => _o.B = value; }
|
||||
@@ -95,12 +96,6 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
public void SetSlotsToSetupPose() => _o.SetSlotsToSetupPose();
|
||||
public void Update(float delta) => _o.Update(delta);
|
||||
|
||||
public void GetBounds(out float x, out float y, out float w, out float h)
|
||||
{
|
||||
float[] _ = [];
|
||||
_o.GetBounds(out x, out y, out w, out h, ref _);
|
||||
}
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using Spine.Utils;
|
||||
using SpineRuntime37;
|
||||
using System;
|
||||
@@ -8,7 +8,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
internal sealed class SkeletonClipping37 : ISkeletonClipping
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime37;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
internal sealed class Skin37 : ISkin
|
||||
{
|
||||
@@ -5,10 +5,11 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using SpineRuntime37;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
internal sealed class Slot37 : ISlot
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
public float A { get => _o.A; set => _o.A = value; }
|
||||
public IBone Bone => _bone;
|
||||
|
||||
public Spine.SpineWrappers.Attachments.IAttachment? Attachment
|
||||
public IAttachment? Attachment
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -73,6 +74,8 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
}
|
||||
}
|
||||
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public override string ToString() => _o.ToString();
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.Utils;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using SpineRuntime37;
|
||||
using Spine.Implementations.SpineWrappers.V37.Attachments;
|
||||
using Spine.Implementations.V37.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
[SpineImplementation(3, 7)]
|
||||
internal sealed class SpineObjectData37 : SpineObjectData
|
||||
@@ -26,12 +26,19 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
private readonly ImmutableArray<IAnimation> _animations;
|
||||
private readonly FrozenDictionary<string, IAnimation> _animationsByName;
|
||||
|
||||
public SpineObjectData37(string skelPath, string atlasPath, Spine.SpineWrappers.TextureLoader textureLoader)
|
||||
public SpineObjectData37(string skelPath, string atlasPath, TextureLoader textureLoader)
|
||||
: base(skelPath, atlasPath, textureLoader)
|
||||
{
|
||||
// 加载 atlas
|
||||
try { _atlas = new Atlas(atlasPath, textureLoader); }
|
||||
catch (Exception ex) { throw new InvalidDataException($"Failed to load atlas '{atlasPath}'", ex); }
|
||||
try
|
||||
{
|
||||
_atlas = new Atlas(atlasPath, textureLoader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load atlas '{atlasPath}'");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -41,8 +48,9 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
{
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
@@ -52,8 +60,9 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
{
|
||||
_skeletonData = new SkeletonBinary(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Trace(ex.ToString());
|
||||
_skeletonData = new SkeletonJson(_atlas).ReadSkeletonData(skelPath);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +70,8 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
catch (Exception ex)
|
||||
{
|
||||
_atlas.Dispose();
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}", ex);
|
||||
_logger.Trace(ex.ToString());
|
||||
throw new InvalidDataException($"Failed to load skeleton file {skelPath}");
|
||||
}
|
||||
|
||||
// 加载动画数据
|
||||
@@ -1,4 +1,4 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime37;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V37
|
||||
namespace Spine.Implementations.V37
|
||||
{
|
||||
internal sealed class TrackEntry37(TrackEntry innerObject, AnimationState37 animationState, SpineObjectData37 data): ITrackEntry
|
||||
{
|
||||
@@ -26,7 +26,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
@@ -55,7 +55,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
@@ -84,7 +84,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
@@ -113,7 +113,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
@@ -142,7 +142,7 @@ namespace Spine.Implementations.SpineWrappers.V37
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(_animationState.GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
@@ -1,4 +1,4 @@
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime38;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V38
|
||||
namespace Spine.Implementations.V38
|
||||
{
|
||||
internal sealed class Animation38(Animation innerObject) : IAnimation
|
||||
{
|
||||
@@ -3,10 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.Interfaces;
|
||||
using SpineRuntime38;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V38
|
||||
namespace Spine.Implementations.V38
|
||||
{
|
||||
internal sealed class AnimationState38(AnimationState innerObject, SpineObjectData38 data) : IAnimationState
|
||||
{
|
||||
@@ -27,7 +27,7 @@ namespace Spine.Implementations.SpineWrappers.V38
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Start += f;
|
||||
@@ -56,7 +56,7 @@ namespace Spine.Implementations.SpineWrappers.V38
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Interrupt += f;
|
||||
@@ -85,7 +85,7 @@ namespace Spine.Implementations.SpineWrappers.V38
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.End += f;
|
||||
@@ -114,7 +114,7 @@ namespace Spine.Implementations.SpineWrappers.V38
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Complete += f;
|
||||
@@ -143,7 +143,7 @@ namespace Spine.Implementations.SpineWrappers.V38
|
||||
if (value is null) return;
|
||||
if (!_eventMapping.TryGetValue(value, out var f))
|
||||
{
|
||||
_eventMapping[value] = f = (TrackEntry t) => value(GetTrackEntry(t));
|
||||
_eventMapping[value] = f = (t) => value(GetTrackEntry(t));
|
||||
_eventCount[value] = 0;
|
||||
}
|
||||
_o.Dispose += f;
|
||||
@@ -3,12 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime38;
|
||||
using SpineRuntime38.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V38.Attachments
|
||||
namespace Spine.Implementations.V38.Attachments
|
||||
{
|
||||
internal abstract class Attachment38(Attachment innerObject) : IAttachment
|
||||
{
|
||||
@@ -3,11 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V38;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime38;
|
||||
using SpineRuntime38.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V38.Attachments
|
||||
namespace Spine.Implementations.V38.Attachments
|
||||
{
|
||||
internal sealed class BoundingBoxAttachment38(BoundingBoxAttachment innerObject) :
|
||||
Attachment38(innerObject),
|
||||
@@ -17,7 +19,7 @@ namespace Spine.Implementations.SpineWrappers.V38.Attachments
|
||||
|
||||
public override BoundingBoxAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot38 st)
|
||||
{
|
||||
@@ -3,11 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V38;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime38;
|
||||
using SpineRuntime38.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V38.Attachments
|
||||
namespace Spine.Implementations.V38.Attachments
|
||||
{
|
||||
internal sealed class ClippingAttachment38(ClippingAttachment innerObject) :
|
||||
Attachment38(innerObject),
|
||||
@@ -17,7 +19,7 @@ namespace Spine.Implementations.SpineWrappers.V38.Attachments
|
||||
|
||||
public override ClippingAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot38 st)
|
||||
{
|
||||
@@ -3,11 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Spine.SpineWrappers.Attachments;
|
||||
using Spine.Implementations.V38;
|
||||
using Spine.Interfaces;
|
||||
using Spine.Interfaces.Attachments;
|
||||
using SpineRuntime38;
|
||||
using SpineRuntime38.Attachments;
|
||||
|
||||
namespace Spine.Implementations.SpineWrappers.V38.Attachments
|
||||
namespace Spine.Implementations.V38.Attachments
|
||||
{
|
||||
internal sealed class MeshAttachment38(MeshAttachment innerObject) :
|
||||
Attachment38(innerObject),
|
||||
@@ -17,7 +19,7 @@ namespace Spine.Implementations.SpineWrappers.V38.Attachments
|
||||
|
||||
public override MeshAttachment InnerObject => _o;
|
||||
|
||||
public override int ComputeWorldVertices(Spine.SpineWrappers.ISlot slot, ref float[] worldVertices)
|
||||
public override int ComputeWorldVertices(ISlot slot, ref float[] worldVertices)
|
||||
{
|
||||
if (slot is Slot38 st)
|
||||
{
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user