Reuse previous render command, if game object not updated.

This commit is contained in:
Davide Tantillo 2025-12-09 17:09:16 +01:00
parent 546c9f8a6b
commit 4e2d01d67a
3 changed files with 36 additions and 21 deletions

View File

@ -27,7 +27,7 @@
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { type BlendMode, type Bone, ClippingAttachment, MathUtils, MeshAttachment, PathAttachment, RegionAttachment, type Skeleton, SkeletonRendererCore, Utils, Vector2 } from "@esotericsoftware/spine-core";
import { type BlendMode, type Bone, ClippingAttachment, MathUtils, MeshAttachment, PathAttachment, RegionAttachment, RenderCommand, type Skeleton, SkeletonRendererCore, Utils, Vector2 } from "@esotericsoftware/spine-core";
import type { C3Matrix } from "./C3Matrix";
import { BlendingModeSpineToC3, type C3TextureEditor, type C3TextureRuntime } from "./C3Texture";
@ -40,6 +40,7 @@ abstract class C3SkeletonRenderer<
Texture extends C3Texture,
> extends SkeletonRendererCore {
private command?: RenderCommand;
private tempVertices = new Float32Array(4096);
private tempColors = new Float32Array(4096);
private tempPoint = new Vector2();
@ -53,10 +54,15 @@ abstract class C3SkeletonRenderer<
super();
}
draw (skeleton: Skeleton, inColors: [number, number, number], opacity = 1) {
draw (skeleton: Skeleton, inColors: [number, number, number], opacity = 1, isPlaying = true, fromUpdate = true) {
const { matrix, inv255 } = this;
let command = this.render(skeleton, true, [...inColors, opacity]);
this.command = (isPlaying || !this.command)
? this.render(skeleton, true, [...inColors, opacity])
: this.command;
let command = this.command;
const { a, b, c, d, tx, ty } = matrix;
while (command) {
const { numVertices, positions, uvs, colors, indices, numIndices, blendMode } = command;
@ -70,11 +76,11 @@ abstract class C3SkeletonRenderer<
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;
const x = positions[srcIndex];
const y = positions[srcIndex + 1];
vertices[dstIndex] = a * x + c * y + tx;
vertices[dstIndex + 1] = b * x + d * y + ty;
vertices[dstIndex + 2] = 0;
const color = colors[i];

View File

@ -19,7 +19,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
propDebugSkeleton = false;
isFlippedX = false;
isPlaying = true;
isPlaying = false;
animationSpeed = 1.0;
physicsMode = spine.Physics.update;
customSkins: Record<string, Skin> = {};
@ -111,12 +111,19 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
return;
}
if (!this.isPlaying) return;
this.matrix.update(
this.x + this.propOffsetX,
this.y + this.propOffsetY,
this.angle + this.propOffsetAngle);
this.update(this.dt);
this.runtime.sdk.updateRender();
if (this.isPlaying) {
this.update(this.dt);
this.runtime.sdk.updateRender();
}
}
private fromUpdate = false;
private update (delta: number) {
const { state, skeleton, animationSpeed, physicsMode, matrix } = this;
@ -126,16 +133,14 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
state.update(adjustedDelta);
skeleton.update(adjustedDelta);
state.apply(skeleton);
matrix.update(
this.x + this.propOffsetX,
this.y + this.propOffsetY,
this.angle + this.propOffsetAngle);
this.updateHandles(skeleton, matrix);
skeleton.updateWorldTransform(physicsMode);
this.updateBoneFollowers();
this.updateBoneFollowers(matrix);
this.fromUpdate = true;
}
_draw (renderer: IRenderer) {
@ -148,7 +153,8 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
if (!skeleton) return;
this.skeletonRenderer ||= new spine.C3RendererRuntime(renderer, this.matrix);
this.skeletonRenderer.draw(skeleton, this.colorRgb, this.opacity);
this.skeletonRenderer.draw(skeleton, this.colorRgb, this.opacity, this.isPlaying, this.fromUpdate);
this.fromUpdate = false;
if (this.propDebugSkeleton) this.skeletonRenderer.drawDebug(skeleton, this.x, this.y, this.getBoundingQuad(false));
this.renderDragHandles();
@ -187,6 +193,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
this.runtime.addEventListener("pointermove", this.dragHandleMove);
this.runtime.addEventListener("pointerup", this.dragHandleUp);
}
this.isPlaying = true;
}
private dragHandleDown = (event: ConstructPointerEvent) => {
@ -394,6 +401,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
if (this.propAnimation) {
this.setAnimation(0, this.propAnimation, true);
this.isPlaying = true;
}
this._setSkin();
@ -402,6 +410,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
this.skeleton.scaleY = this.propScaleY;
this.update(0);
this.runtime.sdk.updateRender();
this.skeletonLoaded = true;
this._trigger(C3.Plugins.EsotericSoftware_SpineConstruct3.Cnds.OnSkeletonLoaded);
@ -582,7 +591,6 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
}
skeleton.setupPose();
this.update(0);
}
public createCustomSkin (skinName: string) {
@ -691,13 +699,14 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
}
this.boneFollowers.set(boneName, { uid, offsetX, offsetY, offsetAngle });
this.isPlaying = true;
}
public detachInstanceFromBone (boneName: string) {
this.boneFollowers.delete(boneName);
}
private updateBoneFollowers () {
private updateBoneFollowers (matrix: C3Matrix) {
if (this.boneFollowers.size === 0) return;
for (const [boneName, follower] of this.boneFollowers) {
@ -707,7 +716,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
const instance = this.runtime.getInstanceByUid(follower.uid) as IWorldInstance;
if (!instance) continue;
const { x, y } = this.matrix.boneToGame(bone);
const { x, y } = matrix.boneToGame(bone);
const boneRotation = bone.applied.getWorldRotationX();
// Apply rotation to offset

View File

@ -292,7 +292,7 @@ export class SkeletonRendererCore {
// values with under score is the original sized array, bigger than necessary
// values without under score is a view of the orignal array, sized as needed
interface RenderCommand {
export interface RenderCommand {
positions: Float32Array;
uvs: Float32Array;
colors: Uint32Array;