[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; continue;
} }
if (vertices && triangles) { if (vertices && triangles) {
if (clipper != null && clipper.isClipping()) { if (clipper != null && clipper.isClipping() && clipper.clipTriangles(vertices, triangles, triangles.length)) {
clipper.clipTriangles(vertices, triangles, triangles.length);
vertices = clipper.clippedVertices; vertices = clipper.clippedVertices;
verticesLength = clipper.clippedVertices.length; verticesLength = clipper.clippedVertices.length;
} }

View File

@ -81,59 +81,18 @@ export class SkeletonClipping {
return this.clipAttachment != null; return this.clipAttachment != null;
} }
/** clipTriangles (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number): boolean;
* @deprecated Use clipTriangles without verticesLength parameter. Mark for removal in 4.3. clipTriangles (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number,
*/ uvs: NumberArrayLike, light: Color, dark: Color, twoColor: boolean, stride: number): boolean;
clipTriangles (vertices: NumberArrayLike, verticesLength: number, triangles: NumberArrayLike, trianglesLength: number): void; clipTriangles (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number,
uvs?: NumberArrayLike, light?: Color, dark?: Color, twoColor?: boolean, stride?: number): boolean {
/** return (uvs && light && dark && typeof twoColor === 'boolean' && typeof stride === 'number')
* @deprecated Use clipTriangles without verticesLength parameter. Mark for removal in 4.3. ? this.clipTrianglesRender(vertices, triangles, trianglesLength, uvs, light, dark, twoColor, stride)
*/ : this.clipTrianglesNoRender(vertices, triangles, trianglesLength);
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);
} }
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 clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
let clippedTriangles = this.clippedTriangles; let clippedTriangles = this.clippedTriangles;
@ -143,24 +102,25 @@ export class SkeletonClipping {
let index = 0; let index = 0;
clippedVertices.length = 0; clippedVertices.length = 0;
clippedTriangles.length = 0; clippedTriangles.length = 0;
let clipOutputItems = null;
for (let i = 0; i < trianglesLength; i += 3) { for (let i = 0; i < trianglesLength; i += 3) {
let vertexOffset = triangles[i] << 1; let v = triangles[i] << 1;
let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; let x1 = vertices[v], y1 = vertices[v + 1];
vertexOffset = triangles[i + 1] << 1; v = triangles[i + 1] << 1;
let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; let x2 = vertices[v], y2 = vertices[v + 1];
vertexOffset = triangles[i + 2] << 1; v = triangles[i + 2] << 1;
let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; let x3 = vertices[v], y3 = vertices[v + 1];
for (let p = 0; p < polygonsCount; p++) { for (let p = 0; p < polygonsCount; p++) {
let s = clippedVertices.length; let s = clippedVertices.length;
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) { if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
clipOutputItems = this.clipOutput;
let clipOutputLength = clipOutput.length; let clipOutputLength = clipOutput.length;
if (clipOutputLength == 0) continue; if (clipOutputLength == 0) continue;
let clipOutputCount = clipOutputLength >> 1; let clipOutputCount = clipOutputLength >> 1;
let clipOutputItems = this.clipOutput;
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * 2); let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * 2);
for (let ii = 0; ii < clipOutputLength; ii += 2, s += 2) { for (let ii = 0; ii < clipOutputLength; ii += 2, s += 2) {
let x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; 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, 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 clipOutput = this.clipOutput, clippedVertices = this.clippedVertices;
let clippedTriangles = this.clippedTriangles; let clippedTriangles = this.clippedTriangles;
let polygons = this.clippingPolygons!; let polygons = this.clippingPolygons!;
let polygonsCount = polygons.length; let polygonsCount = polygons.length;
let vertexSize = twoColor ? 12 : 8;
let index = 0; let index = 0;
clippedVertices.length = 0; clippedVertices.length = 0;
clippedTriangles.length = 0; clippedTriangles.length = 0;
let clipOutputItems = null;
for (let i = 0; i < trianglesLength; i += 3) { for (let i = 0; i < trianglesLength; i += 3) {
let vertexOffset = triangles[i] << 1; let t = triangles[i];
let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; let u1 = uvs[t << 1], v1 = uvs[(t << 1) + 1];
let u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; let x1 = vertices[t * stride], y1 = vertices[t * stride + 1];
vertexOffset = triangles[i + 1] << 1; t = triangles[i + 1];
let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; let u2 = uvs[t << 1], v2 = uvs[(t << 1) + 1];
let u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; let x2 = vertices[t * stride], y2 = vertices[t * stride + 1];
vertexOffset = triangles[i + 2] << 1; t = triangles[i + 2];
let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; let u3 = uvs[t << 1], v3 = uvs[(t << 1) + 1];
let u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; let x3 = vertices[t * stride], y3 = vertices[t * stride + 1];
for (let p = 0; p < polygonsCount; p++) { for (let p = 0; p < polygonsCount; p++) {
let s = clippedVertices.length; let s = clippedVertices.length;
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) { if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) {
clipOutputItems = this.clipOutput;
let clipOutputLength = clipOutput.length; let clipOutputLength = clipOutput.length;
if (clipOutputLength == 0) continue; if (clipOutputLength == 0) continue;
let d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; let d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1;
let d = 1 / (d0 * d2 + d1 * (y1 - y3)); let d = 1 / (d0 * d2 + d1 * (y1 - y3));
let clipOutputCount = clipOutputLength >> 1; let clipOutputCount = clipOutputLength >> 1;
let clipOutputItems = this.clipOutput; let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * stride);
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + clipOutputCount * vertexSize); for (let ii = 0; ii < clipOutputLength; ii += 2, s += stride) {
for (let ii = 0; ii < clipOutputLength; ii += 2, s += vertexSize) {
let x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; let x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
clippedVerticesItems[s] = x; clippedVerticesItems[s] = x;
clippedVerticesItems[s + 1] = y; clippedVerticesItems[s + 1] = y;
@ -270,7 +231,7 @@ export class SkeletonClipping {
index += clipOutputCount + 1; index += clipOutputCount + 1;
} else { } else {
let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * vertexSize); let clippedVerticesItems = Utils.setArraySize(clippedVertices, s + 3 * stride);
clippedVerticesItems[s] = x1; clippedVerticesItems[s] = x1;
clippedVerticesItems[s + 1] = y1; clippedVerticesItems[s + 1] = y1;
clippedVerticesItems[s + 2] = light.r; 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) { public clipTrianglesUnpacked (vertices: NumberArrayLike, triangles: NumberArrayLike, trianglesLength: number, uvs: NumberArrayLike) {
@ -356,17 +318,17 @@ export class SkeletonClipping {
clippedUVs.length = 0; clippedUVs.length = 0;
clippedTriangles.length = 0; clippedTriangles.length = 0;
for (let i = 0; i < trianglesLength; i += 3) { for (let i = 0; i < trianglesLength; i += 3) {
let vertexOffset = triangles[i] << 1; let v = triangles[i] << 1;
let x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; let x1 = vertices[v], y1 = vertices[v + 1];
let u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; let u1 = uvs[v], v1 = uvs[v + 1];
vertexOffset = triangles[i + 1] << 1; v = triangles[i + 1] << 1;
let x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; let x2 = vertices[v], y2 = vertices[v + 1];
let u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; let u2 = uvs[v], v2 = uvs[v + 1];
vertexOffset = triangles[i + 2] << 1; v = triangles[i + 2] << 1;
let x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; let x3 = vertices[v], y3 = vertices[v + 1];
let u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; let u3 = uvs[v], v3 = uvs[v + 1];
for (let p = 0; p < polygonsCount; p++) { for (let p = 0; p < polygonsCount; p++) {
let s = clippedVertices.length; 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 /** 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. */ * 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 originalOutput = output;
let clipped = false; let clipped = false;

View File

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

View File

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