新增spine版本支持
This commit is contained in:
1 parent
8802bbe25e
commit
19278dac01
91 files changed
+69095
-5744
No files matched your search
@@ -2,6 +2,7 @@ import type { EmitterConfig, ParticleImageResource, ParticleState, TrailImageRes
|
||||
import type { ParticleSystem, TimelineState } from '../store/particleStore'
|
||||
import type { SpineExportSettings } from './spineExportSettings'
|
||||
import type { SpineExportResult, SpineExportWarning, SpineJsonDocument, SpineJsonMap } from './spineJsonTypes'
|
||||
import { downgradeSpineJsonTo38 } from './spine38JsonExporter'
|
||||
import { reduceBezierSamples, reduceNumericSamples, roundSpine, unwrapDegrees, type NumericSample } from './spineKeyframeReducer'
|
||||
import { buildWeightedTrailMesh } from './spineMeshBuilder'
|
||||
|
||||
@@ -111,7 +112,7 @@ function resolvedTimelineCurve(mode: '跟随导出' | '线性' | '贝塞尔' | u
|
||||
|
||||
function numericTimeline(samples: NumericSample[], fps: number, names: string[], tolerances: number[], settings: SpineExportSettings, forcedFrames: Set<number> = new Set(), curveMode: TimelineCurveMode = settings.keyframeCurve) {
|
||||
if (!samples.length) return []
|
||||
const fitted = curveMode === 'bezier'
|
||||
const fitted = curveMode === 'bezier' && settings.spineVersion !== '3.8'
|
||||
? reduceBezierSamples(samples, tolerances, forcedFrames)
|
||||
: { samples: reduceNumericSamples(samples, tolerances, forcedFrames), controls: [] }
|
||||
const keys = fitted.samples.map((sample) => {
|
||||
@@ -212,7 +213,7 @@ function rgbaTimeline(colors: number[][], attachments: Array<string | null>, fps
|
||||
// RGB 保持单通道 1/255,透明度使用 0.01 的视觉容差。
|
||||
const samples: NumericSample[] = colors.map((values, frame) => ({ frame, values }))
|
||||
const rgbTolerance = 1 / 255
|
||||
const fitted = curveMode === 'bezier'
|
||||
const fitted = curveMode === 'bezier' && settings.spineVersion !== '3.8'
|
||||
? reduceBezierSamples(samples, [rgbTolerance, rgbTolerance, rgbTolerance, 0.01], forcedFrames)
|
||||
: { samples: reduceNumericSamples(samples, [rgbTolerance, rgbTolerance, rgbTolerance, 0.01], forcedFrames), controls: [] }
|
||||
const keys = fitted.samples.map((sample) => ({ time: time(sample.frame, fps), color: `${byte(sample.values[0])}${byte(sample.values[1])}${byte(sample.values[2])}${byte(sample.values[3])}` } as SpineJsonMap))
|
||||
@@ -232,7 +233,9 @@ function rgbaTimeline(colors: number[][], attachments: Array<string | null>, fps
|
||||
|
||||
function addSlotAnimation(animation: SpineJsonMap, slotName: string, attachments: Array<string | null>, colors: number[][], fps: number, settings: SpineExportSettings, curveMode: TimelineCurveMode = settings.keyframeCurve) {
|
||||
const slot = animation.slots[slotName] ||= {}
|
||||
const attachment = discreteTimeline(attachments, fps, (name, frame) => name == null ? { time: time(frame, fps) } : { time: time(frame, fps), name })
|
||||
// Spine 3.8 编辑器要求附件时间轴的每一帧都存在 name;隐藏附件必须显式写 null,
|
||||
// 省略字段会在导入时触发 “Named value not found: name”。4.x 同样接受该标准写法。
|
||||
const attachment = discreteTimeline(attachments, fps, (name, frame) => ({ time: time(frame, fps), name: name ?? null }))
|
||||
if (attachment.length) slot.attachment = attachment
|
||||
const rgba = rgbaTimeline(colors, attachments, fps, settings, curveMode)
|
||||
if (rgba.length) slot.rgba = rgba
|
||||
@@ -247,7 +250,8 @@ export function buildSpineJson(input: ExportInput): SpineExportResult {
|
||||
const warnings: SpineExportWarning[] = []
|
||||
const visibleSystems = input.systems.filter((system) => system.visible !== false)
|
||||
const document: SpineJsonDocument = {
|
||||
skeleton: { hash: '', spine: '4.2', x: 0, y: 0, width: 0, height: 0, images: settings.imagesPath || './images/' },
|
||||
// 先生成当前运行时使用的公共结构;3.8 会在最终序列化前降级字段与时间轴格式。
|
||||
skeleton: { hash: '', spine: settings.spineVersion, x: 0, y: 0, width: 0, height: 0, images: settings.imagesPath || './images/' },
|
||||
bones: [{ name: 'root' }], slots: [], skins: [{ name: 'default', attachments: {} }], animations: {},
|
||||
}
|
||||
if (!settings.omitFps) document.skeleton.fps = targetFps
|
||||
@@ -426,8 +430,9 @@ export function buildSpineJson(input: ExportInput): SpineExportResult {
|
||||
}
|
||||
|
||||
if (!Object.keys(document.animations).length && !settings.excludeEmptyAnimations) document.animations.animation = { bones: {}, slots: {} }
|
||||
const text = JSON.stringify(document, null, 2)
|
||||
return { json: document, text, warnings, particleSystemCount: visibleSystems.length, animationCount: Object.keys(document.animations).length, boneCount: document.bones.length }
|
||||
const output = settings.spineVersion === '3.8' ? downgradeSpineJsonTo38(document) : document
|
||||
const text = JSON.stringify(output, null, 2)
|
||||
return { json: output, text, warnings, particleSystemCount: visibleSystems.length, animationCount: Object.keys(output.animations).length, boneCount: output.bones.length }
|
||||
}
|
||||
|
||||
export function downloadSpineJson(result: SpineExportResult, fileName: string) {
|
||||
|
||||
Reference in new issue
Block a user