Ver Fonte

[canvaskit] Fix JSON loading.

Mario Zechner há 1 ano atrás
pai
commit
e9bcaa7a9b

+ 0 - 1
spine-ts/spine-canvaskit/example/index.html

@@ -34,7 +34,6 @@
     canvasElement.width = canvasElement.clientWidth * dpr;
     canvasElement.height = canvasElement.clientHeight * dpr;
 
-
     // Initialize CanvasKit and create a surface from the Canvas element to draw to
     const ck = await CanvasKitInit();
     const surface = ck.MakeCanvasSurface('foo');

+ 260 - 161
spine-ts/spine-canvaskit/src/index.ts

@@ -1,103 +1,170 @@
 export * from "@esotericsoftware/spine-core";
 
-import { AnimationState, AnimationStateData, AtlasAttachmentLoader, BlendMode, ClippingAttachment, Color, MeshAttachment, NumberArrayLike, Physics, RegionAttachment, Skeleton, SkeletonBinary, SkeletonClipping, SkeletonData, SkeletonJson, Texture, TextureAtlas, TextureFilter, TextureWrap, Utils } from "@esotericsoftware/spine-core";
-import { Canvas, Surface, CanvasKit, Image, Paint, Shader, BlendMode as CanvasKitBlendMode } from "canvaskit-wasm";
+import {
+	AnimationState,
+	AnimationStateData,
+	AtlasAttachmentLoader,
+	BlendMode,
+	ClippingAttachment,
+	Color,
+	MeshAttachment,
+	NumberArrayLike,
+	Physics,
+	RegionAttachment,
+	Skeleton,
+	SkeletonBinary,
+	SkeletonClipping,
+	SkeletonData,
+	SkeletonJson,
+	Texture,
+	TextureAtlas,
+	TextureFilter,
+	TextureWrap,
+	Utils,
+} from "@esotericsoftware/spine-core";
+import {
+	Canvas,
+	Surface,
+	CanvasKit,
+	Image,
+	Paint,
+	Shader,
+	BlendMode as CanvasKitBlendMode,
+} from "canvaskit-wasm";
 
 Skeleton.yDown = true;
 
-type CanvasKitImage = { shaders: Shader[], paintPerBlendMode: Map<BlendMode, Paint>, image: Image };
+type CanvasKitImage = {
+	shaders: Shader[];
+	paintPerBlendMode: Map<BlendMode, Paint>;
+	image: Image;
+};
 
 // CanvasKit blend modes for premultiplied alpha
-function toCkBlendMode(ck: CanvasKit, blendMode: BlendMode) {
-    switch(blendMode) {
-        case BlendMode.Normal: return ck.BlendMode.SrcOver;
-        case BlendMode.Additive: return ck.BlendMode.Plus;
-        case BlendMode.Multiply: return ck.BlendMode.SrcOver;
-        case BlendMode.Screen: return ck.BlendMode.Screen;
-        default: return ck.BlendMode.SrcOver;
-    }
+function toCkBlendMode (ck: CanvasKit, blendMode: BlendMode) {
+	switch (blendMode) {
+		case BlendMode.Normal:
+			return ck.BlendMode.SrcOver;
+		case BlendMode.Additive:
+			return ck.BlendMode.Plus;
+		case BlendMode.Multiply:
+			return ck.BlendMode.SrcOver;
+		case BlendMode.Screen:
+			return ck.BlendMode.Screen;
+		default:
+			return ck.BlendMode.SrcOver;
+	}
 }
 
-function bufferToUtf8String(buffer: any) {
-    if (typeof Buffer !== 'undefined') {
-        return buffer.toString('utf-8');
-      } else if (typeof TextDecoder !== 'undefined') {
-        return new TextDecoder('utf-8').decode(buffer);
-      } else {
-        throw new Error('Unsupported environment');
-      }
+function bufferToUtf8String (buffer: any) {
+	if (typeof Buffer !== "undefined") {
+		return buffer.toString("utf-8");
+	} else if (typeof TextDecoder !== "undefined") {
+		return new TextDecoder("utf-8").decode(buffer);
+	} else {
+		throw new Error("Unsupported environment");
+	}
 }
 
 class CanvasKitTexture extends Texture {
-    getImage(): CanvasKitImage {
-        return this._image;
-    }
-
-    setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void {
-    }
-
-    setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void {
-    }
-
-    dispose(): void {
-        const data: CanvasKitImage = this._image;
-        for (const paint of data.paintPerBlendMode.values()) {
-            paint.delete();
-        }
-        for (const shader of data.shaders) {
-            shader.delete();
-        }
-        data.image.delete();
-        this._image = null;
-    }
-
-    static async fromFile(ck: CanvasKit, path: string, readFile: (path: string) => Promise<any>): Promise<CanvasKitTexture> {
-        const imgData = await readFile(path);
-        if (!imgData) throw new Error(`Could not load image ${path}`);
-        const image = ck.MakeImageFromEncoded(imgData);
-        if (!image) throw new Error(`Could not load image ${path}`);
-        const paintPerBlendMode = new Map<BlendMode, Paint>();
-        const shaders: Shader[] = [];
-        for (const blendMode of [BlendMode.Normal, BlendMode.Additive, BlendMode.Multiply, BlendMode.Screen]) {
-            const paint = new ck.Paint();
-            const shader = image.makeShaderOptions(ck.TileMode.Clamp, ck.TileMode.Clamp, ck.FilterMode.Linear, ck.MipmapMode.Linear);
-            paint.setShader(shader);
-            paint.setBlendMode(toCkBlendMode(ck, blendMode));
-            paintPerBlendMode.set(blendMode, paint);
-            shaders.push(shader);
-        }
-        return new CanvasKitTexture({ shaders, paintPerBlendMode, image });
-    }
+	getImage (): CanvasKitImage {
+		return this._image;
+	}
+
+	setFilters (minFilter: TextureFilter, magFilter: TextureFilter): void { }
+
+	setWraps (uWrap: TextureWrap, vWrap: TextureWrap): void { }
+
+	dispose (): void {
+		const data: CanvasKitImage = this._image;
+		for (const paint of data.paintPerBlendMode.values()) {
+			paint.delete();
+		}
+		for (const shader of data.shaders) {
+			shader.delete();
+		}
+		data.image.delete();
+		this._image = null;
+	}
+
+	static async fromFile (
+		ck: CanvasKit,
+		path: string,
+		readFile: (path: string) => Promise<any>
+	): Promise<CanvasKitTexture> {
+		const imgData = await readFile(path);
+		if (!imgData) throw new Error(`Could not load image ${path}`);
+		const image = ck.MakeImageFromEncoded(imgData);
+		if (!image) throw new Error(`Could not load image ${path}`);
+		const paintPerBlendMode = new Map<BlendMode, Paint>();
+		const shaders: Shader[] = [];
+		for (const blendMode of [
+			BlendMode.Normal,
+			BlendMode.Additive,
+			BlendMode.Multiply,
+			BlendMode.Screen,
+		]) {
+			const paint = new ck.Paint();
+			const shader = image.makeShaderOptions(
+				ck.TileMode.Clamp,
+				ck.TileMode.Clamp,
+				ck.FilterMode.Linear,
+				ck.MipmapMode.Linear
+			);
+			paint.setShader(shader);
+			paint.setBlendMode(toCkBlendMode(ck, blendMode));
+			paintPerBlendMode.set(blendMode, paint);
+			shaders.push(shader);
+		}
+		return new CanvasKitTexture({ shaders, paintPerBlendMode, image });
+	}
 }
 
 /**
  * Loads a {@link TextureAtlas} and its atlas page images from the given file path using the `readFile(path: string): Promise<Buffer>` function.
  * Throws an `Error` if the file or one of the atlas page images could not be loaded.
  */
-export async function loadTextureAtlas(ck: CanvasKit, atlasFile: string, readFile: (path: string) => Promise<Buffer>): Promise<TextureAtlas> {
-    const atlas = new TextureAtlas(bufferToUtf8String(await readFile(atlasFile)));
-    const slashIndex = atlasFile.lastIndexOf("/");
-    const parentDir = slashIndex >= 0 ? atlasFile.substring(0, slashIndex + 1) + "/" : "";
-    for (const page of atlas.pages) {
-        const texture = await CanvasKitTexture.fromFile(ck, parentDir + page.name, readFile);
-        page.setTexture(texture);
-    }
-    return atlas;
+export async function loadTextureAtlas (
+	ck: CanvasKit,
+	atlasFile: string,
+	readFile: (path: string) => Promise<Buffer>
+): Promise<TextureAtlas> {
+	const atlas = new TextureAtlas(bufferToUtf8String(await readFile(atlasFile)));
+	const slashIndex = atlasFile.lastIndexOf("/");
+	const parentDir =
+		slashIndex >= 0 ? atlasFile.substring(0, slashIndex + 1) + "/" : "";
+	for (const page of atlas.pages) {
+		const texture = await CanvasKitTexture.fromFile(
+			ck,
+			parentDir + page.name,
+			readFile
+		);
+		page.setTexture(texture);
+	}
+	return atlas;
 }
 
 /**
  * Loads a {@link SkeletonData} from the given file path (`.json` or `.skel`) using the `readFile(path: string): Promise<Buffer>` function.
  * Attachments will be looked up in the provided atlas.
  */
-export async function loadSkeletonData(skeletonFile: string, atlas: TextureAtlas, readFile: (path: string) => Promise<Buffer>): Promise<SkeletonData> {
-    const attachmentLoader = new AtlasAttachmentLoader(atlas);
-    const loader = skeletonFile.endsWith(".json") ? new SkeletonJson(attachmentLoader) : new SkeletonBinary(attachmentLoader);
-    let data = await readFile(skeletonFile);
-    if (skeletonFile.endsWith(".json")) {
-        data = bufferToUtf8String(data);
-    }
-    const skeletonData = loader.readSkeletonData(data);
-    return skeletonData;
+export async function loadSkeletonData (
+	skeletonFile: string,
+	atlas: TextureAtlas,
+	readFile: (path: string) => Promise<Buffer>,
+	scale = 1
+): Promise<SkeletonData> {
+	const attachmentLoader = new AtlasAttachmentLoader(atlas);
+	const loader = skeletonFile.endsWith(".json")
+		? new SkeletonJson(attachmentLoader)
+		: new SkeletonBinary(attachmentLoader);
+	loader.scale = scale;
+	let data = await readFile(skeletonFile);
+	if (skeletonFile.endsWith(".json")) {
+		data = bufferToUtf8String(data);
+	}
+	const skeletonData = loader.readSkeletonData(data);
+	return skeletonData;
 }
 
 /**
@@ -105,58 +172,60 @@ export async function loadSkeletonData(skeletonFile: string, atlas: TextureAtlas
  * be shared by any number of drawables.
  */
 export class SkeletonDrawable {
-    public readonly skeleton: Skeleton;
-    public readonly animationState: AnimationState;
-
-    /**
-     * Constructs a new drawble from the skeleton data.
-     */
-    constructor(skeletonData: SkeletonData) {
-        this.skeleton = new Skeleton(skeletonData);
-        this.animationState = new AnimationState(new AnimationStateData(skeletonData));
-    }
-
-    /**
-     * Updates the animation state and skeleton time by the delta time. Applies the
-     * animations to the skeleton and calculates the final pose of the skeleton.
-     *
-     * @param deltaTime the time since the last update in seconds
-     * @param physicsUpdate optional {@link Physics} update mode.
-     */
-    update(deltaTime: number, physicsUpdate: Physics = Physics.update) {
-        this.animationState.update(deltaTime);
-        this.skeleton.update(deltaTime);
-        this.animationState.apply(this.skeleton);
-        this.skeleton.updateWorldTransform(physicsUpdate);
-    }
+	public readonly skeleton: Skeleton;
+	public readonly animationState: AnimationState;
+
+	/**
+	 * Constructs a new drawble from the skeleton data.
+	 */
+	constructor (skeletonData: SkeletonData) {
+		this.skeleton = new Skeleton(skeletonData);
+		this.animationState = new AnimationState(
+			new AnimationStateData(skeletonData)
+		);
+	}
+
+	/**
+	 * Updates the animation state and skeleton time by the delta time. Applies the
+	 * animations to the skeleton and calculates the final pose of the skeleton.
+	 *
+	 * @param deltaTime the time since the last update in seconds
+	 * @param physicsUpdate optional {@link Physics} update mode.
+	 */
+	update (deltaTime: number, physicsUpdate: Physics = Physics.update) {
+		this.animationState.update(deltaTime);
+		this.skeleton.update(deltaTime);
+		this.animationState.apply(this.skeleton);
+		this.skeleton.updateWorldTransform(physicsUpdate);
+	}
 }
 
 /**
  * Renders a {@link Skeleton} or {@link SkeletonDrawable} to a CanvasKit {@link Canvas}.
  */
 export class SkeletonRenderer {
-    private clipper = new SkeletonClipping();
-    private tempColor = new Color();
-    private tempColor2 = new Color();
-    private static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
-    private scratchPositions = Utils.newFloatArray(100);
-    private scratchColors = Utils.newFloatArray(100);
-    private scratchUVs = Utils.newFloatArray(100);
-
-    /**
-     * Creates a new skeleton renderer.
-     * @param ck the {@link CanvasKit} instance returned by `CanvasKitInit()`.
-     */
-    constructor(private ck: CanvasKit) {}
-
-    /**
-     * Renders a skeleton or skeleton drawable in its current pose to the canvas.
-     * @param canvas the canvas to render to.
-     * @param skeleton the skeleton or drawable to render.
-     */
-    render(canvas: Canvas, skeleton: Skeleton | SkeletonDrawable) {
-        if (skeleton instanceof SkeletonDrawable) skeleton = skeleton.skeleton;
-        let clipper = this.clipper;
+	private clipper = new SkeletonClipping();
+	private tempColor = new Color();
+	private tempColor2 = new Color();
+	private static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
+	private scratchPositions = Utils.newFloatArray(100);
+	private scratchColors = Utils.newFloatArray(100);
+	private scratchUVs = Utils.newFloatArray(100);
+
+	/**
+	 * Creates a new skeleton renderer.
+	 * @param ck the {@link CanvasKit} instance returned by `CanvasKitInit()`.
+	 */
+	constructor (private ck: CanvasKit) { }
+
+	/**
+	 * Renders a skeleton or skeleton drawable in its current pose to the canvas.
+	 * @param canvas the canvas to render to.
+	 * @param skeleton the skeleton or drawable to render.
+	 */
+	render (canvas: Canvas, skeleton: Skeleton | SkeletonDrawable) {
+		if (skeleton instanceof SkeletonDrawable) skeleton = skeleton.skeleton;
+		let clipper = this.clipper;
 		let drawOrder = skeleton.drawOrder;
 		let skeletonColor = skeleton.color;
 
@@ -165,20 +234,20 @@ export class SkeletonRenderer {
 			if (!slot.bone.active) {
 				clipper.clipEndWithSlot(slot);
 				continue;
-            }
+			}
 
 			let attachment = slot.getAttachment();
-            let positions = this.scratchPositions;
-            let colors = this.scratchColors;
-            let uvs: NumberArrayLike;
+			let positions = this.scratchPositions;
+			let colors = this.scratchColors;
+			let uvs: NumberArrayLike;
 			let texture: CanvasKitTexture;
-            let triangles: Array<number>;
-            let attachmentColor: Color;
-            let numVertices = 0;
+			let triangles: Array<number>;
+			let attachmentColor: Color;
+			let numVertices = 0;
 			if (attachment instanceof RegionAttachment) {
 				let region = attachment as RegionAttachment;
-                positions = positions.length < 8 ? Utils.newFloatArray(8) : positions;
-                numVertices = 4;
+				positions = positions.length < 8 ? Utils.newFloatArray(8) : positions;
+				numVertices = 4;
 				region.computeWorldVertices(slot, positions, 0, 2);
 				triangles = SkeletonRenderer.QUAD_TRIANGLES;
 				uvs = region.uvs as Float32Array;
@@ -186,9 +255,19 @@ export class SkeletonRenderer {
 				attachmentColor = region.color;
 			} else if (attachment instanceof MeshAttachment) {
 				let mesh = attachment as MeshAttachment;
-                positions = positions.length < mesh.worldVerticesLength ? Utils.newFloatArray(mesh.worldVerticesLength) : positions;
-                numVertices = mesh.worldVerticesLength >> 1;
-				mesh.computeWorldVertices(slot, 0, mesh.worldVerticesLength, positions, 0, 2);
+				positions =
+					positions.length < mesh.worldVerticesLength
+						? Utils.newFloatArray(mesh.worldVerticesLength)
+						: positions;
+				numVertices = mesh.worldVerticesLength >> 1;
+				mesh.computeWorldVertices(
+					slot,
+					0,
+					mesh.worldVerticesLength,
+					positions,
+					0,
+					2
+				);
 				triangles = mesh.triangles;
 				texture = mesh.region?.texture as CanvasKitTexture;
 				uvs = mesh.uvs as Float32Array;
@@ -204,43 +283,63 @@ export class SkeletonRenderer {
 
 			if (texture) {
 				if (clipper.isClipping()) {
-					clipper.clipTrianglesUnpacked(positions, triangles, triangles.length, uvs);
-                    positions = clipper.clippedVertices;
-                    uvs = clipper.clippedUVs;
-                    triangles = clipper.clippedTriangles;
+					clipper.clipTrianglesUnpacked(
+						positions,
+						triangles,
+						triangles.length,
+						uvs
+					);
+					positions = clipper.clippedVertices;
+					uvs = clipper.clippedUVs;
+					triangles = clipper.clippedTriangles;
 				}
 
-                let slotColor = slot.color;
+				let slotColor = slot.color;
 				let finalColor = this.tempColor;
 				finalColor.r = skeletonColor.r * slotColor.r * attachmentColor.r;
 				finalColor.g = skeletonColor.g * slotColor.g * attachmentColor.g;
 				finalColor.b = skeletonColor.b * slotColor.b * attachmentColor.b;
 				finalColor.a = skeletonColor.a * slotColor.a * attachmentColor.a;
 
-                if (colors.length / 4 < numVertices) colors = Utils.newFloatArray(numVertices * 4);
-                for (let i = 0, n = numVertices * 4; i < n; i += 4) {
-                    colors[i] = finalColor.r;
-                    colors[i + 1] = finalColor.g;
-                    colors[i + 2] = finalColor.b;
-                    colors[i + 3] = finalColor.a;
-                }
-
-                const scaledUvs = this.scratchUVs.length < uvs.length ? Utils.newFloatArray(uvs.length) : this.scratchUVs;
-                const width = texture.getImage().image.width();
-                const height = texture.getImage().image.height();
-                for (let i = 0; i < uvs.length; i+=2) {
-                    scaledUvs[i] = uvs[i] * width;
-                    scaledUvs[i + 1] = uvs[i + 1] * height;
-                }
-
-                const blendMode = slot.data.blendMode;
-                const vertices = this.ck.MakeVertices(this.ck.VertexMode.Triangles, positions, scaledUvs, colors, triangles, false);
-                canvas.drawVertices(vertices, this.ck.BlendMode.Modulate, texture.getImage().paintPerBlendMode.get(blendMode)!);
-                vertices.delete();
+				if (colors.length / 4 < numVertices)
+					colors = Utils.newFloatArray(numVertices * 4);
+				for (let i = 0, n = numVertices * 4; i < n; i += 4) {
+					colors[i] = finalColor.r;
+					colors[i + 1] = finalColor.g;
+					colors[i + 2] = finalColor.b;
+					colors[i + 3] = finalColor.a;
+				}
+
+				const scaledUvs =
+					this.scratchUVs.length < uvs.length
+						? Utils.newFloatArray(uvs.length)
+						: this.scratchUVs;
+				const width = texture.getImage().image.width();
+				const height = texture.getImage().image.height();
+				for (let i = 0; i < uvs.length; i += 2) {
+					scaledUvs[i] = uvs[i] * width;
+					scaledUvs[i + 1] = uvs[i + 1] * height;
+				}
+
+				const blendMode = slot.data.blendMode;
+				const vertices = this.ck.MakeVertices(
+					this.ck.VertexMode.Triangles,
+					positions,
+					scaledUvs,
+					colors,
+					triangles,
+					false
+				);
+				canvas.drawVertices(
+					vertices,
+					this.ck.BlendMode.Modulate,
+					texture.getImage().paintPerBlendMode.get(blendMode)!
+				);
+				vertices.delete();
 			}
 
 			clipper.clipEndWithSlot(slot);
 		}
 		clipper.clipEnd();
-    }
-}
+	}
+}

+ 1 - 1
spine-ts/spine-core/src/SkeletonBinary.ts

@@ -64,7 +64,7 @@ export class SkeletonBinary {
 		this.attachmentLoader = attachmentLoader;
 	}
 
-	readSkeletonData (binary: Uint8Array | ArrayBuffer): SkeletonData {
+	readSkeletonData (binary: Uint8Array | ArrayBuffer): SkeletonData {
 		let scale = this.scale;
 
 		let skeletonData = new SkeletonData();

+ 1 - 1
spine-ue/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineAtlasAsset.cpp

@@ -95,7 +95,7 @@ void USpineAtlasAsset::BeginDestroy() {
 
 class UETextureLoader : public TextureLoader {
 	void load(AtlasPage &page, const String &path) {
-		page.texture = (void*)(uintptr_t)page.index;
+		page.texture = (void *) (uintptr_t) page.index;
 	}
 
 	void unload(void *texture) {

+ 1 - 1
spine-ue/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonRendererComponent.cpp

@@ -268,7 +268,7 @@ void USpineSkeletonRendererComponent::UpdateMesh(USpineSkeletonComponent *compon
 		// to the correct skeleton data yet, we won't find any regions.
 		// ignore regions for which we can't find a material
 		UMaterialInstanceDynamic *material = nullptr;
-		int foundPageIndex = (int)(intptr_t)attachmentAtlasRegion->rendererObject;
+		int foundPageIndex = (int) (intptr_t) attachmentAtlasRegion->rendererObject;
 		if (foundPageIndex == -1) {
 			clipper.clipEnd(*slot);
 			continue;