[ts] Port of commit 68d262b5: Improved rendering performance when an attachment is fully inside a clipping attachment.

Reuse the original vertices rather than clipping's triangle soup.
Removed deprecated clipping methods.
This commit is contained in:
Davide Tantillo 2025-04-01 15:19:21 +02:00
parent 8711e55672
commit 457be16385
5 changed files with 77 additions and 134 deletions

View File

@ -652,8 +652,7 @@ export class Skeleton {
continue;
}
if (vertices && triangles) {
if (clipper != null && clipper.isClipping()) {
clipper.clipTriangles(vertices, triangles, triangles.length);
if (clipper != null && clipper.isClipping() && clipper.clipTriangles(vertices, triangles, triangles.length)) {
vertices = clipper.clippedVertices;
verticesLength = clipper.clippedVertices.length;
}

View File

@ -81,59 +81,18 @@ export class SkeletonClipping {
return this.clipAttachment != null;
}
/**
* @deprecated Use clipTriangles without verticesLength parameter. Mark for removal in 4.3.
*/
clipTriangles (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number): void;
clipTriangles (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number): boolean;
clipTriangles (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number,
uvs: NumberArrayLike, light: Color, dark: Color, twoColor: boolean, stride: number): boolean;
clipTriangles (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number,
uvs?: NumberArrayLike, light?: Color, dark?: Color, twoColor?: boolean, stride?: number): boolean {
/**
* @deprecated Use clipTriangles without verticesLength parameter. Mark for removal in 4.3.
*/
clipTriangles (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike, light: Color, dark: Color, twoColor: boolean): void;
clipTriangles (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number): void;
clipTriangles (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike, light: Color, dark: Color, twoColor: boolean): void;
clipTriangles (
vertices: NumberArrayLike,
verticesLengthOrTriangles: number | NumberArrayLike,
trianglesOrTrianglesLength: NumberArrayLike | number,
trianglesLengthOrUvs?: number | NumberArrayLike,
uvsOrLight?: NumberArrayLike | Color,
lightOrDark?: Color,
darkOrTwoColor?: Color | boolean,
twoColorParam?: boolean
): void {
// Determine which overload is being used
let triangles: NumberArrayLike;
let trianglesLength: number;
let uvs: NumberArrayLike | undefined;
let light: Color | undefined;
let dark: Color | undefined;
let twoColor: boolean | undefined;
if (typeof verticesLengthOrTriangles === 'number') {
triangles = trianglesOrTrianglesLength as NumberArrayLike;
trianglesLength = trianglesLengthOrUvs as number;
uvs = uvsOrLight as NumberArrayLike;
light = lightOrDark as Color | undefined;
dark = darkOrTwoColor as Color | undefined;
twoColor = twoColorParam;
} else {
triangles = verticesLengthOrTriangles;
trianglesLength = trianglesOrTrianglesLength as number;
uvs = trianglesLengthOrUvs as NumberArrayLike;
light = uvsOrLight as Color | undefined;
dark = lightOrDark as Color | undefined;
twoColor = darkOrTwoColor as boolean;
}
if (uvs && light && dark && typeof twoColor === 'boolean')
this.clipTrianglesRender(vertices, triangles, trianglesLength, uvs, light, dark, twoColor);
else
this.clipTrianglesNoRender(vertices, triangles, trianglesLength);
return (uvs && light && dark && typeof twoColor === 'boolean' && typeof stride === 'number')
? this.clipTrianglesRender(vertices, triangles, trianglesLength, uvs, light, dark, twoColor, stride)
: this.clipTrianglesNoRender(vertices, triangles, trianglesLength);
}
private clipTrianglesNoRender (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number) {
private clipTrianglesNoRender (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number): boolean {
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
let clippedTriangles = this.clippedTriangles;
@ -143,24 +102,25 @@ export class SkeletonClipping {
let index = 0;
clippedVertices.length = 0;
clippedTriangles.length = 0;
let clipOutputItems = null;
for (let i = 0; i < trianglesLength; i += 3) {
let vertexOffset = triangles[i] << 1;
let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
let v = triangles[i] << 1;
let x1 = vertices[v], y1 = vertices[v + 1];
vertexOffset = triangles[i + 1] << 1;
let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
v = triangles[i + 1] << 1;
let x2 = vertices[v], y2 = vertices[v + 1];
vertexOffset = triangles[i + 2] << 1;
let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
v = triangles[i + 2] << 1;
let x3 = vertices[v], y3 = vertices[v + 1];
for (let p = 0; p < polygonsCount; p++) {
let s = clippedVertices.length;
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
clipOutputItems = this.clipOutput;
let clipOutputLength = clipOutput.length;
if (clipOutputLength == 0) continue;
let clipOutputCount = clipOutputLength >> 1;
let clipOutputItems = this.clipOutput;
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * 2);
for (let ii = 0; ii < clipOutputLength; ii += 2, s += 2) {
let x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
@ -199,45 +159,46 @@ export class SkeletonClipping {
}
}
}
return clipOutputItems != null;
}
private clipTrianglesRender (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike,
light: Color, dark: Color, twoColor: boolean) {
light: Color, dark: Color, twoColor: boolean, stride: number): boolean {
let clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
let clippedTriangles = this.clippedTriangles;
let polygons = this.clippingPolygons!;
let polygonsCount = polygons.length;
let vertexSize = twoColor ? 12 : 8;
let index = 0;
clippedVertices.length = 0;
clippedTriangles.length = 0;
let clipOutputItems = null;
for (let i = 0; i < trianglesLength; i += 3) {
let vertexOffset = triangles[i] << 1;
let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
let u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1];
let t = triangles[i];
let u1 = uvs[t << 1], v1 = uvs[(t << 1) + 1];
let x1 = vertices[t * stride], y1 = vertices[t * stride + 1];
vertexOffset = triangles[i + 1] << 1;
let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
let u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1];
t = triangles[i + 1];
let u2 = uvs[t << 1], v2 = uvs[(t << 1) + 1];
let x2 = vertices[t * stride], y2 = vertices[t * stride + 1];
vertexOffset = triangles[i + 2] << 1;
let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
let u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1];
t = triangles[i + 2];
let u3 = uvs[t << 1], v3 = uvs[(t << 1) + 1];
let x3 = vertices[t * stride], y3 = vertices[t * stride + 1];
for (let p = 0; p < polygonsCount; p++) {
let s = clippedVertices.length;
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
clipOutputItems = this.clipOutput;
let clipOutputLength = clipOutput.length;
if (clipOutputLength == 0) continue;
let d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
let d = 1 / (d0 * d2 + d1 * (y1 - y3));
let clipOutputCount = clipOutputLength >> 1;
let clipOutputItems = this.clipOutput;
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * vertexSize);
for (let ii = 0; ii < clipOutputLength; ii += 2, s += vertexSize) {
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * stride);
for (let ii = 0; ii < clipOutputLength; ii += 2, s += stride) {
let x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
clippedVerticesItems[s] = x;
clippedVerticesItems[s + 1] = y;
@ -270,7 +231,7 @@ export class SkeletonClipping {
index += clipOutputCount + 1;
} else {
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * vertexSize);
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * stride);
clippedVerticesItems[s] = x1;
clippedVerticesItems[s + 1] = y1;
clippedVerticesItems[s + 2] = light.r;
@ -343,6 +304,7 @@ export class SkeletonClipping {
}
}
}
return clipOutputItems != null;
}
public clipTrianglesUnpacked (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike) {
@ -356,17 +318,17 @@ export class SkeletonClipping {
clippedUVs.length = 0;
clippedTriangles.length = 0;
for (let i = 0; i < trianglesLength; i += 3) {
let vertexOffset = triangles[i] << 1;
let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1];
let u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1];
let v = triangles[i] << 1;
let x1 = vertices[v], y1 = vertices[v + 1];
let u1 = uvs[v], v1 = uvs[v + 1];
vertexOffset = triangles[i + 1] << 1;
let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1];
let u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1];
v = triangles[i + 1] << 1;
let x2 = vertices[v], y2 = vertices[v + 1];
let u2 = uvs[v], v2 = uvs[v + 1];
vertexOffset = triangles[i + 2] << 1;
let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1];
let u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1];
v = triangles[i + 2] << 1;
let x3 = vertices[v], y3 = vertices[v + 1];
let u3 = uvs[v], v3 = uvs[v + 1];
for (let p = 0; p < polygonsCount; p++) {
let s = clippedVertices.length;
@ -433,7 +395,7 @@ export class SkeletonClipping {
/** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping
* area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */
clip (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, clippingArea: Array<number>, output: Array<number>) {
private clip (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, clippingArea: Array<number>, output: Array<number>) {
let originalOutput = output;
let clipped = false;

View File

@ -659,7 +659,7 @@ export class Spine extends Container {
}
const useDarkColor = slot.darkColor != null;
const vertexSize = Spine.clipper.isClipping() ? 2 : useDarkColor ? Spine.DARK_VERTEX_SIZE : Spine.VERTEX_SIZE;
const vertexSize = useDarkColor ? Spine.DARK_VERTEX_SIZE : Spine.VERTEX_SIZE;
if (!slot.bone.active) {
Spine.clipper.clipEndWithSlot(slot);
this.pixiMaskCleanup(slot);
@ -728,9 +728,7 @@ export class Spine extends Container {
let finalIndices: NumberArrayLike;
let finalIndicesLength: number;
if (Spine.clipper.isClipping()) {
Spine.clipper.clipTriangles(this.verticesCache, triangles, triangles.length, uvs, this.lightColor, this.darkColor, useDarkColor);
if (Spine.clipper.isClipping() && Spine.clipper.clipTriangles(this.verticesCache, triangles, triangles.length, uvs, this.lightColor, this.darkColor, useDarkColor, vertexSize)) {
finalVertices = Spine.clipper.clippedVertices;
finalVerticesLength = finalVertices.length;

View File

@ -101,7 +101,7 @@ export class SkeletonMesh extends THREE.Object3D {
static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
static VERTEX_SIZE = 2 + 2 + 4;
private vertexSize = 2 + 2 + 4;
private twoColorTint;
private twoColorTint: boolean;
private vertices = Utils.newFloatArray(1024);
private tempColor = new Color();
@ -231,8 +231,8 @@ export class SkeletonMesh extends THREE.Object3D {
batch.begin();
let z = 0;
let zOffset = this.zOffset;
let vertexSize = this.vertexSize;
for (let i = 0, n = drawOrder.length; i < n; i++) {
let vertexSize = clipper.isClipping() ? 2 : this.vertexSize;
let slot = drawOrder[i];
if (!slot.bone.active) {
clipper.clipEndWithSlot(slot);
@ -243,36 +243,33 @@ export class SkeletonMesh extends THREE.Object3D {
let texture: ThreeJsTexture | null;
let numFloats = 0;
if (attachment instanceof RegionAttachment) {
let region = <RegionAttachment>attachment;
attachmentColor = region.color;
attachmentColor = attachment.color;
vertices = this.vertices;
numFloats = vertexSize * 4;
region.computeWorldVertices(slot, vertices, 0, vertexSize);
attachment.computeWorldVertices(slot, vertices, 0, vertexSize);
triangles = SkeletonMesh.QUAD_TRIANGLES;
uvs = region.uvs;
texture = <ThreeJsTexture>region.region!.texture;
uvs = attachment.uvs;
texture = <ThreeJsTexture>attachment.region!.texture;
} else if (attachment instanceof MeshAttachment) {
let mesh = <MeshAttachment>attachment;
attachmentColor = mesh.color;
attachmentColor = attachment.color;
vertices = this.vertices;
numFloats = (mesh.worldVerticesLength >> 1) * vertexSize;
numFloats = (attachment.worldVerticesLength >> 1) * vertexSize;
if (numFloats > vertices.length) {
vertices = this.vertices = Utils.newFloatArray(numFloats);
}
mesh.computeWorldVertices(
attachment.computeWorldVertices(
slot,
0,
mesh.worldVerticesLength,
attachment.worldVerticesLength,
vertices,
0,
vertexSize
);
triangles = mesh.triangles;
uvs = mesh.uvs;
texture = <ThreeJsTexture>mesh.region!.texture;
triangles = attachment.triangles;
uvs = attachment.uvs;
texture = <ThreeJsTexture>attachment.region!.texture;
} else if (attachment instanceof ClippingAttachment) {
let clip = <ClippingAttachment>attachment;
clipper.clipStart(slot, clip);
clipper.clipStart(slot, attachment);
continue;
} else {
clipper.clipEndWithSlot(slot);
@ -307,16 +304,7 @@ export class SkeletonMesh extends THREE.Object3D {
let finalIndices: NumberArrayLike;
let finalIndicesLength: number;
if (clipper.isClipping()) {
clipper.clipTriangles(
vertices,
triangles,
triangles.length,
uvs,
color,
tempLight,
this.twoColorTint,
);
if (clipper.isClipping() && clipper.clipTriangles( vertices, triangles, triangles.length, uvs, color, tempLight, this.twoColorTint, vertexSize)) {
let clippedVertices = clipper.clippedVertices;
let clippedTriangles = clipper.clippedTriangles;
finalVertices = clippedVertices;

View File

@ -78,7 +78,6 @@ export class SkeletonRenderer {
let inRange = false;
if (slotRangeStart == -1) inRange = true;
for (let i = 0, n = drawOrder.length; i < n; i++) {
let clippedVertexSize = clipper.isClipping() ? 2 : vertexSize;
let slot = drawOrder[i];
if (!slot.bone.active) {
clipper.clipEndWithSlot(slot);
@ -101,31 +100,29 @@ export class SkeletonRenderer {
let attachment = slot.getAttachment();
let texture: GLTexture;
if (attachment instanceof RegionAttachment) {
let region = <RegionAttachment>attachment;
renderable.vertices = this.vertices;
renderable.numVertices = 4;
renderable.numFloats = clippedVertexSize << 2;
region.computeWorldVertices(slot, renderable.vertices, 0, clippedVertexSize);
renderable.numFloats = vertexSize << 2;
attachment.computeWorldVertices(slot, renderable.vertices, 0, vertexSize);
triangles = SkeletonRenderer.QUAD_TRIANGLES;
uvs = region.uvs;
texture = <GLTexture>region.region!.texture;
attachmentColor = region.color;
uvs = attachment.uvs;
texture = <GLTexture>attachment.region!.texture;
attachmentColor = attachment.color;
} else if (attachment instanceof MeshAttachment) {
let mesh = <MeshAttachment>attachment;
renderable.vertices = this.vertices;
renderable.numVertices = (mesh.worldVerticesLength >> 1);
renderable.numFloats = renderable.numVertices * clippedVertexSize;
renderable.numVertices = (attachment.worldVerticesLength >> 1);
renderable.numFloats = renderable.numVertices * vertexSize;
if (renderable.numFloats > renderable.vertices.length) {
renderable.vertices = this.vertices = Utils.newFloatArray(renderable.numFloats);
}
mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, renderable.vertices, 0, clippedVertexSize);
triangles = mesh.triangles;
texture = <GLTexture>mesh.region!.texture;
uvs = mesh.uvs;
attachmentColor = mesh.color;
attachment.computeWorldVertices(slot, 0, attachment.worldVerticesLength, renderable.vertices, 0, vertexSize);
triangles = attachment.triangles;
texture = <GLTexture>attachment.region!.texture;
uvs = attachment.uvs;
attachmentColor = attachment.color;
} else if (attachment instanceof ClippingAttachment) {
let clip = <ClippingAttachment>(attachment);
clipper.clipStart(slot, clip);
clipper.clipStart(slot, attachment);
continue;
} else {
clipper.clipEndWithSlot(slot);
@ -164,8 +161,7 @@ export class SkeletonRenderer {
batcher.setBlendMode(blendMode, premultipliedAlpha);
}
if (clipper.isClipping()) {
clipper.clipTriangles(renderable.vertices, triangles, triangles.length, uvs, finalColor, darkColor, twoColorTint);
if (clipper.isClipping() && clipper.clipTriangles(renderable.vertices, triangles, triangles.length, uvs, finalColor, darkColor, twoColorTint, vertexSize)) {
let clippedVertices = new Float32Array(clipper.clippedVertices);
let clippedTriangles = clipper.clippedTriangles;
if (transformer) transformer(clippedVertices, clippedVertices.length, vertexSize);