mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-05 02:06:53 +08:00
Refactored rendering and matrix into specific class defined in c3 lib.
This commit is contained in:
parent
86a0950a5f
commit
683574fb41
26
spine-ts/spine-construct3/LICENSE
Normal file
26
spine-ts/spine-construct3/LICENSE
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
Spine Runtimes License Agreement
|
||||||
|
Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
|
||||||
|
Copyright (c) 2013-2025, Esoteric Software LLC
|
||||||
|
|
||||||
|
Integration of the Spine Runtimes into software or otherwise creating
|
||||||
|
derivative works of the Spine Runtimes is permitted under the terms and
|
||||||
|
conditions of Section 2 of the Spine Editor License Agreement:
|
||||||
|
http://esotericsoftware.com/spine-editor-license
|
||||||
|
|
||||||
|
Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||||
|
or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||||
|
"Products"), provided that each user of the Products must obtain their own
|
||||||
|
Spine Editor license and redistribution of the Products in any form must
|
||||||
|
include this license and copyright notice.
|
||||||
|
|
||||||
|
THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||||
|
BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
3
spine-ts/spine-construct3/README.md
Normal file
3
spine-ts/spine-construct3/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# spine-ts Construct3
|
||||||
|
|
||||||
|
Please see the top-level [README.md](../README.md) for more information.
|
||||||
@ -1,3 +1,3 @@
|
|||||||
# spine-ts THREE.JS
|
# spine-ts Construct3 Lib
|
||||||
|
|
||||||
Please see the top-level [README.md](../README.md) for more information.
|
This is just an internal lib that contains spine-core and additional basic functions for C3.
|
||||||
@ -28,7 +28,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { AtlasAttachmentLoader, SkeletonBinary, type SkeletonData, SkeletonJson, TextureAtlas, TextureAtlasPage } from "@esotericsoftware/spine-core";
|
import { AtlasAttachmentLoader, SkeletonBinary, type SkeletonData, SkeletonJson, TextureAtlas, TextureAtlasPage } from "@esotericsoftware/spine-core";
|
||||||
import { C3Texture, C3TextureEditor } from "./C3Texture";
|
import { C3TextureEditor, C3TextureRuntime } from "./C3Texture";
|
||||||
|
|
||||||
|
|
||||||
interface CacheEntry<T> {
|
interface CacheEntry<T> {
|
||||||
@ -40,7 +40,7 @@ export class AssetLoader {
|
|||||||
|
|
||||||
private static CacheSkeleton = new Map<string, CacheEntry<SkeletonData>>();
|
private static CacheSkeleton = new Map<string, CacheEntry<SkeletonData>>();
|
||||||
private static CacheAtlas = new Map<string, CacheEntry<TextureAtlas>>();
|
private static CacheAtlas = new Map<string, CacheEntry<TextureAtlas>>();
|
||||||
private static CacheTexture = new Map<string, CacheEntry<C3Texture>>();
|
private static CacheTexture = new Map<string, CacheEntry<C3TextureRuntime>>();
|
||||||
|
|
||||||
public async loadSkeletonEditor (sid: number, textureAtlas: TextureAtlas, scale = 1, instance: SDK.IWorldInstance) {
|
public async loadSkeletonEditor (sid: number, textureAtlas: TextureAtlas, scale = 1, instance: SDK.IWorldInstance) {
|
||||||
const projectFile = instance.GetProject().GetProjectFileBySID(sid);
|
const projectFile = instance.GetProject().GetProjectFileBySID(sid);
|
||||||
@ -167,7 +167,7 @@ export class AssetLoader {
|
|||||||
const image = await AssetLoader.createImageBitmapFromBlob(content, page.pma);
|
const image = await AssetLoader.createImageBitmapFromBlob(content, page.pma);
|
||||||
if (!image) return null;
|
if (!image) return null;
|
||||||
|
|
||||||
const spineTexture = new C3Texture(image, renderer, page);
|
const spineTexture = new C3TextureRuntime(image, renderer, page);
|
||||||
|
|
||||||
this.addToCache(AssetLoader.CacheTexture, cacheKey, spineTexture);
|
this.addToCache(AssetLoader.CacheTexture, cacheKey, spineTexture);
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,83 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2025, Esoteric Software LLC
|
||||||
|
*
|
||||||
|
* Integration of the Spine Runtimes into software or otherwise creating
|
||||||
|
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||||
|
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||||
|
* http://esotericsoftware.com/spine-editor-license
|
||||||
|
*
|
||||||
|
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||||
|
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||||
|
* "Products"), provided that each user of the Products must obtain their own
|
||||||
|
* Spine Editor license and redistribution of the Products in any form must
|
||||||
|
* include this license and copyright notice.
|
||||||
|
*
|
||||||
|
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||||
|
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import { type Bone, Vector2 } from "@esotericsoftware/spine-core";
|
||||||
|
|
||||||
|
export class C3Matrix {
|
||||||
|
|
||||||
|
public a = 0;
|
||||||
|
public b = 0;
|
||||||
|
public c = 0;
|
||||||
|
public d = 0;
|
||||||
|
public tx = 0;
|
||||||
|
public ty = 0;
|
||||||
|
|
||||||
|
private tempPoint = new Vector2();
|
||||||
|
|
||||||
|
public update (x: number, y: number, angle: number) {
|
||||||
|
const cos = Math.cos(angle);
|
||||||
|
const sin = Math.sin(angle);
|
||||||
|
this.a = cos;
|
||||||
|
this.b = sin;
|
||||||
|
this.c = -sin;
|
||||||
|
this.d = cos;
|
||||||
|
this.tx = x;
|
||||||
|
this.ty = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public gameToSkeleton (x: number, y: number) {
|
||||||
|
const tx = x - this.tx;
|
||||||
|
const ty = y - this.ty;
|
||||||
|
const { a, b, c, d, tempPoint } = this;
|
||||||
|
const delta = a * d - b * c;
|
||||||
|
tempPoint.x = (d * tx - c * ty) / delta;
|
||||||
|
tempPoint.y = (a * ty - b * tx) / delta;
|
||||||
|
return tempPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public gameToBone (x: number, y: number, bone: Bone) {
|
||||||
|
const point = this.gameToSkeleton(x, y);
|
||||||
|
if (bone.parent)
|
||||||
|
return bone.parent.applied.worldToLocal(point);
|
||||||
|
return bone.applied.worldToLocal(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
public skeletonToGame = (skeletonX: number, skeletonY: number) => {
|
||||||
|
const { a, b, c, d, tempPoint } = this;
|
||||||
|
tempPoint.x = a * skeletonX + c * skeletonY + this.tx;
|
||||||
|
tempPoint.y = b * skeletonX + d * skeletonY + this.ty;
|
||||||
|
return tempPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boneToGame (bone: Bone) {
|
||||||
|
const { applied } = bone;
|
||||||
|
return this.skeletonToGame(applied.worldX, applied.worldY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,239 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2025, Esoteric Software LLC
|
||||||
|
*
|
||||||
|
* Integration of the Spine Runtimes into software or otherwise creating
|
||||||
|
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||||
|
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||||
|
* http://esotericsoftware.com/spine-editor-license
|
||||||
|
*
|
||||||
|
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||||
|
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||||
|
* "Products"), provided that each user of the Products must obtain their own
|
||||||
|
* Spine Editor license and redistribution of the Products in any form must
|
||||||
|
* include this license and copyright notice.
|
||||||
|
*
|
||||||
|
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||||
|
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
import { type BlendMode, type Bone, MathUtils, type Skeleton, SkeletonRendererCore, Vector2 } from "@esotericsoftware/spine-core";
|
||||||
|
import type { C3Matrix } from "./C3Matrix";
|
||||||
|
import { BlendingModeSpineToC3, type C3TextureEditor, type C3TextureRuntime } from "./C3Texture";
|
||||||
|
|
||||||
|
type C3Renderer = IRenderer | SDK.Gfx.IWebGLRenderer;
|
||||||
|
type C3Texture = C3TextureRuntime | C3TextureEditor;
|
||||||
|
type C3Quad = DOMQuad | SDK.Quad;
|
||||||
|
|
||||||
|
abstract class C3SkeletonRenderer<
|
||||||
|
Renderer extends C3Renderer,
|
||||||
|
Texture extends C3Texture,
|
||||||
|
> extends SkeletonRendererCore {
|
||||||
|
|
||||||
|
private tempVertices = new Float32Array(4096);
|
||||||
|
private tempColors = new Float32Array(4096);
|
||||||
|
private tempPoint = new Vector2();
|
||||||
|
private inv255 = 1 / 255;
|
||||||
|
|
||||||
|
constructor (
|
||||||
|
protected renderer: Renderer,
|
||||||
|
private skeleton: Skeleton,
|
||||||
|
protected matrix: C3Matrix,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
draw (opacity = 1) {
|
||||||
|
const { skeleton, matrix, inv255 } = this;
|
||||||
|
|
||||||
|
let command = this.render(skeleton);
|
||||||
|
while (command) {
|
||||||
|
const { numVertices, positions, uvs, colors, indices, numIndices, blendMode } = command;
|
||||||
|
|
||||||
|
const vertices = this.tempVertices.length < numVertices * 3
|
||||||
|
? (this.tempVertices = new Float32Array(numVertices * 3))
|
||||||
|
: this.tempVertices;
|
||||||
|
|
||||||
|
const c3colors = this.tempColors.length < numVertices * 4
|
||||||
|
? (this.tempColors = new Float32Array(numVertices * 4))
|
||||||
|
: this.tempColors;
|
||||||
|
|
||||||
|
for (let i = 0; i < numVertices; i++) {
|
||||||
|
const srcIndex = i * 2;
|
||||||
|
const { x, y } = matrix.skeletonToGame(positions[srcIndex], positions[srcIndex + 1]);
|
||||||
|
|
||||||
|
const dstIndex = i * 3;
|
||||||
|
vertices[dstIndex] = x;
|
||||||
|
vertices[dstIndex + 1] = y;
|
||||||
|
vertices[dstIndex + 2] = 0;
|
||||||
|
|
||||||
|
const color = colors[i];
|
||||||
|
const colorDst = i * 4;
|
||||||
|
const alpha = (color >>> 24 & 0xFF) * inv255 * opacity;
|
||||||
|
const alphaInverse = inv255 * alpha;
|
||||||
|
c3colors[colorDst] = (color >>> 16 & 0xFF) * alphaInverse;
|
||||||
|
c3colors[colorDst + 1] = (color >>> 8 & 0xFF) * alphaInverse;
|
||||||
|
c3colors[colorDst + 2] = (color & 0xFF) * alphaInverse;
|
||||||
|
c3colors[colorDst + 3] = alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderSkeleton(
|
||||||
|
vertices.subarray(0, numVertices * 3),
|
||||||
|
uvs.subarray(0, numVertices * 2),
|
||||||
|
indices.subarray(0, numIndices),
|
||||||
|
c3colors.subarray(0, numVertices * 4),
|
||||||
|
command.texture,
|
||||||
|
blendMode)
|
||||||
|
command = command.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawDebug (x: number, y: number, quad: C3Quad) {
|
||||||
|
const { skeleton, matrix } = this;
|
||||||
|
|
||||||
|
const bones = skeleton.bones;
|
||||||
|
for (let i = 0, n = bones.length; i < n; i++) {
|
||||||
|
const bone = bones[i];
|
||||||
|
if (!bone.parent) continue;
|
||||||
|
const boneApplied = bone.applied;
|
||||||
|
const { x: x1, y: y1 } = matrix.skeletonToGame(boneApplied.worldX, boneApplied.worldY);
|
||||||
|
const x2 = bone.data.length * boneApplied.a + x1;
|
||||||
|
const y2 = bone.data.length * boneApplied.c + y1;
|
||||||
|
|
||||||
|
this.setColor(1, 0, 0, 1);
|
||||||
|
this.setColorFillMode();
|
||||||
|
|
||||||
|
const t = this.tempPoint.set(y2 - y1, x1 - x2);
|
||||||
|
t.normalize();
|
||||||
|
const width = 1 * 0.5;
|
||||||
|
const tx = t.x * width;
|
||||||
|
const ty = t.y * width;
|
||||||
|
this.poly([
|
||||||
|
x1 + tx, y1 + ty,
|
||||||
|
x1 - tx, y1 - ty,
|
||||||
|
x2 + tx, y2 + ty,
|
||||||
|
x2 - tx, y2 - ty,
|
||||||
|
x2 + tx, y2 + ty,
|
||||||
|
x1 - tx, y1 - ty,
|
||||||
|
]);
|
||||||
|
|
||||||
|
this.setColor(0, 1, 0, 1);
|
||||||
|
this.poly(this.circle(x1, y1, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderGameObjectBounds(x, y, quad);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract setColor (r: number, g: number, b: number, a: number): void;
|
||||||
|
protected abstract setColorFillMode (): void;
|
||||||
|
protected abstract poly (points: number[]): void;
|
||||||
|
|
||||||
|
protected abstract renderSkeleton (vertices: Float32Array, uvs: Float32Array, indices: Uint16Array, colors: Float32Array, texture: Texture, blendMode: BlendMode): void;
|
||||||
|
public abstract renderGameObjectBounds (x: number, y: number, quad: DOMQuad | SDK.Quad): void;
|
||||||
|
|
||||||
|
protected circle (x: number, y: number, radius: number) {
|
||||||
|
let segments = Math.max(1, (6 * MathUtils.cbrt(radius)) | 0);
|
||||||
|
if (segments <= 0) throw new Error("segments must be > 0.");
|
||||||
|
const angle = 2 * MathUtils.PI / segments;
|
||||||
|
const cos = Math.cos(angle);
|
||||||
|
const sin = Math.sin(angle);
|
||||||
|
let cx = radius, cy = 0;
|
||||||
|
segments--;
|
||||||
|
const poly = [];
|
||||||
|
for (let i = 0; i < segments; i++) {
|
||||||
|
poly.push(x, y);
|
||||||
|
poly.push(x + cx, y + cy);
|
||||||
|
const temp = cx;
|
||||||
|
cx = cos * cx - sin * cy;
|
||||||
|
cy = sin * temp + cos * cy;
|
||||||
|
poly.push(x + cx, y + cy);
|
||||||
|
}
|
||||||
|
poly.push(x, y, x + cx, y + cy);
|
||||||
|
cx = radius;
|
||||||
|
cy = 0;
|
||||||
|
poly.push(x + cx, y + cy);
|
||||||
|
return poly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class C3RendererRuntime extends C3SkeletonRenderer<IRenderer, C3TextureRuntime> {
|
||||||
|
constructor (renderer: IRenderer, skeleton: Skeleton, matrix: C3Matrix) {
|
||||||
|
super(renderer, skeleton, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setColor (r: number, g: number, b: number, a: number): void {
|
||||||
|
this.renderer.setColor([r, g, b, a]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setColorFillMode (): void {
|
||||||
|
this.renderer.setColorFillMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected poly (points: number[]): void {
|
||||||
|
this.renderer.convexPoly(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderSkeleton (vertices: Float32Array, uvs: Float32Array, indices: Uint16Array, colors: Float32Array, texture: C3TextureRuntime, blendMode: BlendMode) {
|
||||||
|
this.renderer.setTexture(texture.texture);
|
||||||
|
this.renderer.setBlendMode(BlendingModeSpineToC3[blendMode]);
|
||||||
|
this.renderer.drawMesh(vertices, uvs, indices, colors);
|
||||||
|
};
|
||||||
|
|
||||||
|
public renderDragHandles (bone: Bone, radius: number) {
|
||||||
|
const boneApplied = bone.applied;
|
||||||
|
const { x: x1, y: y1 } = this.matrix.skeletonToGame(boneApplied.worldX, boneApplied.worldY);
|
||||||
|
this.renderer.setColorFillMode();
|
||||||
|
this.renderer.setColor([1, 0, 0, .2]);
|
||||||
|
this.renderer.convexPoly(this.circle(x1, y1, radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderGameObjectBounds (x: number, y: number, quad: DOMQuad) {
|
||||||
|
const { renderer, matrix } = this;
|
||||||
|
renderer.setAlphaBlendMode();
|
||||||
|
renderer.setColorFillMode();
|
||||||
|
renderer.setColorRgba(0.25, 0, 0, 0.25);
|
||||||
|
renderer.lineQuad(quad);
|
||||||
|
renderer.line(x, y, matrix.tx, matrix.ty);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class C3RendererEditor extends C3SkeletonRenderer<SDK.Gfx.IWebGLRenderer, C3TextureEditor> {
|
||||||
|
protected setColor (r: number, g: number, b: number, a: number): void {
|
||||||
|
this.renderer.SetColorRgba(r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setColorFillMode (): void {
|
||||||
|
this.renderer.SetColorFillMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected poly (points: number[]): void {
|
||||||
|
this.renderer.ConvexPoly(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderSkeleton (vertices: Float32Array, uvs: Float32Array, indices: Uint16Array, colors: Float32Array, texture: C3TextureEditor, blendMode: BlendMode) {
|
||||||
|
this.renderer.ResetColor();
|
||||||
|
this.renderer.SetBlendMode(BlendingModeSpineToC3[blendMode]);
|
||||||
|
this.renderer.SetTextureFillMode();
|
||||||
|
this.renderer.SetTexture(texture.texture);
|
||||||
|
this.renderer.DrawMesh(vertices, uvs, indices, colors);
|
||||||
|
};
|
||||||
|
|
||||||
|
public renderGameObjectBounds (x: number, y: number, quad: SDK.Quad): void {
|
||||||
|
const { renderer, matrix } = this;
|
||||||
|
renderer.SetAlphaBlend();
|
||||||
|
renderer.SetColorFillMode();
|
||||||
|
renderer.SetColorRgba(0.25, 0, 0, 0.25);
|
||||||
|
renderer.LineQuad(quad);
|
||||||
|
renderer.Line(x, y, matrix.tx, matrix.ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -61,7 +61,7 @@ export class C3TextureEditor extends Texture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class C3Texture extends Texture {
|
export class C3TextureRuntime extends Texture {
|
||||||
texture: ITexture;
|
texture: ITexture;
|
||||||
renderer?: IRenderer;
|
renderer?: IRenderer;
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,32 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Spine Runtimes License Agreement
|
||||||
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013-2025, Esoteric Software LLC
|
||||||
|
*
|
||||||
|
* Integration of the Spine Runtimes into software or otherwise creating
|
||||||
|
* derivative works of the Spine Runtimes is permitted under the terms and
|
||||||
|
* conditions of Section 2 of the Spine Editor License Agreement:
|
||||||
|
* http://esotericsoftware.com/spine-editor-license
|
||||||
|
*
|
||||||
|
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
||||||
|
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
||||||
|
* "Products"), provided that each user of the Products must obtain their own
|
||||||
|
* Spine Editor license and redistribution of the Products in any form must
|
||||||
|
* include this license and copyright notice.
|
||||||
|
*
|
||||||
|
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
||||||
|
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
import { AnimationState, Physics, Skeleton, SkeletonClipping, Skin } from "@esotericsoftware/spine-core";
|
import { AnimationState, Physics, Skeleton, SkeletonClipping, Skin } from "@esotericsoftware/spine-core";
|
||||||
|
|
||||||
interface Rectangle {
|
interface Rectangle {
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
export * from "@esotericsoftware/spine-core";
|
export * from "@esotericsoftware/spine-core";
|
||||||
export * from './AssetLoader.js';
|
export * from './AssetLoader.js';
|
||||||
|
export * from './C3Matrix.js';
|
||||||
|
export * from './C3SkeletonRenderer.js';
|
||||||
export * from './C3Texture.js';
|
export * from './C3Texture.js';
|
||||||
export * from './SpineBoundsProvider.js';
|
export * from './SpineBoundsProvider.js';
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { AnimationState, AnimationStateListener, AssetLoader, Bone, Event, NumberArrayLike, RegionAttachment, Skeleton, SkeletonRendererCore, Skin, Slot, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib";
|
import type { AnimationState, AnimationStateListener, AssetLoader, Bone, C3Matrix, C3RendererRuntime, Event, NumberArrayLike, RegionAttachment, Skeleton, Skin, Slot, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib";
|
||||||
|
|
||||||
const C3 = globalThis.C3;
|
const C3 = globalThis.C3;
|
||||||
const spine = globalThis.spine;
|
const spine = globalThis.spine;
|
||||||
@ -39,12 +39,8 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
public triggeredEventData?: Event;
|
public triggeredEventData?: Event;
|
||||||
|
|
||||||
private assetLoader: AssetLoader;
|
private assetLoader: AssetLoader;
|
||||||
private skeletonRenderer: SkeletonRendererCore;
|
private skeletonRenderer?: C3RendererRuntime;
|
||||||
|
private matrix: C3Matrix;
|
||||||
private a = 0;
|
|
||||||
private b = 0;
|
|
||||||
private c = 0;
|
|
||||||
private d = 0;
|
|
||||||
|
|
||||||
private tempVertices = new Float32Array(4096);
|
private tempVertices = new Float32Array(4096);
|
||||||
private tempColors = new Float32Array(4096);
|
private tempColors = new Float32Array(4096);
|
||||||
@ -86,7 +82,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.assetLoader = new spine.AssetLoader();
|
this.assetLoader = new spine.AssetLoader();
|
||||||
this.skeletonRenderer = new spine.SkeletonRendererCore();
|
this.matrix = new spine.C3Matrix();
|
||||||
|
|
||||||
this._setTicking(true);
|
this._setTicking(true);
|
||||||
}
|
}
|
||||||
@ -125,7 +121,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private update (delta: number) {
|
private update (delta: number) {
|
||||||
const { state, skeleton, animationSpeed, physicsMode } = this;
|
const { state, skeleton, animationSpeed, physicsMode, matrix } = this;
|
||||||
|
|
||||||
if (!skeleton || !state) return;
|
if (!skeleton || !state) return;
|
||||||
|
|
||||||
@ -133,17 +129,13 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
state.update(adjustedDelta);
|
state.update(adjustedDelta);
|
||||||
skeleton.update(adjustedDelta);
|
skeleton.update(adjustedDelta);
|
||||||
state.apply(skeleton);
|
state.apply(skeleton);
|
||||||
|
matrix.update(
|
||||||
const cos = Math.cos(this.angle + this.propOffsetAngle);
|
this.x + this.propOffsetX,
|
||||||
const sin = Math.sin(this.angle + this.propOffsetAngle);
|
this.y + this.propOffsetY,
|
||||||
this.a = cos;
|
this.angle + this.propOffsetAngle);
|
||||||
this.b = sin;
|
|
||||||
this.c = -sin;
|
|
||||||
this.d = cos;
|
|
||||||
|
|
||||||
skeleton.updateWorldTransform(physicsMode);
|
skeleton.updateWorldTransform(physicsMode);
|
||||||
|
|
||||||
this.updateHandles(skeleton);
|
this.updateHandles(skeleton, matrix);
|
||||||
this.updateBoneFollowers();
|
this.updateBoneFollowers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,134 +148,19 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
const { skeleton } = this;
|
const { skeleton } = this;
|
||||||
if (!skeleton) return;
|
if (!skeleton) return;
|
||||||
|
|
||||||
this.renderSkeleton(renderer, skeleton);
|
this.skeletonRenderer ||= new spine.C3RendererRuntime(renderer, skeleton, this.matrix);
|
||||||
this.renderDragHandles(renderer);
|
this.skeletonRenderer.draw(this.opacity);
|
||||||
this.renderDebugSkeleton(renderer, skeleton);
|
if (this.propDebugSkeleton) this.skeletonRenderer.drawDebug(this.x, this.y, this.getBoundingQuad(false));
|
||||||
|
this.renderDragHandles();
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSkeleton (renderer: IRenderer, skeleton: Skeleton) {
|
private renderDragHandles () {
|
||||||
let command = this.skeletonRenderer.render(skeleton);
|
|
||||||
const opacity = this.opacity;
|
|
||||||
const inv255 = 1 / 255;
|
|
||||||
while (command) {
|
|
||||||
const { numVertices, positions, uvs, colors, indices, numIndices, blendMode } = command;
|
|
||||||
|
|
||||||
const vertices = this.tempVertices.length < numVertices * 3
|
|
||||||
? (this.tempVertices = new Float32Array(numVertices * 3))
|
|
||||||
: this.tempVertices;
|
|
||||||
|
|
||||||
const c3colors = this.tempColors.length < numVertices * 4
|
|
||||||
? (this.tempColors = new Float32Array(numVertices * 4))
|
|
||||||
: this.tempColors;
|
|
||||||
|
|
||||||
for (let i = 0; i < numVertices; i++) {
|
|
||||||
const srcIndex = i * 2;
|
|
||||||
const { x, y } = this.skeletonToC3WorldCoordinates(positions[srcIndex], positions[srcIndex + 1]);
|
|
||||||
|
|
||||||
const dstIndex = i * 3;
|
|
||||||
vertices[dstIndex] = x;
|
|
||||||
vertices[dstIndex + 1] = y;
|
|
||||||
vertices[dstIndex + 2] = 0;
|
|
||||||
|
|
||||||
const color = colors[i];
|
|
||||||
const colorDst = i * 4;
|
|
||||||
const alpha = (color >>> 24 & 0xFF) * inv255 * opacity;
|
|
||||||
const alphaInverse = inv255 * alpha;
|
|
||||||
c3colors[colorDst] = (color >>> 16 & 0xFF) * alphaInverse;
|
|
||||||
c3colors[colorDst + 1] = (color >>> 8 & 0xFF) * alphaInverse;
|
|
||||||
c3colors[colorDst + 2] = (color & 0xFF) * alphaInverse;
|
|
||||||
c3colors[colorDst + 3] = alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.setTexture(command.texture.texture);
|
|
||||||
renderer.setBlendMode(spine.BlendingModeSpineToC3[blendMode]);
|
|
||||||
renderer.drawMesh(
|
|
||||||
vertices.subarray(0, numVertices * 3),
|
|
||||||
uvs.subarray(0, numVertices * 2),
|
|
||||||
indices.subarray(0, numIndices),
|
|
||||||
c3colors.subarray(0, numVertices * 4),
|
|
||||||
);
|
|
||||||
|
|
||||||
command = command.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderDragHandles (renderer: IRenderer) {
|
|
||||||
for (const { bone, radius, debug } of this.dragHandles) {
|
for (const { bone, radius, debug } of this.dragHandles) {
|
||||||
if (!debug) continue;
|
if (!debug) continue;
|
||||||
const boneApplied = bone.applied;
|
this.skeletonRenderer?.renderDragHandles(bone, radius);
|
||||||
const { x: x1, y: y1 } = this.skeletonToC3WorldCoordinates(boneApplied.worldX, boneApplied.worldY);
|
|
||||||
renderer.setColorFillMode();
|
|
||||||
renderer.setColor([1, 0, 0, .2]);
|
|
||||||
renderer.convexPoly(this.circle(x1, y1, radius));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderDebugSkeleton (renderer: IRenderer, skeleton: Skeleton) {
|
|
||||||
if (!this.propDebugSkeleton) return;
|
|
||||||
|
|
||||||
const bones = skeleton.bones;
|
|
||||||
for (let i = 0, n = bones.length; i < n; i++) {
|
|
||||||
const bone = bones[i];
|
|
||||||
if (!bone.parent) continue;
|
|
||||||
const boneApplied = bone.applied;
|
|
||||||
const { x: x1, y: y1 } = this.skeletonToC3WorldCoordinates(boneApplied.worldX, boneApplied.worldY);
|
|
||||||
const x2 = bone.data.length * boneApplied.a + x1;
|
|
||||||
const y2 = bone.data.length * boneApplied.c + y1;
|
|
||||||
|
|
||||||
renderer.setColor([1, 0, 0, 1]);
|
|
||||||
renderer.setColorFillMode();
|
|
||||||
|
|
||||||
const t = this.tempPoint.set(y2 - y1, x1 - x2);
|
|
||||||
t.normalize();
|
|
||||||
const width = 1 * 0.5;
|
|
||||||
const tx = t.x * width;
|
|
||||||
const ty = t.y * width;
|
|
||||||
renderer.convexPoly([
|
|
||||||
x1 + tx, y1 + ty,
|
|
||||||
x1 - tx, y1 - ty,
|
|
||||||
x2 + tx, y2 + ty,
|
|
||||||
x2 - tx, y2 - ty,
|
|
||||||
x2 + tx, y2 + ty,
|
|
||||||
x1 - tx, y1 - ty,
|
|
||||||
]);
|
|
||||||
|
|
||||||
renderer.setColor([0, 1, 0, 1]);
|
|
||||||
renderer.convexPoly(this.circle(x1, y1, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// debug bounds
|
|
||||||
renderer.setAlphaBlendMode();
|
|
||||||
renderer.setColorFillMode();
|
|
||||||
renderer.setColorRgba(0.25, 0, 0, 0.25);
|
|
||||||
renderer.lineQuad(this.getBoundingQuad(false));
|
|
||||||
renderer.line(this.x, this.y, this.x + this.propOffsetX, this.y + this.propOffsetY);
|
|
||||||
}
|
|
||||||
|
|
||||||
private circle (x: number, y: number, radius: number) {
|
|
||||||
let segments = Math.max(1, (6 * spine.MathUtils.cbrt(radius)) | 0);
|
|
||||||
if (segments <= 0) throw new Error("segments must be > 0.");
|
|
||||||
const angle = 2 * spine.MathUtils.PI / segments;
|
|
||||||
const cos = Math.cos(angle);
|
|
||||||
const sin = Math.sin(angle);
|
|
||||||
let cx = radius, cy = 0;
|
|
||||||
segments--;
|
|
||||||
const poly = [];
|
|
||||||
for (let i = 0; i < segments; i++) {
|
|
||||||
poly.push(x, y);
|
|
||||||
poly.push(x + cx, y + cy);
|
|
||||||
const temp = cx;
|
|
||||||
cx = cos * cx - sin * cy;
|
|
||||||
cy = sin * temp + cos * cy;
|
|
||||||
poly.push(x + cx, y + cy);
|
|
||||||
}
|
|
||||||
poly.push(x, y, x + cx, y + cy);
|
|
||||||
cx = radius;
|
|
||||||
cy = 0;
|
|
||||||
poly.push(x + cx, y + cy);
|
|
||||||
return poly;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********/
|
/**********/
|
||||||
|
|
||||||
|
|
||||||
@ -327,7 +204,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateHandles (skeleton: Skeleton) {
|
private updateHandles (skeleton: Skeleton, matrix: C3Matrix) {
|
||||||
if (this.dragHandles.size === 0) return;
|
if (this.dragHandles.size === 0) return;
|
||||||
|
|
||||||
// accessing mouse without having a mouse object will throw an error
|
// accessing mouse without having a mouse object will throw an error
|
||||||
@ -357,16 +234,16 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
|
|
||||||
if (handleObject.dragging) {
|
if (handleObject.dragging) {
|
||||||
if (bone.parent) {
|
if (bone.parent) {
|
||||||
const { x, y } = this.c3WorldCoordinatesToBone(mx - handleObject.offsetX, my - handleObject.offsetY, bone);
|
const { x, y } = matrix.gameToBone(mx - handleObject.offsetX, my - handleObject.offsetY, bone);
|
||||||
boneApplied.x = x;
|
boneApplied.x = x;
|
||||||
boneApplied.y = y;
|
boneApplied.y = y;
|
||||||
} else {
|
} else {
|
||||||
const { x, y } = this.c3WorldCoordinatesToSkeleton(mx - handleObject.offsetX, my - handleObject.offsetY);
|
const { x, y } = matrix.gameToSkeleton(mx - handleObject.offsetX, my - handleObject.offsetY);
|
||||||
boneApplied.x = x / skeleton.scaleX;
|
boneApplied.x = x / skeleton.scaleX;
|
||||||
boneApplied.y = -y / skeleton.scaleY * spine.Skeleton.yDir;
|
boneApplied.y = -y / skeleton.scaleY * spine.Skeleton.yDir;
|
||||||
}
|
}
|
||||||
} else if (!this.prevLeftClickDown) {
|
} else if (!this.prevLeftClickDown) {
|
||||||
const { x, y } = this.c3WorldCoordinatesToSkeleton(mx, my);
|
const { x, y } = matrix.gameToSkeleton(mx, my);
|
||||||
const inside = handleObject.slot
|
const inside = handleObject.slot
|
||||||
? this.isInsideSlot(x, y, handleObject.slot, true)
|
? this.isInsideSlot(x, y, handleObject.slot, true)
|
||||||
: this.inRadius(x, y, boneApplied.worldX, boneApplied.worldY, handleObject.radius);
|
: this.inRadius(x, y, boneApplied.worldX, boneApplied.worldY, handleObject.radius);
|
||||||
@ -402,7 +279,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
|
|
||||||
if (skeletonCoordinate) return this.isPointInPolygon(vertices, hullLength, x, y);
|
if (skeletonCoordinate) return this.isPointInPolygon(vertices, hullLength, x, y);
|
||||||
|
|
||||||
const coords = this.c3WorldCoordinatesToSkeleton(x, y);
|
const coords = this.matrix.gameToSkeleton(x, y);
|
||||||
return this.isPointInPolygon(vertices, hullLength, coords.x, coords.y);
|
return this.isPointInPolygon(vertices, hullLength, coords.x, coords.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,7 +683,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
const instance = this.runtime.getInstanceByUid(follower.uid) as IWorldInstance;
|
const instance = this.runtime.getInstanceByUid(follower.uid) as IWorldInstance;
|
||||||
if (!instance) continue;
|
if (!instance) continue;
|
||||||
|
|
||||||
const { x, y } = this.boneToC3WorldCoordinates(bone);
|
const { x, y } = this.matrix.boneToGame(bone);
|
||||||
const boneRotation = bone.applied.getWorldRotationX();
|
const boneRotation = bone.applied.getWorldRotationX();
|
||||||
|
|
||||||
// Apply rotation to offset
|
// Apply rotation to offset
|
||||||
@ -824,41 +701,6 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
|
|
||||||
/**********/
|
/**********/
|
||||||
|
|
||||||
/*
|
|
||||||
* Coordinates transformation
|
|
||||||
*/
|
|
||||||
|
|
||||||
private c3WorldCoordinatesToSkeleton (x: number, y: number) {
|
|
||||||
const tx = x - (this.x + this.propOffsetX);
|
|
||||||
const ty = y - (this.y + this.propOffsetY);
|
|
||||||
const { a: ta, b: tb, c: tc, d: td, tempPoint } = this;
|
|
||||||
const delta = ta * td - tb * tc;
|
|
||||||
tempPoint.x = (td * tx - tc * ty) / delta;
|
|
||||||
tempPoint.y = (ta * ty - tb * tx) / delta;
|
|
||||||
return this.tempPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private c3WorldCoordinatesToBone (x: number, y: number, bone: Bone) {
|
|
||||||
const point = this.c3WorldCoordinatesToSkeleton(x, y);
|
|
||||||
if (bone.parent)
|
|
||||||
return bone.parent.applied.worldToLocal(point);
|
|
||||||
return bone.applied.worldToLocal(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
private skeletonToC3WorldCoordinates (skeletonX: number, skeletonY: number) {
|
|
||||||
const { a, b, c, d, tempPoint } = this;
|
|
||||||
tempPoint.x = a * skeletonX + c * skeletonY + this.x + this.propOffsetX;
|
|
||||||
tempPoint.y = b * skeletonX + d * skeletonY + this.y + this.propOffsetY;
|
|
||||||
return tempPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boneToC3WorldCoordinates (bone: Bone) {
|
|
||||||
const { applied } = bone;
|
|
||||||
return this.skeletonToC3WorldCoordinates(applied.worldX, applied.worldY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bone
|
* Bone
|
||||||
*/
|
*/
|
||||||
@ -947,7 +789,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const point = this.boneToC3WorldCoordinates(bone);
|
const point = this.matrix.boneToGame(bone);
|
||||||
return point.x;
|
return point.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -964,7 +806,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const point = this.boneToC3WorldCoordinates(bone);
|
const point = this.matrix.boneToGame(bone);
|
||||||
return point.y;
|
return point.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,7 +814,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
const bone = this.getBone(boneName);
|
const bone = this.getBone(boneName);
|
||||||
if (!bone) return;
|
if (!bone) return;
|
||||||
|
|
||||||
const { x, y } = this.c3WorldCoordinatesToBone(c3X, c3Y, bone);
|
const { x, y } = this.matrix.gameToBone(c3X, c3Y, bone);
|
||||||
bone.applied.x = x;
|
bone.applied.x = x;
|
||||||
bone.applied.y = y;
|
bone.applied.y = y;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// / <reference types="editor/sdk" />
|
// / <reference types="editor/sdk" />
|
||||||
|
|
||||||
import type { AnimationState, AssetLoader, Skeleton, SkeletonRendererCore, SpineBoundsProvider, TextureAtlas } from "@esotericsoftware/spine-construct3-lib";
|
import type { AnimationState, AssetLoader, C3Matrix, C3RendererEditor, Skeleton, SpineBoundsProvider, TextureAtlas, } from "@esotericsoftware/spine-construct3-lib";
|
||||||
import type { SpineC3PluginType } from "./type";
|
import type { SpineC3PluginType } from "./type";
|
||||||
|
|
||||||
const SDK = globalThis.SDK;
|
const SDK = globalThis.SDK;
|
||||||
@ -25,7 +25,8 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
|||||||
animation?: string;
|
animation?: string;
|
||||||
|
|
||||||
private assetLoader: AssetLoader;
|
private assetLoader: AssetLoader;
|
||||||
private skeletonRenderer: SkeletonRendererCore;
|
private skeletonRenderer?: C3RendererEditor;
|
||||||
|
private matrix: C3Matrix;
|
||||||
|
|
||||||
// position mode
|
// position mode
|
||||||
private positioningBounds = false;
|
private positioningBounds = false;
|
||||||
@ -40,10 +41,6 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
|||||||
height: 200,
|
height: 200,
|
||||||
};
|
};
|
||||||
|
|
||||||
// utils for drawing
|
|
||||||
private tempVertices = new Float32Array(4096);
|
|
||||||
private tempColors = new Float32Array(4096);
|
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
private errorTextureAtlas?: string;
|
private errorTextureAtlas?: string;
|
||||||
private errorSkeleton?: string;
|
private errorSkeleton?: string;
|
||||||
@ -56,7 +53,7 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
|||||||
spine.Skeleton.yDown = true;
|
spine.Skeleton.yDown = true;
|
||||||
|
|
||||||
this.assetLoader = new spine.AssetLoader();
|
this.assetLoader = new spine.AssetLoader();
|
||||||
this.skeletonRenderer = new spine.SkeletonRendererCore();
|
this.matrix = new spine.C3Matrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
Release () {
|
Release () {
|
||||||
@ -118,64 +115,13 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
|||||||
skeleton.scaleY = _inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_Y) as number;
|
skeleton.scaleY = _inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_Y) as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const opacity = _inst.GetOpacity();
|
|
||||||
const cos = Math.cos(offsetAngle);
|
|
||||||
const sin = Math.sin(offsetAngle);
|
|
||||||
const inv255 = 1 / 255;
|
|
||||||
|
|
||||||
this.update(0);
|
this.update(0);
|
||||||
let command = this.skeletonRenderer.render(skeleton);
|
this.skeletonRenderer ||= new spine.C3RendererEditor(iRenderer, skeleton, this.matrix);
|
||||||
while (command) {
|
this.skeletonRenderer.draw(_inst.GetOpacity());
|
||||||
const { numVertices, positions, uvs, colors, indices, numIndices, blendMode } = command;
|
const quad = _inst.GetQuad();
|
||||||
|
if (_inst.GetPropertyValue(PLUGIN_CLASS.PROP_DEBUG_SKELETON) as boolean)
|
||||||
const vertices = this.tempVertices.length < numVertices * 3
|
this.skeletonRenderer.drawDebug(rectX, rectY, quad);
|
||||||
? (this.tempVertices = new Float32Array(numVertices * 3))
|
this.skeletonRenderer.renderGameObjectBounds(rectX, rectY, quad);
|
||||||
: this.tempVertices;
|
|
||||||
|
|
||||||
const c3colors = this.tempColors.length < numVertices * 4
|
|
||||||
? (this.tempColors = new Float32Array(numVertices * 4))
|
|
||||||
: this.tempColors;
|
|
||||||
|
|
||||||
for (let i = 0; i < numVertices; i++) {
|
|
||||||
const srcIndex = i * 2;
|
|
||||||
const dstIndex = i * 3;
|
|
||||||
const x = positions[srcIndex];
|
|
||||||
const y = positions[srcIndex + 1];
|
|
||||||
vertices[dstIndex] = x * cos - y * sin + offsetX;
|
|
||||||
vertices[dstIndex + 1] = x * sin + y * cos + offsetY;
|
|
||||||
vertices[dstIndex + 2] = 0;
|
|
||||||
|
|
||||||
const color = colors[i];
|
|
||||||
const colorDst = i * 4;
|
|
||||||
const alpha = (color >>> 24 & 0xFF) * inv255 * opacity;
|
|
||||||
const alphaInverse = inv255 * alpha;
|
|
||||||
c3colors[colorDst] = (color >>> 16 & 0xFF) * alphaInverse;
|
|
||||||
c3colors[colorDst + 1] = (color >>> 8 & 0xFF) * alphaInverse;
|
|
||||||
c3colors[colorDst + 2] = (color & 0xFF) * alphaInverse;
|
|
||||||
c3colors[colorDst + 3] = alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
iRenderer.ResetColor();
|
|
||||||
iRenderer.SetBlendMode(spine.BlendingModeSpineToC3[blendMode]);
|
|
||||||
iRenderer.SetTextureFillMode();
|
|
||||||
iRenderer.SetTexture(command.texture.texture);
|
|
||||||
|
|
||||||
iRenderer.DrawMesh(
|
|
||||||
vertices.subarray(0, numVertices * 3),
|
|
||||||
uvs.subarray(0, numVertices * 2),
|
|
||||||
indices.subarray(0, numIndices),
|
|
||||||
c3colors,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
command = command.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
iRenderer.SetAlphaBlend();
|
|
||||||
iRenderer.SetColorFillMode();
|
|
||||||
iRenderer.SetColorRgba(0.25, 0, 0, 0.25);
|
|
||||||
iRenderer.LineQuad(_inst.GetQuad());
|
|
||||||
iRenderer.Line(rectX, rectY, offsetX, offsetY);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
iRenderer.SetAlphaBlend();
|
iRenderer.SetAlphaBlend();
|
||||||
@ -501,6 +447,10 @@ class SpineC3PluginInstance extends SDK.IWorldInstanceBase {
|
|||||||
state.update(delta);
|
state.update(delta);
|
||||||
skeleton.update(delta);
|
skeleton.update(delta);
|
||||||
state.apply(skeleton);
|
state.apply(skeleton);
|
||||||
|
this.matrix.update(
|
||||||
|
this._inst.GetX() + (this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X) as number),
|
||||||
|
this._inst.GetY() + (this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y) as number),
|
||||||
|
this._inst.GetAngle() + (this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE) as number));
|
||||||
skeleton.updateWorldTransform(spine.Physics.update);
|
skeleton.updateWorldTransform(spine.Physics.update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user