支持spine进行绘制
This commit is contained in:
1 parent
266c353fd2
commit
e89eeaa502
19 files changed
+311
-19
No files matched your search
@@ -1,15 +1,36 @@
|
||||
import { Container, Matrix } from 'pixi.js'
|
||||
import { Container, Graphics, Matrix } from 'pixi.js'
|
||||
import { getSpineAsset } from './spineAssetRegistry'
|
||||
import type { SpineSceneObject } from './spineTypes'
|
||||
|
||||
interface RuntimeEntry {
|
||||
root: Container
|
||||
spine: any
|
||||
skeletonDebug: Graphics
|
||||
skinMeshDebug: Graphics
|
||||
runtimeVersion: '4.2' | '4.3'
|
||||
assetVersion: number
|
||||
animation: string
|
||||
}
|
||||
|
||||
function attachmentDebugColor(key: string) {
|
||||
let hash = 2166136261
|
||||
for (let i = 0; i < key.length; i++) hash = Math.imul(hash ^ key.charCodeAt(i), 16777619)
|
||||
const hue = (hash >>> 0) % 360
|
||||
const segment = hue / 60
|
||||
const chroma = 0.78
|
||||
const x = chroma * (1 - Math.abs(segment % 2 - 1))
|
||||
const offset = 1 - chroma
|
||||
const [red, green, blue] = segment < 1 ? [chroma, x, 0]
|
||||
: segment < 2 ? [x, chroma, 0]
|
||||
: segment < 3 ? [0, chroma, x]
|
||||
: segment < 4 ? [0, x, chroma]
|
||||
: segment < 5 ? [x, 0, chroma]
|
||||
: [chroma, 0, x]
|
||||
return (Math.round((red + offset) * 255) << 16)
|
||||
| (Math.round((green + offset) * 255) << 8)
|
||||
| Math.round((blue + offset) * 255)
|
||||
}
|
||||
|
||||
export interface SpineBoneWorldTransform {
|
||||
x: number
|
||||
y: number
|
||||
@@ -91,6 +112,90 @@ export class SpineRuntimeLayer {
|
||||
entry.root.destroy({ children: true })
|
||||
}
|
||||
|
||||
private drawDebug(entry: RuntimeEntry, object: SpineSceneObject) {
|
||||
const { skeletonDebug, skinMeshDebug, root, spine } = entry
|
||||
skeletonDebug.clear()
|
||||
skinMeshDebug.clear()
|
||||
skeletonDebug.visible = object.showBones
|
||||
skinMeshDebug.visible = object.showOnlySkinMesh
|
||||
|| object.showRegionAttachments
|
||||
|| object.showClippingAttachments
|
||||
if (!object.showBones && !skinMeshDebug.visible) return
|
||||
|
||||
const screenScale = Math.max(
|
||||
0.0001,
|
||||
Math.abs(root.scale.x * (this.container.parent?.scale.x ?? 1)),
|
||||
Math.abs(root.scale.y * (this.container.parent?.scale.y ?? 1)),
|
||||
)
|
||||
const lineWidth = 1 / screenScale
|
||||
const skeleton = spine.skeleton
|
||||
const offsetX = Number(skeleton.x) || 0
|
||||
const offsetY = Number(skeleton.y) || 0
|
||||
|
||||
if (object.showBones) {
|
||||
skeletonDebug.lineStyle(lineWidth, 0x55d6ff, 0.95)
|
||||
for (const bone of skeleton.bones) {
|
||||
if (!bone.active || !bone.parent?.active) continue
|
||||
skeletonDebug.moveTo(offsetX + bone.parent.worldX, offsetY + bone.parent.worldY)
|
||||
skeletonDebug.lineTo(offsetX + bone.worldX, offsetY + bone.worldY)
|
||||
}
|
||||
skeletonDebug.beginFill(0xb8efff, 0.95)
|
||||
for (const bone of skeleton.bones) {
|
||||
if (bone.active) skeletonDebug.drawCircle(offsetX + bone.worldX, offsetY + bone.worldY, 2 / screenScale)
|
||||
}
|
||||
skeletonDebug.endFill()
|
||||
}
|
||||
|
||||
if (!object.showOnlySkinMesh && !object.showRegionAttachments && !object.showClippingAttachments) return
|
||||
const activeSlots = skeleton.slots.filter((slot: any) => slot.bone?.active)
|
||||
for (const slot of activeSlots) {
|
||||
const attachment = slot.getAttachment()
|
||||
if (!attachment) continue
|
||||
const color = attachmentDebugColor(`${slot.data.index}:${attachment.name}`)
|
||||
const isMesh = Array.isArray(attachment.triangles) && attachment.worldVerticesLength > 0
|
||||
if (isMesh) {
|
||||
if (!object.showOnlySkinMesh) continue
|
||||
skinMeshDebug.lineStyle(lineWidth, color, 0.9)
|
||||
skinMeshDebug.beginFill(color, 0.18)
|
||||
const vertices = new Float32Array(attachment.worldVerticesLength)
|
||||
attachment.computeWorldVertices(slot, 0, attachment.worldVerticesLength, vertices, 0, 2)
|
||||
for (let i = 0; i < attachment.triangles.length; i += 3) {
|
||||
const a = attachment.triangles[i] * 2
|
||||
const b = attachment.triangles[i + 1] * 2
|
||||
const c = attachment.triangles[i + 2] * 2
|
||||
skinMeshDebug.drawPolygon([
|
||||
vertices[a], vertices[a + 1],
|
||||
vertices[b], vertices[b + 1],
|
||||
vertices[c], vertices[c + 1],
|
||||
])
|
||||
}
|
||||
skinMeshDebug.endFill()
|
||||
continue
|
||||
}
|
||||
|
||||
if (!object.showRegionAttachments
|
||||
|| !attachment.offset
|
||||
|| !Number.isFinite(attachment.width)
|
||||
|| !Number.isFinite(attachment.height)
|
||||
|| typeof attachment.computeWorldVertices !== 'function') {
|
||||
if (object.showClippingAttachments
|
||||
&& attachment.endSlot !== undefined
|
||||
&& attachment.worldVerticesLength > 0
|
||||
&& typeof attachment.computeWorldVertices === 'function') {
|
||||
const clippingVertices = new Float32Array(attachment.worldVerticesLength)
|
||||
attachment.computeWorldVertices(slot, 0, attachment.worldVerticesLength, clippingVertices, 0, 2)
|
||||
skinMeshDebug.lineStyle(lineWidth, color, 0.9)
|
||||
skinMeshDebug.drawPolygon(Array.from(clippingVertices))
|
||||
}
|
||||
continue
|
||||
}
|
||||
const regionVertices = new Float32Array(8)
|
||||
attachment.computeWorldVertices(slot, regionVertices, 0, 2)
|
||||
skinMeshDebug.lineStyle(lineWidth, color, 0.9)
|
||||
skinMeshDebug.drawPolygon(Array.from(regionVertices))
|
||||
}
|
||||
}
|
||||
|
||||
sync(objects: SpineSceneObject[], frame: number, fps: number) {
|
||||
for (const [id, entry] of this.entries) {
|
||||
if (objects.some((object) => object.id === id)) continue
|
||||
@@ -110,12 +215,18 @@ export class SpineRuntimeLayer {
|
||||
if (entry) this.destroyEntry(entry)
|
||||
const root = new Container()
|
||||
const spine = new asset.SpineClass({ skeletonData: asset.skeletonData, autoUpdate: false, darkTint: false })
|
||||
const skeletonDebug = new Graphics()
|
||||
const skinMeshDebug = new Graphics()
|
||||
skeletonDebug.eventMode = 'none'
|
||||
skinMeshDebug.eventMode = 'none'
|
||||
skeletonDebug.visible = false
|
||||
skinMeshDebug.visible = false
|
||||
if (asset.skeletonData.defaultSkin) spine.skeleton.setSkin(asset.skeletonData.defaultSkin)
|
||||
if (asset.runtimeVersion === '4.3') spine.skeleton.setupPoseSlots()
|
||||
else spine.skeleton.setSlotsToSetupPose()
|
||||
root.addChild(spine)
|
||||
root.addChild(spine, skeletonDebug, skinMeshDebug)
|
||||
this.container.addChild(root)
|
||||
entry = { root, spine, runtimeVersion: asset.runtimeVersion, assetVersion: object.assetVersion, animation: '' }
|
||||
entry = { root, spine, skeletonDebug, skinMeshDebug, runtimeVersion: asset.runtimeVersion, assetVersion: object.assetVersion, animation: '' }
|
||||
this.entries.set(object.id, entry)
|
||||
}
|
||||
if (!entry) continue
|
||||
@@ -144,6 +255,7 @@ export class SpineRuntimeLayer {
|
||||
track.trackTime = duration > 0 ? Math.min(localTime, duration) : localTime
|
||||
}
|
||||
spine.update(0)
|
||||
this.drawDebug(entry, object)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user