mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 09:16:01 +08:00
Do not reuse the same command if position or color change.
This commit is contained in:
parent
4e2d01d67a
commit
829c3c3e6a
@ -38,9 +38,17 @@ export class C3Matrix {
|
||||
public tx = 0;
|
||||
public ty = 0;
|
||||
|
||||
public prevX = Infinity;
|
||||
public prevY = Infinity;
|
||||
public prevAngle = Infinity;
|
||||
|
||||
private tempPoint = new Vector2();
|
||||
|
||||
public update (x: number, y: number, angle: number) {
|
||||
if (this.prevX === x && this.prevY === y && this.prevAngle === angle) return false;
|
||||
this.prevX = x;
|
||||
this.prevY = y;
|
||||
this.prevAngle = angle;
|
||||
const cos = Math.cos(angle);
|
||||
const sin = Math.sin(angle);
|
||||
this.a = cos;
|
||||
@ -49,6 +57,7 @@ export class C3Matrix {
|
||||
this.d = cos;
|
||||
this.tx = x;
|
||||
this.ty = y;
|
||||
return true;
|
||||
}
|
||||
|
||||
public gameToSkeleton (x: number, y: number) {
|
||||
|
||||
@ -47,6 +47,14 @@ abstract class C3SkeletonRenderer<
|
||||
private tempArray = [] as number[];
|
||||
private inv255 = 1 / 255;
|
||||
|
||||
private prevX = Infinity;
|
||||
private prevY = Infinity;
|
||||
private prevAngle = Infinity;
|
||||
private prevRed = -1;
|
||||
private prevGreen = -1;
|
||||
private prevBlue = -1;
|
||||
private prevAlpha = -1;
|
||||
|
||||
constructor (
|
||||
protected renderer: Renderer,
|
||||
protected matrix: C3Matrix,
|
||||
@ -54,45 +62,51 @@ abstract class C3SkeletonRenderer<
|
||||
super();
|
||||
}
|
||||
|
||||
draw (skeleton: Skeleton, inColors: [number, number, number], opacity = 1, isPlaying = true, fromUpdate = true) {
|
||||
draw (skeleton: Skeleton, inColors: [number, number, number], opacity = 1, requestRedraw = true) {
|
||||
const { matrix, inv255 } = this;
|
||||
const { a, b, c, d, tx, ty, prevX, prevY, prevAngle } = matrix;
|
||||
|
||||
this.command = (isPlaying || !this.command)
|
||||
? this.render(skeleton, true, [...inColors, opacity])
|
||||
: this.command;
|
||||
const requestRedrawForMatrix = this.prevX !== prevX || this.prevY !== prevY || this.prevAngle !== prevAngle;
|
||||
this.prevX = prevX;
|
||||
this.prevY = prevY;
|
||||
this.prevAngle = prevAngle;
|
||||
|
||||
const requestRedrawForColor = this.prevRed !== inColors[0] || this.prevGreen !== inColors[1] || this.prevBlue !== inColors[2] || this.prevAlpha !== opacity;
|
||||
this.prevRed = inColors[0];
|
||||
this.prevGreen = inColors[1];
|
||||
this.prevBlue = inColors[2];
|
||||
this.prevAlpha = opacity;
|
||||
|
||||
const newCommand = (requestRedraw || requestRedrawForColor || requestRedrawForMatrix || !this.command);
|
||||
this.command = newCommand ? this.render(skeleton, true, [...inColors, opacity], 3) : 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;
|
||||
|
||||
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 dstIndex = i * 3;
|
||||
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;
|
||||
if (newCommand) {
|
||||
for (let i = 0; i < numVertices; i++) {
|
||||
const index = i * 3;
|
||||
const x = positions[index];
|
||||
const y = positions[index + 1];
|
||||
positions[index] = a * x + c * y + tx;
|
||||
positions[index + 1] = b * x + d * y + ty;
|
||||
|
||||
const color = colors[i];
|
||||
const colorDst = i * 4;
|
||||
c3colors[colorDst] = (color >>> 16 & 0xFF) * inv255;
|
||||
c3colors[colorDst + 1] = (color >>> 8 & 0xFF) * inv255;
|
||||
c3colors[colorDst + 2] = (color & 0xFF) * inv255;
|
||||
c3colors[colorDst + 3] = (color >>> 24) * inv255;
|
||||
const color = colors[i];
|
||||
const colorDst = i * 4;
|
||||
c3colors[colorDst] = (color >>> 16 & 0xFF) * inv255;
|
||||
c3colors[colorDst + 1] = (color >>> 8 & 0xFF) * inv255;
|
||||
c3colors[colorDst + 2] = (color & 0xFF) * inv255;
|
||||
c3colors[colorDst + 3] = (color >>> 24) * inv255;
|
||||
}
|
||||
}
|
||||
|
||||
this.renderSkeleton(
|
||||
vertices.subarray(0, numVertices * 3),
|
||||
positions.subarray(0, numVertices * 3),
|
||||
uvs.subarray(0, numVertices * 2),
|
||||
indices.subarray(0, numIndices),
|
||||
c3colors.subarray(0, numVertices * 4),
|
||||
|
||||
@ -41,6 +41,7 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
private assetLoader: AssetLoader;
|
||||
private skeletonRenderer?: C3RendererRuntime;
|
||||
private matrix: C3Matrix;
|
||||
private requestRedraw = false;
|
||||
|
||||
private verticesTemp = spine.Utils.newFloatArray(2 * 1024);
|
||||
|
||||
@ -116,14 +117,9 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
this.y + this.propOffsetY,
|
||||
this.angle + this.propOffsetAngle);
|
||||
|
||||
if (this.isPlaying) {
|
||||
this.update(this.dt);
|
||||
this.runtime.sdk.updateRender();
|
||||
}
|
||||
if (this.isPlaying) this.update(this.dt);
|
||||
}
|
||||
|
||||
private fromUpdate = false;
|
||||
|
||||
private update (delta: number) {
|
||||
const { state, skeleton, animationSpeed, physicsMode, matrix } = this;
|
||||
|
||||
@ -140,7 +136,8 @@ class SpineC3Instance extends globalThis.ISDKWorldInstanceBase {
|
||||
|
||||
this.updateBoneFollowers(matrix);
|
||||
|
||||
this.fromUpdate = true;
|
||||
this.runtime.sdk.updateRender();
|
||||
this.requestRedraw = true;
|
||||
}
|
||||
|
||||
_draw (renderer: IRenderer) {
|
||||
@ -153,8 +150,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.isPlaying, this.fromUpdate);
|
||||
this.fromUpdate = false;
|
||||
this.skeletonRenderer.draw(skeleton, this.colorRgb, this.opacity, this.requestRedraw);
|
||||
this.requestRedraw = false;
|
||||
|
||||
if (this.propDebugSkeleton) this.skeletonRenderer.drawDebug(skeleton, this.x, this.y, this.getBoundingQuad(false));
|
||||
this.renderDragHandles();
|
||||
@ -410,7 +407,6 @@ 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);
|
||||
|
||||
@ -324,7 +324,7 @@ export class SkeletonClipping {
|
||||
return clipOutputItems != null;
|
||||
}
|
||||
|
||||
public clipTrianglesUnpacked (vertices: NumberArrayLike, triangles: NumberArrayLike | Uint32Array, trianglesLength: number, uvs: NumberArrayLike) {
|
||||
public clipTrianglesUnpacked (vertices: NumberArrayLike, triangles: NumberArrayLike | Uint32Array, trianglesLength: number, uvs: NumberArrayLike, stride = 2) {
|
||||
const clipOutput = this.clipOutput;
|
||||
let clippedVertices = this._clippedVerticesTyped, clippedUVs = this._clippedUVsTyped, clippedTriangles = this._clippedTrianglesTyped;
|
||||
// biome-ignore lint/style/noNonNullAssertion: clipStart define it
|
||||
@ -343,17 +343,23 @@ export class SkeletonClipping {
|
||||
let clipped = false;
|
||||
|
||||
for (let i = 0; i < trianglesLength; i += 3) {
|
||||
let v = triangles[i] << 1;
|
||||
let t = triangles[i];
|
||||
let v = t * stride;
|
||||
const x1 = vertices[v], y1 = vertices[v + 1];
|
||||
const u1 = uvs[v], v1 = uvs[v + 1];
|
||||
let uv = t << 1;
|
||||
const u1 = uvs[uv], v1 = uvs[uv + 1];
|
||||
|
||||
v = triangles[i + 1] << 1;
|
||||
t = triangles[i + 1];
|
||||
v = t * stride;
|
||||
const x2 = vertices[v], y2 = vertices[v + 1];
|
||||
const u2 = uvs[v], v2 = uvs[v + 1];
|
||||
uv = t << 1;
|
||||
const u2 = uvs[uv], v2 = uvs[uv + 1];
|
||||
|
||||
v = triangles[i + 2] << 1;
|
||||
t = triangles[i + 2];
|
||||
v = t * stride;
|
||||
const x3 = vertices[v], y3 = vertices[v + 1];
|
||||
const u3 = uvs[v], v3 = uvs[v + 1];
|
||||
uv = t << 1;
|
||||
const u3 = uvs[uv], v3 = uvs[uv + 1];
|
||||
|
||||
for (let p = 0; p < polygonsCount; p++) {
|
||||
let s = this.clippedVerticesLength;
|
||||
@ -367,20 +373,22 @@ export class SkeletonClipping {
|
||||
let clipOutputCount = clipOutputLength >> 1;
|
||||
const clipOutputItems = this.clipOutput;
|
||||
|
||||
const newLength = s + clipOutputCount * 2;
|
||||
const newLength = s + clipOutputCount * stride;
|
||||
if (clippedVertices.length < newLength) {
|
||||
this._clippedVerticesTyped = new Float32Array(newLength * 2);
|
||||
this._clippedVerticesTyped.set(clippedVertices.subarray(0, s));
|
||||
this._clippedUVsTyped = new Float32Array(newLength * 2);
|
||||
this._clippedUVsTyped.set(clippedUVs.subarray(0, s));
|
||||
this._clippedUVsTyped = new Float32Array((this.clippedUVsLength + clipOutputCount * 2) * 2);
|
||||
this._clippedUVsTyped.set(clippedUVs.subarray(0, this.clippedUVsLength));
|
||||
clippedVertices = this._clippedVerticesTyped;
|
||||
clippedUVs = this._clippedUVsTyped;
|
||||
}
|
||||
const clippedVerticesItems = clippedVertices;
|
||||
const clippedUVsItems = clippedUVs;
|
||||
this.clippedVerticesLength = newLength;
|
||||
this.clippedUVsLength = newLength;
|
||||
for (let ii = 0; ii < clipOutputLength; ii += 2, s += 2) {
|
||||
|
||||
let uvIndex = this.clippedUVsLength;
|
||||
this.clippedUVsLength = uvIndex + clipOutputCount * 2;
|
||||
for (let ii = 0; ii < clipOutputLength; ii += 2, s += stride, uvIndex += 2) {
|
||||
const x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
|
||||
clippedVerticesItems[s] = x;
|
||||
clippedVerticesItems[s + 1] = y;
|
||||
@ -388,8 +396,8 @@ export class SkeletonClipping {
|
||||
const a = (d0 * c0 + d1 * c1) * d;
|
||||
const b = (d4 * c0 + d2 * c1) * d;
|
||||
const c = 1 - a - b;
|
||||
clippedUVsItems[s] = u1 * a + u2 * b + u3 * c;
|
||||
clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c;
|
||||
clippedUVsItems[uvIndex] = u1 * a + u2 * b + u3 * c;
|
||||
clippedUVsItems[uvIndex + 1] = v1 * a + v2 * b + v3 * c;
|
||||
}
|
||||
|
||||
s = this.clippedTrianglesLength;
|
||||
@ -411,7 +419,7 @@ export class SkeletonClipping {
|
||||
|
||||
} else {
|
||||
|
||||
let newLength = s + 3 * 2;
|
||||
let newLength = s + 3 * stride;
|
||||
if (clippedVertices.length < newLength) {
|
||||
this._clippedVerticesTyped = new Float32Array(newLength * 2);
|
||||
this._clippedVerticesTyped.set(clippedVertices.subarray(0, s));
|
||||
@ -419,25 +427,27 @@ export class SkeletonClipping {
|
||||
}
|
||||
clippedVertices[s] = x1;
|
||||
clippedVertices[s + 1] = y1;
|
||||
clippedVertices[s + 2] = x2;
|
||||
clippedVertices[s + 3] = y2;
|
||||
clippedVertices[s + 4] = x3;
|
||||
clippedVertices[s + 5] = y3;
|
||||
clippedVertices[s + stride] = x2;
|
||||
clippedVertices[s + stride + 1] = y2;
|
||||
clippedVertices[s + stride * 2] = x3;
|
||||
clippedVertices[s + stride * 2 + 1] = y3;
|
||||
|
||||
if (clippedUVs.length < newLength) {
|
||||
this._clippedUVsTyped = new Float32Array(newLength * 2);
|
||||
this._clippedUVsTyped.set(clippedUVs.subarray(0, s));
|
||||
let uvLength = this.clippedUVsLength + 3 * 2;
|
||||
if (clippedUVs.length < uvLength) {
|
||||
this._clippedUVsTyped = new Float32Array(uvLength * 2);
|
||||
this._clippedUVsTyped.set(clippedUVs.subarray(0, this.clippedUVsLength));
|
||||
clippedUVs = this._clippedUVsTyped;
|
||||
}
|
||||
clippedUVs[s] = u1;
|
||||
clippedUVs[s + 1] = v1;
|
||||
clippedUVs[s + 2] = u2;
|
||||
clippedUVs[s + 3] = v2;
|
||||
clippedUVs[s + 4] = u3;
|
||||
clippedUVs[s + 5] = v3;
|
||||
let uvIndex = this.clippedUVsLength;
|
||||
clippedUVs[uvIndex] = u1;
|
||||
clippedUVs[uvIndex + 1] = v1;
|
||||
clippedUVs[uvIndex + 2] = u2;
|
||||
clippedUVs[uvIndex + 3] = v2;
|
||||
clippedUVs[uvIndex + 4] = u3;
|
||||
clippedUVs[uvIndex + 5] = v3;
|
||||
|
||||
this.clippedVerticesLength = newLength;
|
||||
this.clippedUVsLength = newLength;
|
||||
this.clippedUVsLength = uvLength;
|
||||
|
||||
s = this.clippedTrianglesLength;
|
||||
newLength = s + 3;
|
||||
|
||||
@ -40,7 +40,7 @@ export class SkeletonRendererCore {
|
||||
private clipping = new SkeletonClipping();
|
||||
private renderCommands: RenderCommand[] = [];
|
||||
|
||||
render (skeleton: Skeleton, pma = false, inColor?: [number, number, number, number]): RenderCommand | undefined {
|
||||
render (skeleton: Skeleton, pma = false, inColor?: [number, number, number, number], stride = 2): RenderCommand | undefined {
|
||||
this.commandPool.reset();
|
||||
this.renderCommands.length = 0;
|
||||
|
||||
@ -80,7 +80,7 @@ export class SkeletonRendererCore {
|
||||
continue;
|
||||
}
|
||||
|
||||
attachment.computeWorldVertices(slot, this.worldVertices, 0, 2);
|
||||
attachment.computeWorldVertices(slot, this.worldVertices, 0, stride);
|
||||
vertices = this.worldVertices;
|
||||
verticesCount = 4;
|
||||
uvs = attachment.uvs as Float32Array;
|
||||
@ -99,7 +99,7 @@ export class SkeletonRendererCore {
|
||||
if (this.worldVertices.length < attachment.worldVerticesLength)
|
||||
this.worldVertices = new Float32Array(attachment.worldVerticesLength);
|
||||
|
||||
attachment.computeWorldVertices(skeleton, slot, 0, attachment.worldVerticesLength, this.worldVertices, 0, 2);
|
||||
attachment.computeWorldVertices(skeleton, slot, 0, attachment.worldVerticesLength, this.worldVertices, 0, stride);
|
||||
vertices = this.worldVertices;
|
||||
verticesCount = attachment.worldVerticesLength >> 1;
|
||||
uvs = attachment.uvs as Float32Array;
|
||||
@ -163,19 +163,19 @@ export class SkeletonRendererCore {
|
||||
}
|
||||
|
||||
if (clipper.isClipping()) {
|
||||
clipper.clipTrianglesUnpacked(vertices, indices, indicesCount, uvs);
|
||||
clipper.clipTrianglesUnpacked(vertices, indices, indicesCount, uvs, stride);
|
||||
vertices = clipper.clippedVerticesTyped;
|
||||
verticesCount = clipper.clippedVerticesLength >> 1;
|
||||
verticesCount = clipper.clippedVerticesLength / stride;
|
||||
uvs = clipper.clippedUVsTyped;
|
||||
indices = clipper.clippedTrianglesTyped;
|
||||
indicesCount = clipper.clippedTrianglesLength;
|
||||
}
|
||||
|
||||
const cmd = this.commandPool.getCommand(verticesCount, indicesCount);
|
||||
const cmd = this.commandPool.getCommand(verticesCount, indicesCount, stride);
|
||||
cmd.blendMode = slot.data.blendMode;
|
||||
cmd.texture = texture;
|
||||
|
||||
cmd.positions.set(vertices.subarray(0, verticesCount << 1));
|
||||
cmd.positions.set(vertices.subarray(0, verticesCount * stride));
|
||||
cmd.uvs.set(uvs.subarray(0, verticesCount << 1));
|
||||
|
||||
for (let j = 0; j < verticesCount; j++) {
|
||||
@ -194,14 +194,14 @@ export class SkeletonRendererCore {
|
||||
}
|
||||
|
||||
clipper.clipEnd();
|
||||
return this.batchCommands();
|
||||
return this.batchCommands(stride);
|
||||
}
|
||||
|
||||
private batchSubCommands (commands: RenderCommand[], first: number, last: number,
|
||||
numVertices: number, numIndices: number): RenderCommand {
|
||||
numVertices: number, numIndices: number, stride: number): RenderCommand {
|
||||
|
||||
const firstCmd = commands[first];
|
||||
const batched = this.commandPool.getCommand(numVertices, numIndices);
|
||||
const batched = this.commandPool.getCommand(numVertices, numIndices, stride);
|
||||
|
||||
batched.blendMode = firstCmd.blendMode;
|
||||
batched.texture = firstCmd.texture;
|
||||
@ -216,7 +216,7 @@ export class SkeletonRendererCore {
|
||||
const cmd = commands[i];
|
||||
|
||||
batched.positions.set(cmd.positions, positionsOffset);
|
||||
positionsOffset += cmd.numVertices << 1;
|
||||
positionsOffset += cmd.numVertices * stride;
|
||||
|
||||
batched.uvs.set(cmd.uvs, uvsOffset);
|
||||
uvsOffset += cmd.numVertices << 1;
|
||||
@ -236,7 +236,7 @@ export class SkeletonRendererCore {
|
||||
return batched;
|
||||
}
|
||||
|
||||
private batchCommands (): RenderCommand | undefined {
|
||||
private batchCommands (stride: number): RenderCommand | undefined {
|
||||
if (this.renderCommands.length === 0) return undefined;
|
||||
|
||||
let root: RenderCommand | undefined;
|
||||
@ -267,7 +267,7 @@ export class SkeletonRendererCore {
|
||||
numIndices += cmd.numIndices;
|
||||
} else {
|
||||
const batched = this.batchSubCommands(this.renderCommands, startIndex, i - 1,
|
||||
numVertices, numIndices);
|
||||
numVertices, numIndices, stride);
|
||||
|
||||
if (!last) {
|
||||
root = last = batched;
|
||||
@ -315,17 +315,17 @@ class CommandPool {
|
||||
private pool: RenderCommand[] = [];
|
||||
private inUse: RenderCommand[] = [];
|
||||
|
||||
getCommand (numVertices: number, numIndices: number): RenderCommand {
|
||||
getCommand (numVertices: number, numIndices: number, stride: number): RenderCommand {
|
||||
let cmd: RenderCommand | undefined;
|
||||
for (const c of this.pool) {
|
||||
if (c._positions.length >= numVertices << 1 && c._indices.length >= numIndices) {
|
||||
if (c._positions.length >= numVertices * stride && c._indices.length >= numIndices) {
|
||||
cmd = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmd) {
|
||||
const _positions = new Float32Array(numVertices << 1);
|
||||
const _positions = new Float32Array(numVertices * stride);
|
||||
const _uvs = new Float32Array(numVertices << 1);
|
||||
const _colors = new Uint32Array(numVertices);
|
||||
const _darkColors = new Uint32Array(numVertices);
|
||||
@ -352,8 +352,8 @@ class CommandPool {
|
||||
cmd.numVertices = numVertices;
|
||||
cmd.numIndices = numIndices;
|
||||
|
||||
cmd.positions = cmd._positions.subarray(0, numVertices << 1);
|
||||
cmd.uvs = cmd._uvs.subarray(0, numVertices * 2);
|
||||
cmd.positions = cmd._positions.subarray(0, numVertices * stride);
|
||||
cmd.uvs = cmd._uvs.subarray(0, numVertices << 1);
|
||||
cmd.colors = cmd._colors.subarray(0, numVertices);
|
||||
cmd.darkColors = cmd._darkColors.subarray(0, numVertices);
|
||||
cmd.indices = cmd._indices.subarray(0, numIndices);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user