首次提交
This commit is contained in:
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
dotnet_diagnostic.CS8600.severity = suggestion
|
||||||
|
dotnet_diagnostic.CS8601.severity = suggestion
|
||||||
|
dotnet_diagnostic.CS8602.severity = suggestion
|
||||||
|
dotnet_diagnostic.CS8603.severity = suggestion
|
||||||
|
dotnet_diagnostic.CS8604.severity = suggestion
|
||||||
|
dotnet_diagnostic.CS8605.severity = suggestion
|
||||||
|
dotnet_diagnostic.CS8618.severity = suggestion
|
||||||
|
dotnet_diagnostic.CS8625.severity = suggestion
|
||||||
|
dotnet_diagnostic.CS8714.severity = suggestion
|
||||||
398
.gitignore
vendored
Normal file
398
.gitignore
vendored
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
|
*.vbp
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
*.sln.iml
|
||||||
13
README.md
Normal file
13
README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# SpineViewer
|
||||||
|
|
||||||
|
[中文](README.md) | [English](README.en.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*如果你觉得这个项目不错请给个 :star:, 并分享给更多人知道! :)*
|
||||||
64
SpineViewer.sln
Normal file
64
SpineViewer.sln
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.11.35208.52
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpineViewer", "SpineViewer\SpineViewer.csproj", "{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SpineRuntimes", "SpineRuntimes", "{EA2E1399-02BC-43BC-AD9F-42E23E9C3DA8}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpineRuntime36", "SpineRuntimes\SpineRuntime36\SpineRuntime36.csproj", "{CA964DA9-3649-44BC-84F7-B1108A652905}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpineRuntime38", "SpineRuntimes\SpineRuntime38\SpineRuntime38.csproj", "{ECF7297E-031B-4E37-8033-7C2345DB8766}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{65DD4332-305A-4AAA-AD92-A7D293296BC9}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
|
.gitignore = .gitignore
|
||||||
|
README.md = README.md
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{ECD11621-9F72-4BCB-92A4-7D8A426F84FA}.Release|x64.Build.0 = Release|x64
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905}.Release|x64.Build.0 = Release|x64
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766}.Release|x64.Build.0 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{CA964DA9-3649-44BC-84F7-B1108A652905} = {EA2E1399-02BC-43BC-AD9F-42E23E9C3DA8}
|
||||||
|
{ECF7297E-031B-4E37-8033-7C2345DB8766} = {EA2E1399-02BC-43BC-AD9F-42E23E9C3DA8}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {91F0EFD1-4B07-4C3C-82D8-90432349D3A5}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
63
SpineViewer/Properties/Resources.Designer.cs
generated
Normal file
63
SpineViewer/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// 此代码由工具生成。
|
||||||
|
// 运行时版本:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||||
|
// 重新生成代码,这些更改将会丢失。
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace SpineViewer.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||||
|
/// </summary>
|
||||||
|
// 此类是由 StronglyTypedResourceBuilder
|
||||||
|
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||||
|
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||||
|
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SpineViewer.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重写当前线程的 CurrentUICulture 属性,对
|
||||||
|
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
SpineViewer/Properties/Resources.resx
Normal file
120
SpineViewer/Properties/Resources.resx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
42
SpineViewer/SpineViewer.csproj
Normal file
42
SpineViewer/SpineViewer.csproj
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Platforms>x64</Platforms>
|
||||||
|
<Version>0.1.0</Version>
|
||||||
|
<BaseOutputPath>$(SolutionDir)out</BaseOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NLog.Windows.Forms" Version="5.2.3" />
|
||||||
|
<PackageReference Include="SFML.Net" Version="2.6.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SpineRuntimes\SpineRuntime36\SpineRuntime36.csproj" />
|
||||||
|
<ProjectReference Include="..\SpineRuntimes\SpineRuntime38\SpineRuntime38.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<RuntimeHostConfigurationOption Include="System.Runtime.Loader.UseRidGraph" Value="true" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Properties\Resources.Designer.cs">
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Update="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
572
SpineViewer/src/MainForm.Designer.cs
generated
Normal file
572
SpineViewer/src/MainForm.Designer.cs
generated
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
namespace SpineViewer
|
||||||
|
{
|
||||||
|
partial class MainForm
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
components = new System.ComponentModel.Container();
|
||||||
|
ListViewItem listViewItem1 = new ListViewItem(new string[] { "A Loooooooooog Name1", "A Loooooooooog Version" }, -1);
|
||||||
|
menuStrip = new MenuStrip();
|
||||||
|
toolStripMenuItem_File = new ToolStripMenuItem();
|
||||||
|
toolStripMenuItem_Open = new ToolStripMenuItem();
|
||||||
|
toolStripMenuItem_BatchOpen = new ToolStripMenuItem();
|
||||||
|
toolStripSeparator1 = new ToolStripSeparator();
|
||||||
|
toolStripMenuItem_Export = new ToolStripMenuItem();
|
||||||
|
toolStripSeparator2 = new ToolStripSeparator();
|
||||||
|
toolStripMenuItem_Exit = new ToolStripMenuItem();
|
||||||
|
toolStripMenuItem_Help = new ToolStripMenuItem();
|
||||||
|
toolStripMenuItem_About = new ToolStripMenuItem();
|
||||||
|
rtbLog = new RichTextBox();
|
||||||
|
splitContainer_MainForm = new SplitContainer();
|
||||||
|
splitContainer_Functional = new SplitContainer();
|
||||||
|
splitContainer_Information = new SplitContainer();
|
||||||
|
groupBox_SkelList = new GroupBox();
|
||||||
|
tableLayoutPanel = new TableLayoutPanel();
|
||||||
|
flowLayoutPanel_Buttons = new FlowLayoutPanel();
|
||||||
|
button_Add = new Button();
|
||||||
|
button_Insert = new Button();
|
||||||
|
button_Remove = new Button();
|
||||||
|
button_MoveUp = new Button();
|
||||||
|
button_MoveDown = new Button();
|
||||||
|
listView_SkelList = new ListView();
|
||||||
|
columnHeader_Name = new ColumnHeader();
|
||||||
|
columnHeader_Version = new ColumnHeader();
|
||||||
|
splitContainer_Config = new SplitContainer();
|
||||||
|
groupBox_SkelConfig = new GroupBox();
|
||||||
|
propertyGrid_Skel = new PropertyGrid();
|
||||||
|
groupBox_PreviewConfig = new GroupBox();
|
||||||
|
groupBox_Preview = new GroupBox();
|
||||||
|
panel_PreviewContainer = new Panel();
|
||||||
|
panel_Preview = new Panel();
|
||||||
|
panel_MainForm = new Panel();
|
||||||
|
openFileDialog_Skel = new OpenFileDialog();
|
||||||
|
openFileDialog_Atlas = new OpenFileDialog();
|
||||||
|
toolTip1 = new ToolTip(components);
|
||||||
|
menuStrip.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)splitContainer_MainForm).BeginInit();
|
||||||
|
splitContainer_MainForm.Panel1.SuspendLayout();
|
||||||
|
splitContainer_MainForm.Panel2.SuspendLayout();
|
||||||
|
splitContainer_MainForm.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)splitContainer_Functional).BeginInit();
|
||||||
|
splitContainer_Functional.Panel1.SuspendLayout();
|
||||||
|
splitContainer_Functional.Panel2.SuspendLayout();
|
||||||
|
splitContainer_Functional.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)splitContainer_Information).BeginInit();
|
||||||
|
splitContainer_Information.Panel1.SuspendLayout();
|
||||||
|
splitContainer_Information.Panel2.SuspendLayout();
|
||||||
|
splitContainer_Information.SuspendLayout();
|
||||||
|
groupBox_SkelList.SuspendLayout();
|
||||||
|
tableLayoutPanel.SuspendLayout();
|
||||||
|
flowLayoutPanel_Buttons.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)splitContainer_Config).BeginInit();
|
||||||
|
splitContainer_Config.Panel1.SuspendLayout();
|
||||||
|
splitContainer_Config.Panel2.SuspendLayout();
|
||||||
|
splitContainer_Config.SuspendLayout();
|
||||||
|
groupBox_SkelConfig.SuspendLayout();
|
||||||
|
groupBox_Preview.SuspendLayout();
|
||||||
|
panel_PreviewContainer.SuspendLayout();
|
||||||
|
panel_MainForm.SuspendLayout();
|
||||||
|
SuspendLayout();
|
||||||
|
//
|
||||||
|
// menuStrip
|
||||||
|
//
|
||||||
|
menuStrip.BackColor = SystemColors.Control;
|
||||||
|
menuStrip.ImageScalingSize = new Size(24, 24);
|
||||||
|
menuStrip.Items.AddRange(new ToolStripItem[] { toolStripMenuItem_File, toolStripMenuItem_Help });
|
||||||
|
menuStrip.Location = new Point(0, 0);
|
||||||
|
menuStrip.Name = "menuStrip";
|
||||||
|
menuStrip.Size = new Size(1519, 32);
|
||||||
|
menuStrip.TabIndex = 0;
|
||||||
|
menuStrip.Text = "菜单";
|
||||||
|
//
|
||||||
|
// toolStripMenuItem_File
|
||||||
|
//
|
||||||
|
toolStripMenuItem_File.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_Open, toolStripMenuItem_BatchOpen, toolStripSeparator1, toolStripMenuItem_Export, toolStripSeparator2, toolStripMenuItem_Exit });
|
||||||
|
toolStripMenuItem_File.Name = "toolStripMenuItem_File";
|
||||||
|
toolStripMenuItem_File.Size = new Size(84, 28);
|
||||||
|
toolStripMenuItem_File.Text = "文件(&F)";
|
||||||
|
//
|
||||||
|
// toolStripMenuItem_Open
|
||||||
|
//
|
||||||
|
toolStripMenuItem_Open.Name = "toolStripMenuItem_Open";
|
||||||
|
toolStripMenuItem_Open.ShortcutKeys = Keys.Control | Keys.O;
|
||||||
|
toolStripMenuItem_Open.Size = new Size(254, 34);
|
||||||
|
toolStripMenuItem_Open.Text = "打开(&O)...";
|
||||||
|
toolStripMenuItem_Open.Click += toolStripMenuItem_Open_Click;
|
||||||
|
//
|
||||||
|
// toolStripMenuItem_BatchOpen
|
||||||
|
//
|
||||||
|
toolStripMenuItem_BatchOpen.Name = "toolStripMenuItem_BatchOpen";
|
||||||
|
toolStripMenuItem_BatchOpen.Size = new Size(254, 34);
|
||||||
|
toolStripMenuItem_BatchOpen.Text = "批量打开(&B)...";
|
||||||
|
toolStripMenuItem_BatchOpen.Click += toolStripMenuItem_BatchOpen_Click;
|
||||||
|
//
|
||||||
|
// toolStripSeparator1
|
||||||
|
//
|
||||||
|
toolStripSeparator1.Name = "toolStripSeparator1";
|
||||||
|
toolStripSeparator1.Size = new Size(251, 6);
|
||||||
|
//
|
||||||
|
// toolStripMenuItem_Export
|
||||||
|
//
|
||||||
|
toolStripMenuItem_Export.Name = "toolStripMenuItem_Export";
|
||||||
|
toolStripMenuItem_Export.ShortcutKeys = Keys.Control | Keys.S;
|
||||||
|
toolStripMenuItem_Export.Size = new Size(254, 34);
|
||||||
|
toolStripMenuItem_Export.Text = "导出(&E)...";
|
||||||
|
toolStripMenuItem_Export.Click += toolStripMenuItem_Export_Click;
|
||||||
|
//
|
||||||
|
// toolStripSeparator2
|
||||||
|
//
|
||||||
|
toolStripSeparator2.Name = "toolStripSeparator2";
|
||||||
|
toolStripSeparator2.Size = new Size(251, 6);
|
||||||
|
//
|
||||||
|
// toolStripMenuItem_Exit
|
||||||
|
//
|
||||||
|
toolStripMenuItem_Exit.Name = "toolStripMenuItem_Exit";
|
||||||
|
toolStripMenuItem_Exit.ShortcutKeys = Keys.Alt | Keys.F4;
|
||||||
|
toolStripMenuItem_Exit.Size = new Size(254, 34);
|
||||||
|
toolStripMenuItem_Exit.Text = "退出(&X)";
|
||||||
|
toolStripMenuItem_Exit.Click += toolStripMenuItem_Exit_Click;
|
||||||
|
//
|
||||||
|
// toolStripMenuItem_Help
|
||||||
|
//
|
||||||
|
toolStripMenuItem_Help.DropDownItems.AddRange(new ToolStripItem[] { toolStripMenuItem_About });
|
||||||
|
toolStripMenuItem_Help.Name = "toolStripMenuItem_Help";
|
||||||
|
toolStripMenuItem_Help.Size = new Size(88, 28);
|
||||||
|
toolStripMenuItem_Help.Text = "帮助(&H)";
|
||||||
|
//
|
||||||
|
// toolStripMenuItem_About
|
||||||
|
//
|
||||||
|
toolStripMenuItem_About.Name = "toolStripMenuItem_About";
|
||||||
|
toolStripMenuItem_About.Size = new Size(171, 34);
|
||||||
|
toolStripMenuItem_About.Text = "关于(&A)";
|
||||||
|
//
|
||||||
|
// rtbLog
|
||||||
|
//
|
||||||
|
rtbLog.BackColor = SystemColors.Window;
|
||||||
|
rtbLog.BorderStyle = BorderStyle.None;
|
||||||
|
rtbLog.Dock = DockStyle.Fill;
|
||||||
|
rtbLog.Font = new Font("Consolas", 9F);
|
||||||
|
rtbLog.Location = new Point(0, 0);
|
||||||
|
rtbLog.Margin = new Padding(3, 2, 3, 2);
|
||||||
|
rtbLog.Name = "rtbLog";
|
||||||
|
rtbLog.ReadOnly = true;
|
||||||
|
rtbLog.Size = new Size(1499, 102);
|
||||||
|
rtbLog.TabIndex = 0;
|
||||||
|
rtbLog.Text = "";
|
||||||
|
rtbLog.WordWrap = false;
|
||||||
|
//
|
||||||
|
// splitContainer_MainForm
|
||||||
|
//
|
||||||
|
splitContainer_MainForm.Cursor = Cursors.SizeNS;
|
||||||
|
splitContainer_MainForm.Dock = DockStyle.Fill;
|
||||||
|
splitContainer_MainForm.Location = new Point(10, 5);
|
||||||
|
splitContainer_MainForm.Name = "splitContainer_MainForm";
|
||||||
|
splitContainer_MainForm.Orientation = Orientation.Horizontal;
|
||||||
|
//
|
||||||
|
// splitContainer_MainForm.Panel1
|
||||||
|
//
|
||||||
|
splitContainer_MainForm.Panel1.Controls.Add(splitContainer_Functional);
|
||||||
|
splitContainer_MainForm.Panel1.Cursor = Cursors.Default;
|
||||||
|
//
|
||||||
|
// splitContainer_MainForm.Panel2
|
||||||
|
//
|
||||||
|
splitContainer_MainForm.Panel2.Controls.Add(rtbLog);
|
||||||
|
splitContainer_MainForm.Panel2.Cursor = Cursors.Default;
|
||||||
|
splitContainer_MainForm.Size = new Size(1499, 838);
|
||||||
|
splitContainer_MainForm.SplitterDistance = 732;
|
||||||
|
splitContainer_MainForm.TabIndex = 3;
|
||||||
|
splitContainer_MainForm.TabStop = false;
|
||||||
|
splitContainer_MainForm.SplitterMoved += splitContainer_SplitterMoved;
|
||||||
|
splitContainer_MainForm.MouseUp += splitContainer_MouseUp;
|
||||||
|
//
|
||||||
|
// splitContainer_Functional
|
||||||
|
//
|
||||||
|
splitContainer_Functional.Cursor = Cursors.SizeWE;
|
||||||
|
splitContainer_Functional.Dock = DockStyle.Fill;
|
||||||
|
splitContainer_Functional.Location = new Point(0, 0);
|
||||||
|
splitContainer_Functional.Name = "splitContainer_Functional";
|
||||||
|
//
|
||||||
|
// splitContainer_Functional.Panel1
|
||||||
|
//
|
||||||
|
splitContainer_Functional.Panel1.Controls.Add(splitContainer_Information);
|
||||||
|
splitContainer_Functional.Panel1.Cursor = Cursors.Default;
|
||||||
|
//
|
||||||
|
// splitContainer_Functional.Panel2
|
||||||
|
//
|
||||||
|
splitContainer_Functional.Panel2.Controls.Add(groupBox_Preview);
|
||||||
|
splitContainer_Functional.Panel2.Cursor = Cursors.Default;
|
||||||
|
splitContainer_Functional.Size = new Size(1499, 732);
|
||||||
|
splitContainer_Functional.SplitterDistance = 698;
|
||||||
|
splitContainer_Functional.TabIndex = 2;
|
||||||
|
splitContainer_Functional.TabStop = false;
|
||||||
|
splitContainer_Functional.SplitterMoved += splitContainer_SplitterMoved;
|
||||||
|
splitContainer_Functional.MouseUp += splitContainer_MouseUp;
|
||||||
|
//
|
||||||
|
// splitContainer_Information
|
||||||
|
//
|
||||||
|
splitContainer_Information.Cursor = Cursors.SizeWE;
|
||||||
|
splitContainer_Information.Dock = DockStyle.Fill;
|
||||||
|
splitContainer_Information.Location = new Point(0, 0);
|
||||||
|
splitContainer_Information.Name = "splitContainer_Information";
|
||||||
|
//
|
||||||
|
// splitContainer_Information.Panel1
|
||||||
|
//
|
||||||
|
splitContainer_Information.Panel1.Controls.Add(groupBox_SkelList);
|
||||||
|
splitContainer_Information.Panel1.Cursor = Cursors.Default;
|
||||||
|
//
|
||||||
|
// splitContainer_Information.Panel2
|
||||||
|
//
|
||||||
|
splitContainer_Information.Panel2.Controls.Add(splitContainer_Config);
|
||||||
|
splitContainer_Information.Panel2.Cursor = Cursors.Default;
|
||||||
|
splitContainer_Information.Size = new Size(698, 732);
|
||||||
|
splitContainer_Information.SplitterDistance = 336;
|
||||||
|
splitContainer_Information.TabIndex = 1;
|
||||||
|
splitContainer_Information.TabStop = false;
|
||||||
|
splitContainer_Information.SplitterMoved += splitContainer_SplitterMoved;
|
||||||
|
splitContainer_Information.MouseUp += splitContainer_MouseUp;
|
||||||
|
//
|
||||||
|
// groupBox_SkelList
|
||||||
|
//
|
||||||
|
groupBox_SkelList.Controls.Add(tableLayoutPanel);
|
||||||
|
groupBox_SkelList.Dock = DockStyle.Fill;
|
||||||
|
groupBox_SkelList.Location = new Point(0, 0);
|
||||||
|
groupBox_SkelList.Name = "groupBox_SkelList";
|
||||||
|
groupBox_SkelList.Size = new Size(336, 732);
|
||||||
|
groupBox_SkelList.TabIndex = 0;
|
||||||
|
groupBox_SkelList.TabStop = false;
|
||||||
|
groupBox_SkelList.Text = "模型列表";
|
||||||
|
//
|
||||||
|
// tableLayoutPanel
|
||||||
|
//
|
||||||
|
tableLayoutPanel.ColumnCount = 1;
|
||||||
|
tableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
|
||||||
|
tableLayoutPanel.Controls.Add(flowLayoutPanel_Buttons, 0, 0);
|
||||||
|
tableLayoutPanel.Controls.Add(listView_SkelList, 0, 1);
|
||||||
|
tableLayoutPanel.Dock = DockStyle.Fill;
|
||||||
|
tableLayoutPanel.Location = new Point(3, 26);
|
||||||
|
tableLayoutPanel.Name = "tableLayoutPanel";
|
||||||
|
tableLayoutPanel.RowCount = 2;
|
||||||
|
tableLayoutPanel.RowStyles.Add(new RowStyle());
|
||||||
|
tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
|
||||||
|
tableLayoutPanel.Size = new Size(330, 703);
|
||||||
|
tableLayoutPanel.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// flowLayoutPanel_Buttons
|
||||||
|
//
|
||||||
|
flowLayoutPanel_Buttons.AutoSize = true;
|
||||||
|
flowLayoutPanel_Buttons.Controls.Add(button_Add);
|
||||||
|
flowLayoutPanel_Buttons.Controls.Add(button_Insert);
|
||||||
|
flowLayoutPanel_Buttons.Controls.Add(button_Remove);
|
||||||
|
flowLayoutPanel_Buttons.Controls.Add(button_MoveUp);
|
||||||
|
flowLayoutPanel_Buttons.Controls.Add(button_MoveDown);
|
||||||
|
flowLayoutPanel_Buttons.Dock = DockStyle.Fill;
|
||||||
|
flowLayoutPanel_Buttons.Location = new Point(3, 3);
|
||||||
|
flowLayoutPanel_Buttons.Name = "flowLayoutPanel_Buttons";
|
||||||
|
flowLayoutPanel_Buttons.Size = new Size(324, 40);
|
||||||
|
flowLayoutPanel_Buttons.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// button_Add
|
||||||
|
//
|
||||||
|
button_Add.Anchor = AnchorStyles.None;
|
||||||
|
button_Add.AutoSize = true;
|
||||||
|
button_Add.Location = new Point(3, 3);
|
||||||
|
button_Add.Name = "button_Add";
|
||||||
|
button_Add.Size = new Size(56, 34);
|
||||||
|
button_Add.TabIndex = 0;
|
||||||
|
button_Add.Text = "添加";
|
||||||
|
button_Add.UseVisualStyleBackColor = true;
|
||||||
|
button_Add.Click += toolStripMenuItem_Open_Click;
|
||||||
|
//
|
||||||
|
// button_Insert
|
||||||
|
//
|
||||||
|
button_Insert.Anchor = AnchorStyles.None;
|
||||||
|
button_Insert.AutoSize = true;
|
||||||
|
button_Insert.Enabled = false;
|
||||||
|
button_Insert.Location = new Point(65, 3);
|
||||||
|
button_Insert.Name = "button_Insert";
|
||||||
|
button_Insert.Size = new Size(56, 34);
|
||||||
|
button_Insert.TabIndex = 4;
|
||||||
|
button_Insert.Text = "插入";
|
||||||
|
button_Insert.UseVisualStyleBackColor = true;
|
||||||
|
button_Insert.Click += button_Insert_Click;
|
||||||
|
//
|
||||||
|
// button_Remove
|
||||||
|
//
|
||||||
|
button_Remove.Anchor = AnchorStyles.None;
|
||||||
|
button_Remove.AutoSize = true;
|
||||||
|
button_Remove.Enabled = false;
|
||||||
|
button_Remove.Location = new Point(127, 3);
|
||||||
|
button_Remove.Name = "button_Remove";
|
||||||
|
button_Remove.Size = new Size(56, 34);
|
||||||
|
button_Remove.TabIndex = 1;
|
||||||
|
button_Remove.Text = "移除";
|
||||||
|
button_Remove.UseVisualStyleBackColor = true;
|
||||||
|
button_Remove.Click += button_Remove_Click;
|
||||||
|
//
|
||||||
|
// button_MoveUp
|
||||||
|
//
|
||||||
|
button_MoveUp.Anchor = AnchorStyles.None;
|
||||||
|
button_MoveUp.AutoSize = true;
|
||||||
|
button_MoveUp.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||||
|
button_MoveUp.Enabled = false;
|
||||||
|
button_MoveUp.Location = new Point(189, 3);
|
||||||
|
button_MoveUp.Name = "button_MoveUp";
|
||||||
|
button_MoveUp.Size = new Size(56, 34);
|
||||||
|
button_MoveUp.TabIndex = 2;
|
||||||
|
button_MoveUp.Text = "上移";
|
||||||
|
button_MoveUp.UseVisualStyleBackColor = true;
|
||||||
|
button_MoveUp.Click += button_MoveUp_Click;
|
||||||
|
//
|
||||||
|
// button_MoveDown
|
||||||
|
//
|
||||||
|
button_MoveDown.Anchor = AnchorStyles.None;
|
||||||
|
button_MoveDown.AutoSize = true;
|
||||||
|
button_MoveDown.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||||
|
button_MoveDown.Enabled = false;
|
||||||
|
button_MoveDown.Location = new Point(251, 3);
|
||||||
|
button_MoveDown.Name = "button_MoveDown";
|
||||||
|
button_MoveDown.Size = new Size(56, 34);
|
||||||
|
button_MoveDown.TabIndex = 3;
|
||||||
|
button_MoveDown.Text = "下移";
|
||||||
|
button_MoveDown.UseVisualStyleBackColor = true;
|
||||||
|
button_MoveDown.Click += button_MoveDown_Click;
|
||||||
|
//
|
||||||
|
// listView_SkelList
|
||||||
|
//
|
||||||
|
listView_SkelList.Columns.AddRange(new ColumnHeader[] { columnHeader_Name, columnHeader_Version });
|
||||||
|
listView_SkelList.Dock = DockStyle.Fill;
|
||||||
|
listView_SkelList.FullRowSelect = true;
|
||||||
|
listView_SkelList.GridLines = true;
|
||||||
|
listView_SkelList.Items.AddRange(new ListViewItem[] { listViewItem1 });
|
||||||
|
listView_SkelList.Location = new Point(3, 49);
|
||||||
|
listView_SkelList.Name = "listView_SkelList";
|
||||||
|
listView_SkelList.ShowItemToolTips = true;
|
||||||
|
listView_SkelList.Size = new Size(324, 651);
|
||||||
|
listView_SkelList.TabIndex = 1;
|
||||||
|
listView_SkelList.UseCompatibleStateImageBehavior = false;
|
||||||
|
listView_SkelList.View = View.Details;
|
||||||
|
listView_SkelList.SelectedIndexChanged += listView_SkelList_SelectedIndexChanged;
|
||||||
|
//
|
||||||
|
// columnHeader_Name
|
||||||
|
//
|
||||||
|
columnHeader_Name.Text = "名称";
|
||||||
|
columnHeader_Name.Width = 150;
|
||||||
|
//
|
||||||
|
// columnHeader_Version
|
||||||
|
//
|
||||||
|
columnHeader_Version.Text = "版本";
|
||||||
|
columnHeader_Version.Width = 150;
|
||||||
|
//
|
||||||
|
// splitContainer_Config
|
||||||
|
//
|
||||||
|
splitContainer_Config.Cursor = Cursors.SizeNS;
|
||||||
|
splitContainer_Config.Dock = DockStyle.Fill;
|
||||||
|
splitContainer_Config.Location = new Point(0, 0);
|
||||||
|
splitContainer_Config.Name = "splitContainer_Config";
|
||||||
|
splitContainer_Config.Orientation = Orientation.Horizontal;
|
||||||
|
//
|
||||||
|
// splitContainer_Config.Panel1
|
||||||
|
//
|
||||||
|
splitContainer_Config.Panel1.Controls.Add(groupBox_SkelConfig);
|
||||||
|
splitContainer_Config.Panel1.Cursor = Cursors.Default;
|
||||||
|
//
|
||||||
|
// splitContainer_Config.Panel2
|
||||||
|
//
|
||||||
|
splitContainer_Config.Panel2.Controls.Add(groupBox_PreviewConfig);
|
||||||
|
splitContainer_Config.Panel2.Cursor = Cursors.Default;
|
||||||
|
splitContainer_Config.Size = new Size(358, 732);
|
||||||
|
splitContainer_Config.SplitterDistance = 493;
|
||||||
|
splitContainer_Config.TabIndex = 0;
|
||||||
|
splitContainer_Config.TabStop = false;
|
||||||
|
splitContainer_Config.SplitterMoved += splitContainer_SplitterMoved;
|
||||||
|
splitContainer_Config.MouseUp += splitContainer_MouseUp;
|
||||||
|
//
|
||||||
|
// groupBox_SkelConfig
|
||||||
|
//
|
||||||
|
groupBox_SkelConfig.Controls.Add(propertyGrid_Skel);
|
||||||
|
groupBox_SkelConfig.Dock = DockStyle.Fill;
|
||||||
|
groupBox_SkelConfig.Location = new Point(0, 0);
|
||||||
|
groupBox_SkelConfig.Name = "groupBox_SkelConfig";
|
||||||
|
groupBox_SkelConfig.Size = new Size(358, 493);
|
||||||
|
groupBox_SkelConfig.TabIndex = 0;
|
||||||
|
groupBox_SkelConfig.TabStop = false;
|
||||||
|
groupBox_SkelConfig.Text = "模型参数";
|
||||||
|
//
|
||||||
|
// propertyGrid_Skel
|
||||||
|
//
|
||||||
|
propertyGrid_Skel.Dock = DockStyle.Fill;
|
||||||
|
propertyGrid_Skel.HelpVisible = false;
|
||||||
|
propertyGrid_Skel.Location = new Point(3, 26);
|
||||||
|
propertyGrid_Skel.Name = "propertyGrid_Skel";
|
||||||
|
propertyGrid_Skel.Size = new Size(352, 464);
|
||||||
|
propertyGrid_Skel.TabIndex = 0;
|
||||||
|
propertyGrid_Skel.ToolbarVisible = false;
|
||||||
|
//
|
||||||
|
// groupBox_PreviewConfig
|
||||||
|
//
|
||||||
|
groupBox_PreviewConfig.Dock = DockStyle.Fill;
|
||||||
|
groupBox_PreviewConfig.Location = new Point(0, 0);
|
||||||
|
groupBox_PreviewConfig.Name = "groupBox_PreviewConfig";
|
||||||
|
groupBox_PreviewConfig.Size = new Size(358, 235);
|
||||||
|
groupBox_PreviewConfig.TabIndex = 1;
|
||||||
|
groupBox_PreviewConfig.TabStop = false;
|
||||||
|
groupBox_PreviewConfig.Text = "画面参数";
|
||||||
|
//
|
||||||
|
// groupBox_Preview
|
||||||
|
//
|
||||||
|
groupBox_Preview.Controls.Add(panel_PreviewContainer);
|
||||||
|
groupBox_Preview.Dock = DockStyle.Fill;
|
||||||
|
groupBox_Preview.Location = new Point(0, 0);
|
||||||
|
groupBox_Preview.Name = "groupBox_Preview";
|
||||||
|
groupBox_Preview.Size = new Size(797, 732);
|
||||||
|
groupBox_Preview.TabIndex = 1;
|
||||||
|
groupBox_Preview.TabStop = false;
|
||||||
|
groupBox_Preview.Text = "预览画面";
|
||||||
|
//
|
||||||
|
// panel_PreviewContainer
|
||||||
|
//
|
||||||
|
panel_PreviewContainer.Controls.Add(panel_Preview);
|
||||||
|
panel_PreviewContainer.Dock = DockStyle.Fill;
|
||||||
|
panel_PreviewContainer.Location = new Point(3, 26);
|
||||||
|
panel_PreviewContainer.Margin = new Padding(0);
|
||||||
|
panel_PreviewContainer.Name = "panel_PreviewContainer";
|
||||||
|
panel_PreviewContainer.Size = new Size(791, 703);
|
||||||
|
panel_PreviewContainer.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// panel_Preview
|
||||||
|
//
|
||||||
|
panel_Preview.BackColor = SystemColors.ControlDark;
|
||||||
|
panel_Preview.Location = new Point(107, 95);
|
||||||
|
panel_Preview.Name = "panel_Preview";
|
||||||
|
panel_Preview.Size = new Size(256, 256);
|
||||||
|
panel_Preview.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// panel_MainForm
|
||||||
|
//
|
||||||
|
panel_MainForm.Controls.Add(splitContainer_MainForm);
|
||||||
|
panel_MainForm.Dock = DockStyle.Fill;
|
||||||
|
panel_MainForm.Location = new Point(0, 32);
|
||||||
|
panel_MainForm.Name = "panel_MainForm";
|
||||||
|
panel_MainForm.Padding = new Padding(10, 5, 10, 10);
|
||||||
|
panel_MainForm.Size = new Size(1519, 853);
|
||||||
|
panel_MainForm.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// openFileDialog_Skel
|
||||||
|
//
|
||||||
|
openFileDialog_Skel.AddExtension = false;
|
||||||
|
openFileDialog_Skel.AddToRecent = false;
|
||||||
|
openFileDialog_Skel.Filter = "skel 文件 (*.skel; *.json)|*.skel;*.json";
|
||||||
|
//
|
||||||
|
// openFileDialog_Atlas
|
||||||
|
//
|
||||||
|
openFileDialog_Atlas.AddExtension = false;
|
||||||
|
openFileDialog_Atlas.AddToRecent = false;
|
||||||
|
openFileDialog_Atlas.Filter = "atlas 文件 (*.atlas)|*.atlas";
|
||||||
|
//
|
||||||
|
// MainForm
|
||||||
|
//
|
||||||
|
AutoScaleDimensions = new SizeF(11F, 24F);
|
||||||
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
|
ClientSize = new Size(1519, 885);
|
||||||
|
Controls.Add(panel_MainForm);
|
||||||
|
Controls.Add(menuStrip);
|
||||||
|
MainMenuStrip = menuStrip;
|
||||||
|
Margin = new Padding(3, 2, 3, 2);
|
||||||
|
Name = "MainForm";
|
||||||
|
StartPosition = FormStartPosition.CenterScreen;
|
||||||
|
Text = "SpineViewer";
|
||||||
|
menuStrip.ResumeLayout(false);
|
||||||
|
menuStrip.PerformLayout();
|
||||||
|
splitContainer_MainForm.Panel1.ResumeLayout(false);
|
||||||
|
splitContainer_MainForm.Panel2.ResumeLayout(false);
|
||||||
|
((System.ComponentModel.ISupportInitialize)splitContainer_MainForm).EndInit();
|
||||||
|
splitContainer_MainForm.ResumeLayout(false);
|
||||||
|
splitContainer_Functional.Panel1.ResumeLayout(false);
|
||||||
|
splitContainer_Functional.Panel2.ResumeLayout(false);
|
||||||
|
((System.ComponentModel.ISupportInitialize)splitContainer_Functional).EndInit();
|
||||||
|
splitContainer_Functional.ResumeLayout(false);
|
||||||
|
splitContainer_Information.Panel1.ResumeLayout(false);
|
||||||
|
splitContainer_Information.Panel2.ResumeLayout(false);
|
||||||
|
((System.ComponentModel.ISupportInitialize)splitContainer_Information).EndInit();
|
||||||
|
splitContainer_Information.ResumeLayout(false);
|
||||||
|
groupBox_SkelList.ResumeLayout(false);
|
||||||
|
tableLayoutPanel.ResumeLayout(false);
|
||||||
|
tableLayoutPanel.PerformLayout();
|
||||||
|
flowLayoutPanel_Buttons.ResumeLayout(false);
|
||||||
|
flowLayoutPanel_Buttons.PerformLayout();
|
||||||
|
splitContainer_Config.Panel1.ResumeLayout(false);
|
||||||
|
splitContainer_Config.Panel2.ResumeLayout(false);
|
||||||
|
((System.ComponentModel.ISupportInitialize)splitContainer_Config).EndInit();
|
||||||
|
splitContainer_Config.ResumeLayout(false);
|
||||||
|
groupBox_SkelConfig.ResumeLayout(false);
|
||||||
|
groupBox_Preview.ResumeLayout(false);
|
||||||
|
panel_PreviewContainer.ResumeLayout(false);
|
||||||
|
panel_MainForm.ResumeLayout(false);
|
||||||
|
ResumeLayout(false);
|
||||||
|
PerformLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private MenuStrip menuStrip;
|
||||||
|
private ToolStripMenuItem toolStripMenuItem_File;
|
||||||
|
private ToolStripMenuItem toolStripMenuItem_Open;
|
||||||
|
private ToolStripMenuItem toolStripMenuItem_Exit;
|
||||||
|
private ToolStripSeparator toolStripSeparator1;
|
||||||
|
private ToolStripMenuItem toolStripMenuItem_Export;
|
||||||
|
private ToolStripSeparator toolStripSeparator2;
|
||||||
|
private RichTextBox rtbLog;
|
||||||
|
private SplitContainer splitContainer_MainForm;
|
||||||
|
private SplitContainer splitContainer_Functional;
|
||||||
|
private SplitContainer splitContainer_Information;
|
||||||
|
private GroupBox groupBox_SkelList;
|
||||||
|
private GroupBox groupBox_SkelConfig;
|
||||||
|
private SplitContainer splitContainer_Config;
|
||||||
|
private GroupBox groupBox_PreviewConfig;
|
||||||
|
private Panel panel_MainForm;
|
||||||
|
private ToolStripMenuItem toolStripMenuItem_Help;
|
||||||
|
private ToolStripMenuItem toolStripMenuItem_About;
|
||||||
|
private ToolStripMenuItem toolStripMenuItem_BatchOpen;
|
||||||
|
private TableLayoutPanel tableLayoutPanel;
|
||||||
|
private FlowLayoutPanel flowLayoutPanel_Buttons;
|
||||||
|
private Button button_Add;
|
||||||
|
private Button button_Insert;
|
||||||
|
private Button button_Remove;
|
||||||
|
private Button button_MoveUp;
|
||||||
|
private Button button_MoveDown;
|
||||||
|
private ListView listView_SkelList;
|
||||||
|
private ColumnHeader columnHeader_Name;
|
||||||
|
private ColumnHeader columnHeader_Version;
|
||||||
|
private GroupBox groupBox_Preview;
|
||||||
|
private Panel panel_Preview;
|
||||||
|
private Panel panel_PreviewContainer;
|
||||||
|
private OpenFileDialog openFileDialog_Skel;
|
||||||
|
private OpenFileDialog openFileDialog_Atlas;
|
||||||
|
private ToolTip toolTip1;
|
||||||
|
private PropertyGrid propertyGrid_Skel;
|
||||||
|
}
|
||||||
|
}
|
||||||
179
SpineViewer/src/MainForm.cs
Normal file
179
SpineViewer/src/MainForm.cs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
using NLog;
|
||||||
|
using SpineViewer.Spine;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace SpineViewer
|
||||||
|
{
|
||||||
|
public partial class MainForm : Form
|
||||||
|
{
|
||||||
|
Manager spineManger = null;
|
||||||
|
|
||||||
|
public MainForm()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
InitializeLogConfiguration();
|
||||||
|
|
||||||
|
spineManger = new(listView_SkelList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־<EFBFBD><D6BE>
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeLogConfiguration()
|
||||||
|
{
|
||||||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־
|
||||||
|
var rtbTarget = new NLog.Windows.Forms.RichTextBoxTarget
|
||||||
|
{
|
||||||
|
Name = "rtbTarget",
|
||||||
|
TargetForm = this,
|
||||||
|
TargetRichTextBox = rtbLog,
|
||||||
|
AutoScroll = true,
|
||||||
|
MaxLines = 3000,
|
||||||
|
SupportLinks = true,
|
||||||
|
Layout = "[${level:format=OneLetter}]${date:format=yyyy-MM-dd HH\\:mm\\:ss} - ${message}"
|
||||||
|
};
|
||||||
|
|
||||||
|
rtbTarget.WordColoringRules.Add(new("[D]", "Gray", "Empty", FontStyle.Bold));
|
||||||
|
rtbTarget.WordColoringRules.Add(new("[I]", "Gray", "Empty", FontStyle.Bold));
|
||||||
|
rtbTarget.WordColoringRules.Add(new("[W]", "DarkOrange", "Empty", FontStyle.Bold));
|
||||||
|
rtbTarget.WordColoringRules.Add(new("[E]", "Red", "Empty", FontStyle.Bold));
|
||||||
|
rtbTarget.WordColoringRules.Add(new("[F]", "Red", "Empty", FontStyle.Bold));
|
||||||
|
|
||||||
|
LogManager.Configuration.AddTarget(rtbTarget);
|
||||||
|
LogManager.Configuration.AddRule(LogLevel.Debug, LogLevel.Fatal, rtbTarget);
|
||||||
|
LogManager.ReconfigExistingLoggers();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region <EFBFBD>˵<EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
|
||||||
|
private void toolStripMenuItem_Open_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var dialog = new SkelSelectDialog();
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
spineManger.Add(Spine.Spine.New(dialog.Version, dialog.SkelPath, dialog.AtlasPath));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Program.Logger.Error(ex.ToString());
|
||||||
|
Program.Logger.Error($"Failed to load {dialog.SkelPath} {dialog.AtlasPath}");
|
||||||
|
MessageBox.Show(ex.ToString(), "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><CAA7>", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripMenuItem_BatchOpen_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripMenuItem_Export_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var a = new SizeF(10, 100);
|
||||||
|
spineManger.Spines[0].Position = spineManger.Spines[0].Position + a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toolStripMenuItem_Exit_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ģ<EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>
|
||||||
|
|
||||||
|
// button_Add_Click => toolStripMenuItem_Open_Click
|
||||||
|
|
||||||
|
private void button_Insert_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (listView_SkelList.SelectedIndices.Count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var index = listView_SkelList.SelectedIndices[0];
|
||||||
|
var dialog = new SkelSelectDialog();
|
||||||
|
dialog.ShowDialog();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
spineManger.Insert(index, Spine.Spine.New(dialog.Version, dialog.SkelPath, dialog.AtlasPath));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Program.Logger.Error(ex.ToString());
|
||||||
|
Program.Logger.Error($"Failed to load {dialog.SkelPath} {dialog.AtlasPath}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button_Remove_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (listView_SkelList.SelectedIndices.Count <= 0)
|
||||||
|
return;
|
||||||
|
spineManger.Remove(listView_SkelList.SelectedIndices.Cast<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button_MoveUp_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (listView_SkelList.SelectedIndices.Count <= 0)
|
||||||
|
return;
|
||||||
|
spineManger.MoveUp(listView_SkelList.SelectedIndices[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button_MoveDown_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (listView_SkelList.SelectedIndices.Count <= 0)
|
||||||
|
return;
|
||||||
|
spineManger.MoveDown(listView_SkelList.SelectedIndices[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void listView_SkelList_SelectedIndexChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (listView_SkelList.SelectedIndices.Count <= 0)
|
||||||
|
{
|
||||||
|
button_Insert.Enabled = false;
|
||||||
|
button_Remove.Enabled = false;
|
||||||
|
button_MoveUp.Enabled = false;
|
||||||
|
button_MoveDown.Enabled = false;
|
||||||
|
propertyGrid_Skel.SelectedObject = null;
|
||||||
|
}
|
||||||
|
else if (listView_SkelList.SelectedIndices.Count <= 1)
|
||||||
|
{
|
||||||
|
button_Insert.Enabled = true;
|
||||||
|
button_Remove.Enabled = true;
|
||||||
|
button_MoveUp.Enabled = true;
|
||||||
|
button_MoveDown.Enabled = true;
|
||||||
|
propertyGrid_Skel.SelectedObject = spineManger.Spines[listView_SkelList.SelectedIndices[0]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
button_Insert.Enabled = false;
|
||||||
|
button_Remove.Enabled = true;
|
||||||
|
button_MoveUp.Enabled = false;
|
||||||
|
button_MoveDown.Enabled = false;
|
||||||
|
propertyGrid_Skel.SelectedObject = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Ԥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region <EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
|
||||||
|
private void splitContainer_SplitterMoved(object sender, SplitterEventArgs e)
|
||||||
|
{
|
||||||
|
ActiveControl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void splitContainer_MouseUp(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
ActiveControl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
135
SpineViewer/src/MainForm.resx
Normal file
135
SpineViewer/src/MainForm.resx
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<metadata name="menuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="openFileDialog_Skel.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>156, 25</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="openFileDialog_Atlas.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>404, 23</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>651, 23</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>89</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
||||||
49
SpineViewer/src/Program.cs
Normal file
49
SpineViewer/src/Program.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace SpineViewer
|
||||||
|
{
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
public static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main entry point for the application.
|
||||||
|
/// </summary>
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
InitializeLogConfiguration();
|
||||||
|
Logger.Info("Program Started.");
|
||||||
|
|
||||||
|
// To customize application configuration such as set high DPI settings or default font,
|
||||||
|
// see https://aka.ms/applicationconfiguration.
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
Application.Run(new MainForm());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>־<EFBFBD><D6BE><EFBFBD><EFBFBD>
|
||||||
|
/// </summary>
|
||||||
|
private static void InitializeLogConfiguration()
|
||||||
|
{
|
||||||
|
var config = new NLog.Config.LoggingConfiguration();
|
||||||
|
|
||||||
|
// <20>ļ<EFBFBD><C4BC><EFBFBD>־
|
||||||
|
var fileTarget = new NLog.Targets.FileTarget("fileTarget")
|
||||||
|
{
|
||||||
|
Encoding = System.Text.Encoding.UTF8,
|
||||||
|
FileName = "${basedir}/logs/app.log",
|
||||||
|
ArchiveFileName = "${basedir}/logs/app.{#}.log",
|
||||||
|
ArchiveNumbering = NLog.Targets.ArchiveNumberingMode.Rolling,
|
||||||
|
ArchiveAboveSize = 1048576,
|
||||||
|
MaxArchiveFiles = 5,
|
||||||
|
Layout = "${date:format=yyyy-MM-dd HH\\:mm\\:ss} - ${level:uppercase=true} - ${callsite-filename:includeSourcePath=false}:${callsite-linenumber} - ${message}"
|
||||||
|
};
|
||||||
|
|
||||||
|
config.AddTarget(fileTarget);
|
||||||
|
config.AddRule(LogLevel.Debug, LogLevel.Fatal, fileTarget);
|
||||||
|
LogManager.Configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
39
SpineViewer/src/SkelBatchSelectForm.Designer.cs
generated
Normal file
39
SpineViewer/src/SkelBatchSelectForm.Designer.cs
generated
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
namespace SpineViewer.src
|
||||||
|
{
|
||||||
|
partial class SkelBatchSelectForm
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
this.components = new System.ComponentModel.Container();
|
||||||
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.ClientSize = new System.Drawing.Size(800, 450);
|
||||||
|
this.Text = "SkelBatchSelectForm";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
20
SpineViewer/src/SkelBatchSelectForm.cs
Normal file
20
SpineViewer/src/SkelBatchSelectForm.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace SpineViewer.src
|
||||||
|
{
|
||||||
|
public partial class SkelBatchSelectForm: Form
|
||||||
|
{
|
||||||
|
public SkelBatchSelectForm()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
SpineViewer/src/SkelBatchSelectForm.resx
Normal file
120
SpineViewer/src/SkelBatchSelectForm.resx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
285
SpineViewer/src/SkelSelectDialog.Designer.cs
generated
Normal file
285
SpineViewer/src/SkelSelectDialog.Designer.cs
generated
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
namespace SpineViewer
|
||||||
|
{
|
||||||
|
partial class SkelSelectDialog
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
panel1 = new Panel();
|
||||||
|
tableLayoutPanel1 = new TableLayoutPanel();
|
||||||
|
label4 = new Label();
|
||||||
|
label1 = new Label();
|
||||||
|
label2 = new Label();
|
||||||
|
label3 = new Label();
|
||||||
|
textBox_SkelPath = new TextBox();
|
||||||
|
button_SelectSkel = new Button();
|
||||||
|
button_SelectAtlas = new Button();
|
||||||
|
comboBox_Version = new ComboBox();
|
||||||
|
textBox_AtlasPath = new TextBox();
|
||||||
|
tableLayoutPanel2 = new TableLayoutPanel();
|
||||||
|
button_Ok = new Button();
|
||||||
|
button_Cancel = new Button();
|
||||||
|
openFileDialog_Skel = new OpenFileDialog();
|
||||||
|
openFileDialog_Atlas = new OpenFileDialog();
|
||||||
|
panel1.SuspendLayout();
|
||||||
|
tableLayoutPanel1.SuspendLayout();
|
||||||
|
tableLayoutPanel2.SuspendLayout();
|
||||||
|
SuspendLayout();
|
||||||
|
//
|
||||||
|
// panel1
|
||||||
|
//
|
||||||
|
panel1.Controls.Add(tableLayoutPanel1);
|
||||||
|
panel1.Dock = DockStyle.Fill;
|
||||||
|
panel1.Location = new Point(0, 0);
|
||||||
|
panel1.Name = "panel1";
|
||||||
|
panel1.Padding = new Padding(50, 15, 50, 10);
|
||||||
|
panel1.Size = new Size(907, 286);
|
||||||
|
panel1.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// tableLayoutPanel1
|
||||||
|
//
|
||||||
|
tableLayoutPanel1.AutoSize = true;
|
||||||
|
tableLayoutPanel1.ColumnCount = 4;
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle());
|
||||||
|
tableLayoutPanel1.Controls.Add(label4, 0, 0);
|
||||||
|
tableLayoutPanel1.Controls.Add(label1, 0, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(label2, 0, 2);
|
||||||
|
tableLayoutPanel1.Controls.Add(label3, 0, 3);
|
||||||
|
tableLayoutPanel1.Controls.Add(textBox_SkelPath, 1, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(button_SelectSkel, 3, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(button_SelectAtlas, 3, 2);
|
||||||
|
tableLayoutPanel1.Controls.Add(comboBox_Version, 1, 3);
|
||||||
|
tableLayoutPanel1.Controls.Add(textBox_AtlasPath, 1, 2);
|
||||||
|
tableLayoutPanel1.Controls.Add(tableLayoutPanel2, 0, 4);
|
||||||
|
tableLayoutPanel1.Dock = DockStyle.Fill;
|
||||||
|
tableLayoutPanel1.Location = new Point(50, 15);
|
||||||
|
tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||||
|
tableLayoutPanel1.RowCount = 5;
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new RowStyle());
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new RowStyle());
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new RowStyle());
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new RowStyle());
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new RowStyle());
|
||||||
|
tableLayoutPanel1.Size = new Size(807, 261);
|
||||||
|
tableLayoutPanel1.TabIndex = 0;
|
||||||
|
//
|
||||||
|
// label4
|
||||||
|
//
|
||||||
|
label4.AutoSize = true;
|
||||||
|
tableLayoutPanel1.SetColumnSpan(label4, 4);
|
||||||
|
label4.Dock = DockStyle.Fill;
|
||||||
|
label4.Location = new Point(15, 15);
|
||||||
|
label4.Margin = new Padding(15);
|
||||||
|
label4.Name = "label4";
|
||||||
|
label4.Size = new Size(777, 24);
|
||||||
|
label4.TabIndex = 11;
|
||||||
|
label4.Text = "说明:如果没有选择atlas,则会自动读取与skel同目录下同名的atlas文件";
|
||||||
|
label4.TextAlign = ContentAlignment.MiddleCenter;
|
||||||
|
//
|
||||||
|
// label1
|
||||||
|
//
|
||||||
|
label1.Anchor = AnchorStyles.Right;
|
||||||
|
label1.AutoSize = true;
|
||||||
|
label1.Location = new Point(10, 62);
|
||||||
|
label1.Name = "label1";
|
||||||
|
label1.Size = new Size(119, 24);
|
||||||
|
label1.TabIndex = 0;
|
||||||
|
label1.Text = "skel文件路径:";
|
||||||
|
//
|
||||||
|
// label2
|
||||||
|
//
|
||||||
|
label2.Anchor = AnchorStyles.Right;
|
||||||
|
label2.AutoSize = true;
|
||||||
|
label2.Location = new Point(3, 102);
|
||||||
|
label2.Name = "label2";
|
||||||
|
label2.Size = new Size(126, 24);
|
||||||
|
label2.TabIndex = 1;
|
||||||
|
label2.Text = "atlas文件路径:";
|
||||||
|
//
|
||||||
|
// label3
|
||||||
|
//
|
||||||
|
label3.Anchor = AnchorStyles.Right;
|
||||||
|
label3.AutoSize = true;
|
||||||
|
label3.Location = new Point(79, 141);
|
||||||
|
label3.Name = "label3";
|
||||||
|
label3.Size = new Size(50, 24);
|
||||||
|
label3.TabIndex = 2;
|
||||||
|
label3.Text = "版本:";
|
||||||
|
//
|
||||||
|
// textBox_SkelPath
|
||||||
|
//
|
||||||
|
tableLayoutPanel1.SetColumnSpan(textBox_SkelPath, 2);
|
||||||
|
textBox_SkelPath.Dock = DockStyle.Fill;
|
||||||
|
textBox_SkelPath.Location = new Point(135, 57);
|
||||||
|
textBox_SkelPath.Name = "textBox_SkelPath";
|
||||||
|
textBox_SkelPath.Size = new Size(630, 30);
|
||||||
|
textBox_SkelPath.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// button_SelectSkel
|
||||||
|
//
|
||||||
|
button_SelectSkel.AutoSize = true;
|
||||||
|
button_SelectSkel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||||
|
button_SelectSkel.Location = new Point(771, 57);
|
||||||
|
button_SelectSkel.Name = "button_SelectSkel";
|
||||||
|
button_SelectSkel.Size = new Size(32, 34);
|
||||||
|
button_SelectSkel.TabIndex = 5;
|
||||||
|
button_SelectSkel.Text = "...";
|
||||||
|
button_SelectSkel.UseVisualStyleBackColor = true;
|
||||||
|
button_SelectSkel.Click += button_SelectSkel_Click;
|
||||||
|
//
|
||||||
|
// button_SelectAtlas
|
||||||
|
//
|
||||||
|
button_SelectAtlas.AutoSize = true;
|
||||||
|
button_SelectAtlas.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||||
|
button_SelectAtlas.Location = new Point(771, 97);
|
||||||
|
button_SelectAtlas.Name = "button_SelectAtlas";
|
||||||
|
button_SelectAtlas.Size = new Size(32, 34);
|
||||||
|
button_SelectAtlas.TabIndex = 6;
|
||||||
|
button_SelectAtlas.Text = "...";
|
||||||
|
button_SelectAtlas.UseVisualStyleBackColor = true;
|
||||||
|
button_SelectAtlas.Click += button_SelectAtlas_Click;
|
||||||
|
//
|
||||||
|
// comboBox_Version
|
||||||
|
//
|
||||||
|
comboBox_Version.Anchor = AnchorStyles.Left;
|
||||||
|
comboBox_Version.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||||
|
comboBox_Version.FormattingEnabled = true;
|
||||||
|
comboBox_Version.Location = new Point(135, 137);
|
||||||
|
comboBox_Version.Name = "comboBox_Version";
|
||||||
|
comboBox_Version.Size = new Size(182, 32);
|
||||||
|
comboBox_Version.Sorted = true;
|
||||||
|
comboBox_Version.TabIndex = 9;
|
||||||
|
//
|
||||||
|
// textBox_AtlasPath
|
||||||
|
//
|
||||||
|
tableLayoutPanel1.SetColumnSpan(textBox_AtlasPath, 2);
|
||||||
|
textBox_AtlasPath.Dock = DockStyle.Fill;
|
||||||
|
textBox_AtlasPath.Location = new Point(135, 97);
|
||||||
|
textBox_AtlasPath.Name = "textBox_AtlasPath";
|
||||||
|
textBox_AtlasPath.Size = new Size(630, 30);
|
||||||
|
textBox_AtlasPath.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// tableLayoutPanel2
|
||||||
|
//
|
||||||
|
tableLayoutPanel2.AutoSize = true;
|
||||||
|
tableLayoutPanel2.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
||||||
|
tableLayoutPanel2.ColumnCount = 2;
|
||||||
|
tableLayoutPanel1.SetColumnSpan(tableLayoutPanel2, 4);
|
||||||
|
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
||||||
|
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
||||||
|
tableLayoutPanel2.Controls.Add(button_Ok, 0, 0);
|
||||||
|
tableLayoutPanel2.Controls.Add(button_Cancel, 1, 0);
|
||||||
|
tableLayoutPanel2.Dock = DockStyle.Bottom;
|
||||||
|
tableLayoutPanel2.Location = new Point(3, 218);
|
||||||
|
tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||||
|
tableLayoutPanel2.RowCount = 1;
|
||||||
|
tableLayoutPanel2.RowStyles.Add(new RowStyle());
|
||||||
|
tableLayoutPanel2.Size = new Size(801, 40);
|
||||||
|
tableLayoutPanel2.TabIndex = 10;
|
||||||
|
//
|
||||||
|
// button_Ok
|
||||||
|
//
|
||||||
|
button_Ok.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||||
|
button_Ok.Location = new Point(258, 3);
|
||||||
|
button_Ok.Margin = new Padding(3, 3, 30, 3);
|
||||||
|
button_Ok.Name = "button_Ok";
|
||||||
|
button_Ok.Size = new Size(112, 34);
|
||||||
|
button_Ok.TabIndex = 7;
|
||||||
|
button_Ok.Text = "确认";
|
||||||
|
button_Ok.UseVisualStyleBackColor = true;
|
||||||
|
button_Ok.Click += button_Ok_Click;
|
||||||
|
//
|
||||||
|
// button_Cancel
|
||||||
|
//
|
||||||
|
button_Cancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
|
||||||
|
button_Cancel.Location = new Point(430, 3);
|
||||||
|
button_Cancel.Margin = new Padding(30, 3, 3, 3);
|
||||||
|
button_Cancel.Name = "button_Cancel";
|
||||||
|
button_Cancel.Size = new Size(112, 34);
|
||||||
|
button_Cancel.TabIndex = 8;
|
||||||
|
button_Cancel.Text = "取消";
|
||||||
|
button_Cancel.UseVisualStyleBackColor = true;
|
||||||
|
button_Cancel.Click += button_Cancel_Click;
|
||||||
|
//
|
||||||
|
// openFileDialog_Skel
|
||||||
|
//
|
||||||
|
openFileDialog_Skel.AddExtension = false;
|
||||||
|
openFileDialog_Skel.AddToRecent = false;
|
||||||
|
openFileDialog_Skel.Filter = "skel 文件 (*.skel; *.json)|*.skel;*.json";
|
||||||
|
//
|
||||||
|
// openFileDialog_Atlas
|
||||||
|
//
|
||||||
|
openFileDialog_Atlas.AddExtension = false;
|
||||||
|
openFileDialog_Atlas.AddToRecent = false;
|
||||||
|
openFileDialog_Atlas.Filter = "atlas 文件 (*.atlas)|*.atlas";
|
||||||
|
//
|
||||||
|
// SkelSelectDialog
|
||||||
|
//
|
||||||
|
AcceptButton = button_Ok;
|
||||||
|
AutoScaleDimensions = new SizeF(11F, 24F);
|
||||||
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
|
CancelButton = button_Cancel;
|
||||||
|
ClientSize = new Size(907, 286);
|
||||||
|
Controls.Add(panel1);
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
|
MaximizeBox = false;
|
||||||
|
MinimizeBox = false;
|
||||||
|
Name = "SkelSelectDialog";
|
||||||
|
ShowIcon = false;
|
||||||
|
ShowInTaskbar = false;
|
||||||
|
StartPosition = FormStartPosition.CenterScreen;
|
||||||
|
Text = "打开骨骼";
|
||||||
|
panel1.ResumeLayout(false);
|
||||||
|
panel1.PerformLayout();
|
||||||
|
tableLayoutPanel1.ResumeLayout(false);
|
||||||
|
tableLayoutPanel1.PerformLayout();
|
||||||
|
tableLayoutPanel2.ResumeLayout(false);
|
||||||
|
ResumeLayout(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private Panel panel1;
|
||||||
|
private TableLayoutPanel tableLayoutPanel1;
|
||||||
|
private Label label1;
|
||||||
|
private Label label2;
|
||||||
|
private Label label3;
|
||||||
|
private TextBox textBox_SkelPath;
|
||||||
|
private Button button_SelectSkel;
|
||||||
|
private Button button_SelectAtlas;
|
||||||
|
private Button button_Ok;
|
||||||
|
private Button button_Cancel;
|
||||||
|
private ComboBox comboBox_Version;
|
||||||
|
private TextBox textBox_AtlasPath;
|
||||||
|
private TableLayoutPanel tableLayoutPanel2;
|
||||||
|
private OpenFileDialog openFileDialog_Skel;
|
||||||
|
private OpenFileDialog openFileDialog_Atlas;
|
||||||
|
private Label label4;
|
||||||
|
}
|
||||||
|
}
|
||||||
80
SpineViewer/src/SkelSelectDialog.cs
Normal file
80
SpineViewer/src/SkelSelectDialog.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using SpineViewer.Spine;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Data;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace SpineViewer
|
||||||
|
{
|
||||||
|
public partial class SkelSelectDialog : Form
|
||||||
|
{
|
||||||
|
public string SkelPath { get; private set; }
|
||||||
|
public string? AtlasPath { get; private set; }
|
||||||
|
public Spine.Version Version { get; private set; }
|
||||||
|
|
||||||
|
public SkelSelectDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
comboBox_Version.DataSource = VersionHelper.Versions.ToList();
|
||||||
|
comboBox_Version.DisplayMember = "Value";
|
||||||
|
comboBox_Version.ValueMember = "Key";
|
||||||
|
comboBox_Version.SelectedValue = Spine.Version.V38;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button_SelectSkel_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
openFileDialog_Skel.InitialDirectory = Path.GetDirectoryName(textBox_SkelPath.Text);
|
||||||
|
if (openFileDialog_Skel.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
textBox_SkelPath.Text = Path.GetFullPath(openFileDialog_Skel.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button_SelectAtlas_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
openFileDialog_Atlas.InitialDirectory = Path.GetDirectoryName(textBox_AtlasPath.Text);
|
||||||
|
if (openFileDialog_Atlas.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
textBox_AtlasPath.Text = Path.GetFullPath(openFileDialog_Atlas.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button_Ok_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var skelPath = textBox_SkelPath.Text;
|
||||||
|
var atlasPath = textBox_AtlasPath.Text;
|
||||||
|
|
||||||
|
if (!File.Exists(skelPath))
|
||||||
|
{
|
||||||
|
MessageBox.Show($"{skelPath}", "skel文件不存在", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(atlasPath))
|
||||||
|
{
|
||||||
|
atlasPath = null;
|
||||||
|
}
|
||||||
|
else if (!File.Exists(atlasPath))
|
||||||
|
{
|
||||||
|
MessageBox.Show($"{atlasPath}", "atlas文件不存在", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkelPath = skelPath;
|
||||||
|
AtlasPath = atlasPath;
|
||||||
|
Version = (Spine.Version)comboBox_Version.SelectedValue;
|
||||||
|
|
||||||
|
DialogResult = DialogResult.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void button_Cancel_Click(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
DialogResult = DialogResult.Cancel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
126
SpineViewer/src/SkelSelectDialog.resx
Normal file
126
SpineViewer/src/SkelSelectDialog.resx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<metadata name="openFileDialog_Skel.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>156, 25</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="openFileDialog_Atlas.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>404, 23</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
||||||
64
SpineViewer/src/Spine/BlendMode.cs
Normal file
64
SpineViewer/src/Spine/BlendMode.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SpineViewer.Spine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// SFML 混合模式
|
||||||
|
/// </summary>
|
||||||
|
public static class BlendMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Alpha Blend
|
||||||
|
/// <code>
|
||||||
|
/// res.c = src.c * src.a + dst.c * (1 - src.a)
|
||||||
|
/// res.a = src.a * 1 + dst.a * (1 - src.a)
|
||||||
|
/// </code>
|
||||||
|
/// </summary>
|
||||||
|
public static SFML.Graphics.BlendMode Normal = SFML.Graphics.BlendMode.Alpha;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additive Blend
|
||||||
|
/// <code>
|
||||||
|
/// res.c = src.c * src.a + dst.c * 1
|
||||||
|
/// res.a = src.a * 1 + dst.a * 1
|
||||||
|
/// </code>
|
||||||
|
/// </summary>
|
||||||
|
public static SFML.Graphics.BlendMode Additive = SFML.Graphics.BlendMode.Add;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiply Blend (PremultipliedAlpha Only)
|
||||||
|
/// <code>
|
||||||
|
/// res.c = src.c * dst.c + dst.c * (1 - src.a)
|
||||||
|
/// res.a = src.a * 1 + dst.a * (1 - src.a)
|
||||||
|
/// </code>
|
||||||
|
/// </summary>
|
||||||
|
public static SFML.Graphics.BlendMode Multiply = new(
|
||||||
|
SFML.Graphics.BlendMode.Factor.DstColor,
|
||||||
|
SFML.Graphics.BlendMode.Factor.OneMinusSrcAlpha,
|
||||||
|
SFML.Graphics.BlendMode.Equation.Add,
|
||||||
|
SFML.Graphics.BlendMode.Factor.One,
|
||||||
|
SFML.Graphics.BlendMode.Factor.OneMinusSrcAlpha,
|
||||||
|
SFML.Graphics.BlendMode.Equation.Add
|
||||||
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Screen Blend (PremultipliedAlpha Only)
|
||||||
|
/// <code>
|
||||||
|
/// res.c = src.c * 1 + dst.c * (1 - src.c) = 1 - [(1 - src.c)(1 - dst.c)]
|
||||||
|
/// res.a = src.a * 1 + dst.a * (1 - src.a)
|
||||||
|
/// </code>
|
||||||
|
/// </summary>
|
||||||
|
public static SFML.Graphics.BlendMode Screen = new(
|
||||||
|
SFML.Graphics.BlendMode.Factor.One,
|
||||||
|
SFML.Graphics.BlendMode.Factor.OneMinusSrcColor,
|
||||||
|
SFML.Graphics.BlendMode.Equation.Add,
|
||||||
|
SFML.Graphics.BlendMode.Factor.One,
|
||||||
|
SFML.Graphics.BlendMode.Factor.OneMinusSrcAlpha,
|
||||||
|
SFML.Graphics.BlendMode.Equation.Add
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
323
SpineViewer/src/Spine/Implementations/Spine36.cs
Normal file
323
SpineViewer/src/Spine/Implementations/Spine36.cs
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SpineRuntime36;
|
||||||
|
|
||||||
|
namespace SpineViewer.Spine.Implementations
|
||||||
|
{
|
||||||
|
[SpineImplementation(Version.V36)]
|
||||||
|
internal class Spine36 : Spine
|
||||||
|
{
|
||||||
|
private class TextureLoader : SpineRuntime36.TextureLoader
|
||||||
|
{
|
||||||
|
public void Load(AtlasPage page, string path)
|
||||||
|
{
|
||||||
|
var texture = new SFML.Graphics.Texture(path);
|
||||||
|
if (page.magFilter == TextureFilter.Linear)
|
||||||
|
texture.Smooth = true;
|
||||||
|
if (page.uWrap == TextureWrap.Repeat && page.vWrap == TextureWrap.Repeat)
|
||||||
|
texture.Repeated = true;
|
||||||
|
|
||||||
|
page.rendererObject = texture;
|
||||||
|
page.width = (int)texture.Size.X;
|
||||||
|
page.height = (int)texture.Size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unload(object texture)
|
||||||
|
{
|
||||||
|
((SFML.Graphics.Texture)texture).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static TextureLoader textureLoader = new();
|
||||||
|
|
||||||
|
private Atlas atlas;
|
||||||
|
private SkeletonBinary? skeletonBinary;
|
||||||
|
private SkeletonJson? skeletonJson;
|
||||||
|
private SkeletonData skeletonData;
|
||||||
|
private AnimationStateData animationStateData;
|
||||||
|
|
||||||
|
private Skeleton skeleton;
|
||||||
|
private AnimationState animationState;
|
||||||
|
|
||||||
|
private SkeletonClipping clipping = new();
|
||||||
|
|
||||||
|
public Spine36(string skelPath, string? atlasPath = null) : base(skelPath, atlasPath)
|
||||||
|
{
|
||||||
|
atlas = new Atlas(AtlasPath, textureLoader);
|
||||||
|
if (Path.GetExtension(SkelPath) == ".skel")
|
||||||
|
{
|
||||||
|
skeletonJson = null;
|
||||||
|
skeletonBinary = new SkeletonBinary(atlas);
|
||||||
|
skeletonData = skeletonBinary.ReadSkeletonData(SkelPath);
|
||||||
|
}
|
||||||
|
else if (Path.GetExtension(SkelPath) == ".json")
|
||||||
|
{
|
||||||
|
skeletonBinary = null;
|
||||||
|
skeletonJson = new SkeletonJson(atlas);
|
||||||
|
skeletonData = skeletonJson.ReadSkeletonData(SkelPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Unknown skeleton file format {SkelPath}");
|
||||||
|
}
|
||||||
|
animationStateData = new AnimationStateData(skeletonData);
|
||||||
|
skeleton = new Skeleton(skeletonData);
|
||||||
|
animationState = new AnimationState(animationStateData);
|
||||||
|
|
||||||
|
foreach (var anime in skeletonData.Animations)
|
||||||
|
animationNames.Add(anime.Name);
|
||||||
|
CurrentAnimation = DefaultAnimationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
atlas.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float Scale
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (skeletonBinary is not null)
|
||||||
|
return skeletonBinary.Scale;
|
||||||
|
else if (skeletonJson is not null)
|
||||||
|
return skeletonJson.Scale;
|
||||||
|
else
|
||||||
|
return 1f;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// 保存状态
|
||||||
|
var position = Position;
|
||||||
|
var flipX = FlipX;
|
||||||
|
var flipY = FlipY;
|
||||||
|
var savedTrack0 = animationState.GetCurrent(0);
|
||||||
|
|
||||||
|
var val = Math.Max(value, SCALE_MIN);
|
||||||
|
if (skeletonBinary is not null)
|
||||||
|
{
|
||||||
|
skeletonBinary.Scale = val;
|
||||||
|
skeletonData = skeletonBinary.ReadSkeletonData(SkelPath);
|
||||||
|
}
|
||||||
|
else if (skeletonJson is not null)
|
||||||
|
{
|
||||||
|
skeletonJson.Scale = val;
|
||||||
|
skeletonData = skeletonJson.ReadSkeletonData(SkelPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reload skel-dependent data
|
||||||
|
animationStateData = new AnimationStateData(skeletonData) { DefaultMix = animationStateData.DefaultMix };
|
||||||
|
skeleton = new Skeleton(skeletonData);
|
||||||
|
animationState = new AnimationState(animationStateData);
|
||||||
|
|
||||||
|
// 恢复状态
|
||||||
|
Position = position;
|
||||||
|
FlipX = flipX;
|
||||||
|
FlipY = flipY;
|
||||||
|
|
||||||
|
// 恢复原本 Track0 上所有动画
|
||||||
|
if (savedTrack0 is not null)
|
||||||
|
{
|
||||||
|
var entry = animationState.SetAnimation(0, savedTrack0.Animation.Name, true);
|
||||||
|
entry.TrackTime = savedTrack0.TrackTime;
|
||||||
|
var savedEntry = savedTrack0.Next;
|
||||||
|
while (savedEntry is not null)
|
||||||
|
{
|
||||||
|
entry = animationState.AddAnimation(0, savedEntry.Animation.Name, true, 0);
|
||||||
|
entry.TrackTime = savedEntry.TrackTime;
|
||||||
|
savedEntry = savedEntry.Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PointF Position
|
||||||
|
{
|
||||||
|
get => new(skeleton.X, skeleton.Y);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
skeleton.X = value.X;
|
||||||
|
skeleton.Y = value.Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool FlipX
|
||||||
|
{
|
||||||
|
get => skeleton.FlipX;
|
||||||
|
set => skeleton.FlipX = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool FlipY
|
||||||
|
{
|
||||||
|
get => skeleton.FlipY;
|
||||||
|
set => skeleton.FlipY = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string CurrentAnimation
|
||||||
|
{
|
||||||
|
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||||
|
set { if (animationNames.Contains(value)) animationState.SetAnimation(0, value, true); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RectangleF Bounds
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
float[] _ = [];
|
||||||
|
skeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
|
||||||
|
return new RectangleF(x, y, w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
|
||||||
|
|
||||||
|
public override void Update(float delta)
|
||||||
|
{
|
||||||
|
skeleton.Update(delta);
|
||||||
|
animationState.Update(delta);
|
||||||
|
animationState.Apply(skeleton);
|
||||||
|
skeleton.UpdateWorldTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SFML.Graphics.BlendMode GetSFMLBlendMode(SpineRuntime36.BlendMode spineBlendMode)
|
||||||
|
{
|
||||||
|
return spineBlendMode switch
|
||||||
|
{
|
||||||
|
SpineRuntime36.BlendMode.Normal => BlendMode.Normal,
|
||||||
|
SpineRuntime36.BlendMode.Additive => BlendMode.Additive,
|
||||||
|
SpineRuntime36.BlendMode.Multiply => BlendMode.Multiply,
|
||||||
|
SpineRuntime36.BlendMode.Screen => BlendMode.Screen,
|
||||||
|
_ => throw new NotImplementedException($"{spineBlendMode}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
|
||||||
|
{
|
||||||
|
vertexArray.Clear();
|
||||||
|
states.Texture = null;
|
||||||
|
|
||||||
|
// 要用 DrawOrder 而不是 Slots
|
||||||
|
foreach (var slot in skeleton.DrawOrder)
|
||||||
|
{
|
||||||
|
var attachment = slot.Attachment;
|
||||||
|
|
||||||
|
SFML.Graphics.Texture texture;
|
||||||
|
|
||||||
|
float[] worldVertices = worldVerticesBuffer; // 顶点世界坐标, 连续的 [x0, y0, x1, y1, ...] 坐标值
|
||||||
|
int worldVerticesCount; // 等于顶点数组的长度除以 2
|
||||||
|
int[] worldTriangleIndices; // 三角形索引, 从顶点坐标数组取的时候要乘以 2, 最大值是 worldVerticesCount - 1
|
||||||
|
int worldTriangleIndicesLength; // 三角形索引数组长度
|
||||||
|
float[] uvs; // 纹理坐标
|
||||||
|
float tintR = skeleton.R * slot.R;
|
||||||
|
float tintG = skeleton.G * slot.G;
|
||||||
|
float tintB = skeleton.B * slot.B;
|
||||||
|
float tintA = skeleton.A * slot.A;
|
||||||
|
|
||||||
|
if (attachment is RegionAttachment regionAttachment)
|
||||||
|
{
|
||||||
|
texture = (SFML.Graphics.Texture)((AtlasRegion)regionAttachment.RendererObject).page.rendererObject;
|
||||||
|
|
||||||
|
regionAttachment.ComputeWorldVertices(slot.Bone, worldVertices, 0);
|
||||||
|
worldVerticesCount = 4;
|
||||||
|
worldTriangleIndices = [0, 1, 2, 2, 3, 0];
|
||||||
|
worldTriangleIndicesLength = 6;
|
||||||
|
uvs = regionAttachment.UVs;
|
||||||
|
tintR *= regionAttachment.R;
|
||||||
|
tintG *= regionAttachment.G;
|
||||||
|
tintB *= regionAttachment.B;
|
||||||
|
tintA *= regionAttachment.A;
|
||||||
|
}
|
||||||
|
else if (attachment is MeshAttachment meshAttachment)
|
||||||
|
{
|
||||||
|
texture = (SFML.Graphics.Texture)((AtlasRegion)meshAttachment.RendererObject).page.rendererObject;
|
||||||
|
|
||||||
|
if (meshAttachment.WorldVerticesLength > worldVertices.Length)
|
||||||
|
worldVertices = worldVerticesBuffer = new float[meshAttachment.WorldVerticesLength * 2];
|
||||||
|
meshAttachment.ComputeWorldVertices(slot, worldVertices);
|
||||||
|
worldVerticesCount = meshAttachment.WorldVerticesLength / 2;
|
||||||
|
worldTriangleIndices = meshAttachment.Triangles;
|
||||||
|
worldTriangleIndicesLength = meshAttachment.Triangles.Length;
|
||||||
|
uvs = meshAttachment.UVs;
|
||||||
|
tintR *= meshAttachment.R;
|
||||||
|
tintG *= meshAttachment.G;
|
||||||
|
tintB *= meshAttachment.B;
|
||||||
|
tintA *= meshAttachment.A;
|
||||||
|
}
|
||||||
|
else if (attachment is ClippingAttachment clippingAttachment)
|
||||||
|
{
|
||||||
|
clipping.ClipStart(slot, clippingAttachment);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clipping.ClipEnd(slot);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SFML.Graphics.BlendMode blendMode = GetSFMLBlendMode(slot.Data.BlendMode);
|
||||||
|
|
||||||
|
states.Texture ??= texture;
|
||||||
|
if (states.BlendMode != blendMode || states.Texture != texture)
|
||||||
|
{
|
||||||
|
if (vertexArray.VertexCount > 0)
|
||||||
|
{
|
||||||
|
if (UsePremultipliedAlpha && (states.BlendMode == BlendMode.Normal || states.BlendMode == BlendMode.Additive))
|
||||||
|
states.Shader = FragmentShader;
|
||||||
|
else
|
||||||
|
states.Shader = null;
|
||||||
|
target.Draw(vertexArray, states);
|
||||||
|
vertexArray.Clear();
|
||||||
|
}
|
||||||
|
states.BlendMode = blendMode;
|
||||||
|
states.Texture = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipping.IsClipping)
|
||||||
|
{
|
||||||
|
// 这里必须单独记录 Count, 和 Items 的 Length 是不一致的
|
||||||
|
clipping.ClipTriangles(worldVertices, worldVerticesCount * 2, worldTriangleIndices, worldTriangleIndicesLength, uvs);
|
||||||
|
worldVertices = clipping.ClippedVertices.Items;
|
||||||
|
worldVerticesCount = clipping.ClippedVertices.Count / 2;
|
||||||
|
worldTriangleIndices = clipping.ClippedTriangles.Items;
|
||||||
|
worldTriangleIndicesLength = clipping.ClippedTriangles.Count;
|
||||||
|
uvs = clipping.ClippedUVs.Items;
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureSizeX = texture.Size.X;
|
||||||
|
var textureSizeY = texture.Size.Y;
|
||||||
|
|
||||||
|
SFML.Graphics.Vertex vertex = new();
|
||||||
|
vertex.Color.R = (byte)(tintR * 255);
|
||||||
|
vertex.Color.G = (byte)(tintG * 255);
|
||||||
|
vertex.Color.B = (byte)(tintB * 255);
|
||||||
|
vertex.Color.A = (byte)(tintA * 255);
|
||||||
|
|
||||||
|
// 必须用 worldTriangleIndicesLength 不能直接 foreach
|
||||||
|
for (int i = 0; i < worldTriangleIndicesLength; i++)
|
||||||
|
{
|
||||||
|
var index = worldTriangleIndices[i] * 2;
|
||||||
|
vertex.Position.X = worldVertices[index];
|
||||||
|
vertex.Position.Y = worldVertices[index + 1];
|
||||||
|
vertex.TexCoords.X = uvs[index] * textureSizeX;
|
||||||
|
vertex.TexCoords.Y = uvs[index + 1] * textureSizeY;
|
||||||
|
vertexArray.Append(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
clipping.ClipEnd(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UsePremultipliedAlpha && (states.BlendMode == BlendMode.Normal || states.BlendMode == BlendMode.Additive))
|
||||||
|
states.Shader = FragmentShader;
|
||||||
|
else
|
||||||
|
states.Shader = null;
|
||||||
|
target.Draw(vertexArray, states);
|
||||||
|
clipping.ClipEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
335
SpineViewer/src/Spine/Implementations/Spine38.cs
Normal file
335
SpineViewer/src/Spine/Implementations/Spine38.cs
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SpineRuntime38;
|
||||||
|
using SpineViewer.Spine;
|
||||||
|
|
||||||
|
namespace SpineViewer.Spine.Implementations
|
||||||
|
{
|
||||||
|
[SpineImplementation(Version.V38)]
|
||||||
|
internal class Spine38 : Spine
|
||||||
|
{
|
||||||
|
private class TextureLoader : SpineRuntime38.TextureLoader
|
||||||
|
{
|
||||||
|
public void Load(AtlasPage page, string path)
|
||||||
|
{
|
||||||
|
var texture = new SFML.Graphics.Texture(path);
|
||||||
|
if (page.magFilter == TextureFilter.Linear)
|
||||||
|
texture.Smooth = true;
|
||||||
|
if (page.uWrap == TextureWrap.Repeat && page.vWrap == TextureWrap.Repeat)
|
||||||
|
texture.Repeated = true;
|
||||||
|
|
||||||
|
page.rendererObject = texture;
|
||||||
|
page.width = (int)texture.Size.X;
|
||||||
|
page.height = (int)texture.Size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unload(object texture)
|
||||||
|
{
|
||||||
|
((SFML.Graphics.Texture)texture).Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextureLoader textureLoader = new();
|
||||||
|
|
||||||
|
private Atlas atlas;
|
||||||
|
private SkeletonBinary? skeletonBinary;
|
||||||
|
private SkeletonJson? skeletonJson;
|
||||||
|
private SkeletonData skeletonData;
|
||||||
|
private AnimationStateData animationStateData;
|
||||||
|
|
||||||
|
private Skeleton skeleton;
|
||||||
|
private AnimationState animationState;
|
||||||
|
|
||||||
|
private SkeletonClipping clipping = new();
|
||||||
|
|
||||||
|
public Spine38(string skelPath, string? atlasPath = null) : base(skelPath, atlasPath)
|
||||||
|
{
|
||||||
|
atlas = new Atlas(AtlasPath, textureLoader);
|
||||||
|
if (Path.GetExtension(SkelPath) == ".skel")
|
||||||
|
{
|
||||||
|
skeletonJson = null;
|
||||||
|
skeletonBinary = new SkeletonBinary(atlas);
|
||||||
|
skeletonData = skeletonBinary.ReadSkeletonData(SkelPath);
|
||||||
|
}
|
||||||
|
else if (Path.GetExtension(SkelPath) == ".json")
|
||||||
|
{
|
||||||
|
skeletonBinary = null;
|
||||||
|
skeletonJson = new SkeletonJson(atlas);
|
||||||
|
skeletonData = skeletonJson.ReadSkeletonData(SkelPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Unknown skeleton file format {SkelPath}");
|
||||||
|
}
|
||||||
|
animationStateData = new AnimationStateData(skeletonData);
|
||||||
|
|
||||||
|
skeleton = new Skeleton(skeletonData);
|
||||||
|
animationState = new AnimationState(animationStateData);
|
||||||
|
|
||||||
|
foreach (var anime in skeletonData.Animations)
|
||||||
|
animationNames.Add(anime.Name);
|
||||||
|
|
||||||
|
CurrentAnimation = DefaultAnimationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
atlas.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float Scale
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (skeletonBinary is not null)
|
||||||
|
return skeletonBinary.Scale;
|
||||||
|
else if (skeletonJson is not null)
|
||||||
|
return skeletonJson.Scale;
|
||||||
|
else
|
||||||
|
return 1f;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// 保存状态
|
||||||
|
var position = Position;
|
||||||
|
var flipX = FlipX;
|
||||||
|
var flipY = FlipY;
|
||||||
|
var savedTrack0 = animationState.GetCurrent(0);
|
||||||
|
|
||||||
|
var val = Math.Max(value, SCALE_MIN);
|
||||||
|
if (skeletonBinary is not null)
|
||||||
|
{
|
||||||
|
skeletonBinary.Scale = val;
|
||||||
|
skeletonData = skeletonBinary.ReadSkeletonData(SkelPath);
|
||||||
|
}
|
||||||
|
else if (skeletonJson is not null)
|
||||||
|
{
|
||||||
|
skeletonJson.Scale = val;
|
||||||
|
skeletonData = skeletonJson.ReadSkeletonData(SkelPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reload skel-dependent data
|
||||||
|
animationStateData = new AnimationStateData(skeletonData) { DefaultMix = animationStateData.DefaultMix };
|
||||||
|
skeleton = new Skeleton(skeletonData);
|
||||||
|
animationState = new AnimationState(animationStateData);
|
||||||
|
|
||||||
|
// 恢复状态
|
||||||
|
Position = position;
|
||||||
|
FlipX = flipX;
|
||||||
|
FlipY = flipY;
|
||||||
|
|
||||||
|
// 恢复原本 Track0 上所有动画
|
||||||
|
if (savedTrack0 is not null)
|
||||||
|
{
|
||||||
|
var entry = animationState.SetAnimation(0, savedTrack0.Animation.Name, true);
|
||||||
|
entry.TrackTime = savedTrack0.TrackTime;
|
||||||
|
var savedEntry = savedTrack0.Next;
|
||||||
|
while (savedEntry is not null)
|
||||||
|
{
|
||||||
|
entry = animationState.AddAnimation(0, savedEntry.Animation.Name, true, 0);
|
||||||
|
entry.TrackTime = savedEntry.TrackTime;
|
||||||
|
savedEntry = savedEntry.Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PointF Position
|
||||||
|
{
|
||||||
|
get => new(skeleton.X, skeleton.Y);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
skeleton.X = value.X;
|
||||||
|
skeleton.Y = value.Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool FlipX
|
||||||
|
{
|
||||||
|
get => skeleton.ScaleX < 0;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (skeleton.ScaleX > 0 && value || skeleton.ScaleX < 0 && !value)
|
||||||
|
skeleton.ScaleX *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool FlipY
|
||||||
|
{
|
||||||
|
get => skeleton.ScaleY < 0;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (skeleton.ScaleY > 0 && value || skeleton.ScaleY < 0 && !value)
|
||||||
|
skeleton.ScaleY *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string CurrentAnimation
|
||||||
|
{
|
||||||
|
get => animationState.GetCurrent(0)?.Animation.Name ?? DefaultAnimationName;
|
||||||
|
set { if (animationNames.Contains(value)) animationState.SetAnimation(0, value, true); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override RectangleF Bounds
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
float[] _ = [];
|
||||||
|
skeleton.GetBounds(out var x, out var y, out var w, out var h, ref _);
|
||||||
|
return new RectangleF(x, y, w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float GetAnimationDuration(string name) { return skeletonData.FindAnimation(name)?.Duration ?? 0f; }
|
||||||
|
|
||||||
|
public override void Update(float delta)
|
||||||
|
{
|
||||||
|
skeleton.Update(delta);
|
||||||
|
animationState.Update(delta);
|
||||||
|
animationState.Apply(skeleton);
|
||||||
|
skeleton.UpdateWorldTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SFML.Graphics.BlendMode GetSFMLBlendMode(SpineRuntime38.BlendMode spineBlendMode)
|
||||||
|
{
|
||||||
|
return spineBlendMode switch
|
||||||
|
{
|
||||||
|
SpineRuntime38.BlendMode.Normal => BlendMode.Normal,
|
||||||
|
SpineRuntime38.BlendMode.Additive => BlendMode.Additive,
|
||||||
|
SpineRuntime38.BlendMode.Multiply => BlendMode.Multiply,
|
||||||
|
SpineRuntime38.BlendMode.Screen => BlendMode.Screen,
|
||||||
|
_ => throw new NotImplementedException($"{spineBlendMode}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states)
|
||||||
|
{
|
||||||
|
vertexArray.Clear();
|
||||||
|
states.Texture = null;
|
||||||
|
|
||||||
|
// 要用 DrawOrder 而不是 Slots
|
||||||
|
foreach (var slot in skeleton.DrawOrder)
|
||||||
|
{
|
||||||
|
var attachment = slot.Attachment;
|
||||||
|
|
||||||
|
SFML.Graphics.Texture texture;
|
||||||
|
|
||||||
|
float[] worldVertices = worldVerticesBuffer; // 顶点世界坐标, 连续的 [x0, y0, x1, y1, ...] 坐标值
|
||||||
|
int worldVerticesCount; // 等于顶点数组的长度除以 2
|
||||||
|
int[] worldTriangleIndices; // 三角形索引, 从顶点坐标数组取的时候要乘以 2, 最大值是 worldVerticesCount - 1
|
||||||
|
int worldTriangleIndicesLength; // 三角形索引数组长度
|
||||||
|
float[] uvs; // 纹理坐标
|
||||||
|
float tintR = skeleton.R * slot.R;
|
||||||
|
float tintG = skeleton.G * slot.G;
|
||||||
|
float tintB = skeleton.B * slot.B;
|
||||||
|
float tintA = skeleton.A * slot.A;
|
||||||
|
|
||||||
|
if (attachment is RegionAttachment regionAttachment)
|
||||||
|
{
|
||||||
|
texture = (SFML.Graphics.Texture)((AtlasRegion)regionAttachment.RendererObject).page.rendererObject;
|
||||||
|
|
||||||
|
regionAttachment.ComputeWorldVertices(slot.Bone, worldVertices, 0);
|
||||||
|
worldVerticesCount = 4;
|
||||||
|
worldTriangleIndices = [0, 1, 2, 2, 3, 0];
|
||||||
|
worldTriangleIndicesLength = 6;
|
||||||
|
uvs = regionAttachment.UVs;
|
||||||
|
tintR *= regionAttachment.R;
|
||||||
|
tintG *= regionAttachment.G;
|
||||||
|
tintB *= regionAttachment.B;
|
||||||
|
tintA *= regionAttachment.A;
|
||||||
|
}
|
||||||
|
else if (attachment is MeshAttachment meshAttachment)
|
||||||
|
{
|
||||||
|
texture = (SFML.Graphics.Texture)((AtlasRegion)meshAttachment.RendererObject).page.rendererObject;
|
||||||
|
|
||||||
|
if (meshAttachment.WorldVerticesLength > worldVertices.Length)
|
||||||
|
worldVertices = worldVerticesBuffer = new float[meshAttachment.WorldVerticesLength * 2];
|
||||||
|
meshAttachment.ComputeWorldVertices(slot, worldVertices);
|
||||||
|
worldVerticesCount = meshAttachment.WorldVerticesLength / 2;
|
||||||
|
worldTriangleIndices = meshAttachment.Triangles;
|
||||||
|
worldTriangleIndicesLength = meshAttachment.Triangles.Length;
|
||||||
|
uvs = meshAttachment.UVs;
|
||||||
|
tintR *= meshAttachment.R;
|
||||||
|
tintG *= meshAttachment.G;
|
||||||
|
tintB *= meshAttachment.B;
|
||||||
|
tintA *= meshAttachment.A;
|
||||||
|
}
|
||||||
|
else if (attachment is ClippingAttachment clippingAttachment)
|
||||||
|
{
|
||||||
|
clipping.ClipStart(slot, clippingAttachment);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clipping.ClipEnd(slot);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SFML.Graphics.BlendMode blendMode = GetSFMLBlendMode(slot.Data.BlendMode);
|
||||||
|
|
||||||
|
states.Texture ??= texture;
|
||||||
|
if (states.BlendMode != blendMode || states.Texture != texture)
|
||||||
|
{
|
||||||
|
if (vertexArray.VertexCount > 0)
|
||||||
|
{
|
||||||
|
// XXX: 实测不用设置 sampler2D 的值也正确
|
||||||
|
if (UsePremultipliedAlpha && (states.BlendMode == BlendMode.Normal || states.BlendMode == BlendMode.Additive))
|
||||||
|
states.Shader = FragmentShader;
|
||||||
|
else
|
||||||
|
states.Shader = null;
|
||||||
|
target.Draw(vertexArray, states);
|
||||||
|
vertexArray.Clear();
|
||||||
|
}
|
||||||
|
states.BlendMode = blendMode;
|
||||||
|
states.Texture = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clipping.IsClipping)
|
||||||
|
{
|
||||||
|
// 这里必须单独记录 Count, 和 Items 的 Length 是不一致的
|
||||||
|
clipping.ClipTriangles(worldVertices, worldVerticesCount * 2, worldTriangleIndices, worldTriangleIndicesLength, uvs);
|
||||||
|
worldVertices = clipping.ClippedVertices.Items;
|
||||||
|
worldVerticesCount = clipping.ClippedVertices.Count / 2;
|
||||||
|
worldTriangleIndices = clipping.ClippedTriangles.Items;
|
||||||
|
worldTriangleIndicesLength = clipping.ClippedTriangles.Count;
|
||||||
|
uvs = clipping.ClippedUVs.Items;
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureSizeX = texture.Size.X;
|
||||||
|
var textureSizeY = texture.Size.Y;
|
||||||
|
|
||||||
|
SFML.Graphics.Vertex vertex = new();
|
||||||
|
vertex.Color.R = (byte)(tintR * 255);
|
||||||
|
vertex.Color.G = (byte)(tintG * 255);
|
||||||
|
vertex.Color.B = (byte)(tintB * 255);
|
||||||
|
vertex.Color.A = (byte)(tintA * 255);
|
||||||
|
|
||||||
|
// 必须用 worldTriangleIndicesLength 不能直接 foreach
|
||||||
|
for (int i = 0; i < worldTriangleIndicesLength; i++)
|
||||||
|
{
|
||||||
|
var index = worldTriangleIndices[i] * 2;
|
||||||
|
vertex.Position.X = worldVertices[index];
|
||||||
|
vertex.Position.Y = worldVertices[index + 1];
|
||||||
|
vertex.TexCoords.X = uvs[index] * textureSizeX;
|
||||||
|
vertex.TexCoords.Y = uvs[index + 1] * textureSizeY;
|
||||||
|
vertexArray.Append(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
clipping.ClipEnd(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UsePremultipliedAlpha && (states.BlendMode == BlendMode.Normal || states.BlendMode == BlendMode.Additive))
|
||||||
|
states.Shader = FragmentShader;
|
||||||
|
else
|
||||||
|
states.Shader = null;
|
||||||
|
target.Draw(vertexArray, states);
|
||||||
|
clipping.ClipEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
115
SpineViewer/src/Spine/Manager.cs
Normal file
115
SpineViewer/src/Spine/Manager.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SpineViewer.Spine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 骨骼列表管理器
|
||||||
|
/// </summary>
|
||||||
|
class Manager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 骨骼列表
|
||||||
|
/// </summary>
|
||||||
|
public ReadOnlyCollection<Spine> Spines { get => spines.AsReadOnly(); }
|
||||||
|
private readonly List<Spine> spines = [];
|
||||||
|
|
||||||
|
private readonly ListView listView;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 骨骼管理器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listView">用于显示骨骼信息的列表</param>
|
||||||
|
public Manager(ListView listView)
|
||||||
|
{
|
||||||
|
listView.BeginUpdate();
|
||||||
|
listView.Columns.Clear();
|
||||||
|
listView.Items.Clear();
|
||||||
|
listView.Columns.AddRange([
|
||||||
|
new() { Text = "名称", Width = 150},
|
||||||
|
new() { Text = "版本", Width = 150 }
|
||||||
|
]);
|
||||||
|
listView.EndUpdate();
|
||||||
|
this.listView = listView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在末尾添加一个
|
||||||
|
/// </summary>
|
||||||
|
public void Add(Spine spine)
|
||||||
|
{
|
||||||
|
spines.Add(spine);
|
||||||
|
listView.Items.Add(new ListViewItem([
|
||||||
|
Path.GetFileNameWithoutExtension(spine.SkelPath),
|
||||||
|
spine.Version.String()
|
||||||
|
], -1)
|
||||||
|
{ ToolTipText = spine.SkelPath });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定下标之前添加一个
|
||||||
|
/// </summary>
|
||||||
|
public void Insert(int index, Spine spine)
|
||||||
|
{
|
||||||
|
spines.Insert(index, spine);
|
||||||
|
listView.Items.Insert(index, new ListViewItem([
|
||||||
|
Path.GetFileNameWithoutExtension(spine.SkelPath),
|
||||||
|
spine.Version.String()
|
||||||
|
], -1)
|
||||||
|
{ ToolTipText = spine.SkelPath });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 批量移除
|
||||||
|
/// </summary>
|
||||||
|
public void Remove(IEnumerable<int> indices)
|
||||||
|
{
|
||||||
|
foreach (var i in indices.OrderByDescending(x => x))
|
||||||
|
{
|
||||||
|
spines.RemoveAt(i);
|
||||||
|
listView.Items.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 指定下标元素前移一位
|
||||||
|
/// </summary>
|
||||||
|
public void MoveUp(int index)
|
||||||
|
{
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
(spines[index - 1], spines[index]) = (spines[index], spines[index - 1]);
|
||||||
|
var item = listView.Items[index];
|
||||||
|
listView.Items.RemoveAt(index);
|
||||||
|
listView.Items.Insert(index - 1, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 指定下标元素后移一位
|
||||||
|
/// </summary>
|
||||||
|
public void MoveDown(int index)
|
||||||
|
{
|
||||||
|
if (index < spines.Count - 1)
|
||||||
|
{
|
||||||
|
(spines[index], spines[index + 1]) = (spines[index + 1], spines[index]);
|
||||||
|
var item = listView.Items[index + 1];
|
||||||
|
listView.Items.RemoveAt(index + 1);
|
||||||
|
listView.Items.Insert(index, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 全部移除
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
spines.Clear();
|
||||||
|
listView.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
SpineViewer/src/Spine/Previewer.cs
Normal file
21
SpineViewer/src/Spine/Previewer.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SpineViewer.Spine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 骨骼预览器
|
||||||
|
/// </summary>
|
||||||
|
class Previewer
|
||||||
|
{
|
||||||
|
private readonly Panel panel;
|
||||||
|
|
||||||
|
public Previewer(Panel panel)
|
||||||
|
{
|
||||||
|
this.panel = panel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
240
SpineViewer/src/Spine/Spine.cs
Normal file
240
SpineViewer/src/Spine/Spine.cs
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using SFML.System;
|
||||||
|
using SFML.Window;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace SpineViewer.Spine
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spine 实现类标记
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||||
|
public class SpineImplementationAttribute : Attribute
|
||||||
|
{
|
||||||
|
public Version Version { get; }
|
||||||
|
|
||||||
|
public SpineImplementationAttribute(Version version)
|
||||||
|
{
|
||||||
|
Version = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spine 基类, 使用静态方法 New 来创建具体版本对象
|
||||||
|
/// </summary>
|
||||||
|
public abstract class Spine : SFML.Graphics.Drawable, IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 实现类缓存
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Dictionary<Version, Type> ImplementationTypes = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于解决 PMA 和渐变动画问题的片段着色器
|
||||||
|
/// </summary>
|
||||||
|
private const string FRAGMENT_SHADER = (
|
||||||
|
"uniform sampler2D t;" +
|
||||||
|
"void main() { vec4 p = texture2D(t, gl_TexCoord[0].xy);" +
|
||||||
|
"if (p.a > 0) p.rgb /= max(max(max(p.r, p.g), p.b), p.a);" +
|
||||||
|
"gl_FragColor = gl_Color * p; }"
|
||||||
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用于解决 PMA 和渐变动画问题的片段着色器
|
||||||
|
/// </summary>
|
||||||
|
protected static readonly SFML.Graphics.Shader? FragmentShader = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静态构造函数
|
||||||
|
/// </summary>
|
||||||
|
static Spine()
|
||||||
|
{
|
||||||
|
// 遍历并缓存标记了 SpineImplementationAttribute 的类型
|
||||||
|
var impTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => typeof(Spine).IsAssignableFrom(t) && !t.IsAbstract);
|
||||||
|
foreach (var type in impTypes)
|
||||||
|
{
|
||||||
|
var attr = type.GetCustomAttribute<SpineImplementationAttribute>();
|
||||||
|
if (attr is not null)
|
||||||
|
{
|
||||||
|
ImplementationTypes[attr.Version] = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Program.Logger.Debug($"Find Spine implementations: [{string.Join(", ", ImplementationTypes.Keys)}]");
|
||||||
|
|
||||||
|
// 加载 FragmentShader
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FragmentShader = SFML.Graphics.Shader.FromString(null, null, FRAGMENT_SHADER);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Program.Logger.Error(ex.ToString());
|
||||||
|
Program.Logger.Error("Failed to load fragment shader");
|
||||||
|
FragmentShader = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建特定版本的 Spine
|
||||||
|
/// </summary>
|
||||||
|
public static Spine New(Version version, string skelPath, string? atlasPath = null)
|
||||||
|
{
|
||||||
|
if (!ImplementationTypes.TryGetValue(version, out var spineType))
|
||||||
|
{
|
||||||
|
throw new NotImplementedException($"Not implemented version: {version}");
|
||||||
|
}
|
||||||
|
return (Spine)Activator.CreateInstance(spineType, skelPath, atlasPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public Spine(string skelPath, string? atlasPath = null)
|
||||||
|
{
|
||||||
|
// 获取子类类型
|
||||||
|
var type = GetType();
|
||||||
|
var attr = type.GetCustomAttribute<SpineImplementationAttribute>();
|
||||||
|
if (attr is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Class {type.Name} has no SpineImplementationAttribute.");
|
||||||
|
}
|
||||||
|
|
||||||
|
atlasPath ??= Path.ChangeExtension(skelPath, ".atlas");
|
||||||
|
|
||||||
|
// 设置 Version
|
||||||
|
Version = attr.Version;
|
||||||
|
SkelPath = Path.GetFullPath(skelPath);
|
||||||
|
AtlasPath = Path.GetFullPath(atlasPath);
|
||||||
|
Name = Path.GetFileNameWithoutExtension(skelPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Spine() { Dispose(false); }
|
||||||
|
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
|
||||||
|
protected virtual void Dispose(bool disposing) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 缩放最小值
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(false)]
|
||||||
|
public const float SCALE_MIN = 0.001f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所属版本
|
||||||
|
/// </summary>
|
||||||
|
[TypeConverter(typeof(VersionTypeConverter))]
|
||||||
|
[Browsable(true), Category("基本信息"), DisplayName("运行时版本")]
|
||||||
|
public Version Version { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// skel 文件完整路径
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(true), Category("基本信息"), DisplayName("skel文件路径")]
|
||||||
|
public string SkelPath { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// atlas 文件完整路径
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(true), Category("基本信息"), DisplayName("atlas文件路径")]
|
||||||
|
public string AtlasPath { get; }
|
||||||
|
|
||||||
|
[Browsable(true), Category("基本信息"), DisplayName("名称")]
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 缩放比例
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(true), Category("空间变换"), DisplayName("缩放比例")]
|
||||||
|
public abstract float Scale { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 位置
|
||||||
|
/// </summary>
|
||||||
|
[TypeConverter(typeof(PointFTypeConverter))]
|
||||||
|
[Browsable(true), Category("空间变换"), DisplayName("位置")]
|
||||||
|
public abstract PointF Position { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 水平翻转
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(true), Category("空间变换"), DisplayName("水平翻转")]
|
||||||
|
public abstract bool FlipX { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 垂直翻转
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(true), Category("空间变换"), DisplayName("垂直翻转")]
|
||||||
|
public abstract bool FlipY { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否使用预乘Alpha
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(true), Category("其他"), DisplayName("预乘Alpha通道")]
|
||||||
|
public bool UsePremultipliedAlpha { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包含的所有动画名称
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(false)]
|
||||||
|
public ReadOnlyCollection<string> AnimationNames { get => animationNames.AsReadOnly(); }
|
||||||
|
protected List<string> animationNames = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认动画名称
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(false)]
|
||||||
|
public string DefaultAnimationName { get => animationNames.Last(); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前动画名称
|
||||||
|
/// </summary>
|
||||||
|
[TypeConverter(typeof(AnimationTypeConverter))]
|
||||||
|
[Browsable(true), Category("其他"), DisplayName("当前播放动画"), PropertyTab()]
|
||||||
|
public abstract string CurrentAnimation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 骨骼包围盒
|
||||||
|
/// </summary>
|
||||||
|
[Browsable(false)]
|
||||||
|
public abstract RectangleF Bounds { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取动画时长, 如果动画不存在则返回 0
|
||||||
|
/// </summary>
|
||||||
|
public abstract float GetAnimationDuration(string name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新内部状态
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="delta">时间间隔</param>
|
||||||
|
public abstract void Update(float delta);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 顶点坐标缓冲区
|
||||||
|
/// </summary>
|
||||||
|
protected float[] worldVerticesBuffer = new float[1024];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 顶点缓冲区
|
||||||
|
/// </summary>
|
||||||
|
protected SFML.Graphics.VertexArray vertexArray = new(SFML.Graphics.PrimitiveType.Triangles);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SFML.Graphics.Drawable 接口实现
|
||||||
|
/// </summary>
|
||||||
|
public abstract void Draw(SFML.Graphics.RenderTarget target, SFML.Graphics.RenderStates states);
|
||||||
|
}
|
||||||
|
}
|
||||||
97
SpineViewer/src/Spine/TypeConverter.cs
Normal file
97
SpineViewer/src/Spine/TypeConverter.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SpineViewer.Spine
|
||||||
|
{
|
||||||
|
public class VersionTypeConverter : EnumConverter
|
||||||
|
{
|
||||||
|
public VersionTypeConverter() : base(typeof(Version)) { }
|
||||||
|
|
||||||
|
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type? destinationType)
|
||||||
|
{
|
||||||
|
if (destinationType == typeof(string) && value is Version version)
|
||||||
|
{
|
||||||
|
// 调用自定义的 String() 方法
|
||||||
|
return version.String();
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.ConvertTo(context, culture, value, destinationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PointFTypeConverter : ExpandableObjectConverter
|
||||||
|
{
|
||||||
|
public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType)
|
||||||
|
{
|
||||||
|
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
|
||||||
|
{
|
||||||
|
if (destinationType == typeof(string) && value is PointF point)
|
||||||
|
{
|
||||||
|
return $"{point.X}, {point.Y}";
|
||||||
|
}
|
||||||
|
return base.ConvertTo(context, culture, value, destinationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
|
||||||
|
{
|
||||||
|
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||||
|
{
|
||||||
|
if (value is string str)
|
||||||
|
{
|
||||||
|
var parts = str.Split(',');
|
||||||
|
if (parts.Length == 2 &&
|
||||||
|
float.TryParse(parts[0], out var x) &&
|
||||||
|
float.TryParse(parts[1], out var y))
|
||||||
|
{
|
||||||
|
return new PointF(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base.ConvertFrom(context, culture, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
|
||||||
|
{
|
||||||
|
return TypeDescriptor.GetProperties(typeof(PointF), attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AnimationTypeConverter : StringConverter
|
||||||
|
{
|
||||||
|
public override bool GetStandardValuesSupported(ITypeDescriptorContext? context)
|
||||||
|
{
|
||||||
|
// 支持标准值列表
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context)
|
||||||
|
{
|
||||||
|
// 排他模式,只有下拉列表中的值可选
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
|
||||||
|
{
|
||||||
|
if (context?.Instance is Spine obj)
|
||||||
|
{
|
||||||
|
// 返回 AnimationNames 作为下拉选项
|
||||||
|
return new StandardValuesCollection(obj.AnimationNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.GetStandardValues(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
SpineViewer/src/Spine/Version.cs
Normal file
51
SpineViewer/src/Spine/Version.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SpineViewer.Spine
|
||||||
|
{
|
||||||
|
public static class VersionHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 描述缓存
|
||||||
|
/// </summary>
|
||||||
|
public static readonly Dictionary<Version, string> Versions = [];
|
||||||
|
|
||||||
|
static VersionHelper()
|
||||||
|
{
|
||||||
|
// 初始化缓存
|
||||||
|
foreach (var value in Enum.GetValues(typeof(Version)))
|
||||||
|
{
|
||||||
|
var field = typeof(Version).GetField(value.ToString());
|
||||||
|
var attribute = field?.GetCustomAttribute<DescriptionAttribute>();
|
||||||
|
Versions[(Version)value] = attribute?.Description ?? value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 版本号字符串
|
||||||
|
/// </summary>
|
||||||
|
public static string String(this Version version)
|
||||||
|
{
|
||||||
|
return Versions.TryGetValue(version, out var description) ? description : version.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 支持的 Spine 版本
|
||||||
|
/// </summary>
|
||||||
|
public enum Version
|
||||||
|
{
|
||||||
|
[Description("v3.6.x")] V36 = 0x0306,
|
||||||
|
[Description("v3.7.x")] V37 = 0x0307,
|
||||||
|
[Description("v3.8.x")] V38 = 0x0308,
|
||||||
|
[Description("v3.9.x")] V39 = 0x0309,
|
||||||
|
[Description("v4.0.x")] V40 = 0x0400,
|
||||||
|
[Description("v4.1.x")] V41 = 0x0401,
|
||||||
|
[Description("v4.2.x")] V42 = 0x0402
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user