mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Improved reset bounds management.
This commit is contained in:
parent
45d94a3201
commit
46920ff54f
@ -32,32 +32,30 @@ import { C3Texture, C3TextureEditor } from "./C3Texture";
|
|||||||
|
|
||||||
|
|
||||||
export class AssetLoader {
|
export class AssetLoader {
|
||||||
constructor (private type: "editor" | "runtime") {
|
|
||||||
}
|
|
||||||
|
|
||||||
public async loadSkeletonEditor (path: string, textureAtlas: TextureAtlas, instance: SDK.IWorldInstance) {
|
public async loadSkeletonEditor (sid: number, textureAtlas: TextureAtlas, scale = 1, instance: SDK.IWorldInstance) {
|
||||||
const projectFile = instance.GetProject().GetProjectFileByName(path);
|
const projectFile = instance.GetProject().GetProjectFileBySID(sid);
|
||||||
if (!projectFile) return null;
|
if (!projectFile) return null;
|
||||||
|
|
||||||
const blob = projectFile.GetBlob();
|
const blob = projectFile.GetBlob();
|
||||||
const atlasLoader = new AtlasAttachmentLoader(textureAtlas);
|
const atlasLoader = new AtlasAttachmentLoader(textureAtlas);
|
||||||
|
|
||||||
const isBinary = path.endsWith(".skel");
|
const isBinary = projectFile.GetName().endsWith(".skel");
|
||||||
if (isBinary) {
|
if (isBinary) {
|
||||||
const skeletonFile = await blob.arrayBuffer();
|
const skeletonFile = await blob.arrayBuffer();
|
||||||
const skeletonLoader = new SkeletonBinary(atlasLoader);
|
const skeletonLoader = new SkeletonBinary(atlasLoader);
|
||||||
skeletonLoader.scale = 1;
|
skeletonLoader.scale = scale;
|
||||||
return skeletonLoader.readSkeletonData(skeletonFile);
|
return skeletonLoader.readSkeletonData(skeletonFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
const skeletonFile = await blob.text();
|
const skeletonFile = await blob.text();
|
||||||
const skeletonLoader = new SkeletonJson(atlasLoader);
|
const skeletonLoader = new SkeletonJson(atlasLoader);
|
||||||
skeletonLoader.scale = 1;
|
skeletonLoader.scale = scale;
|
||||||
return skeletonLoader.readSkeletonData(skeletonFile);
|
return skeletonLoader.readSkeletonData(skeletonFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadAtlasEditor (path: string, instance: SDK.IWorldInstance, renderer: SDK.Gfx.IWebGLRenderer) {
|
public async loadAtlasEditor (sid: number, instance: SDK.IWorldInstance, renderer: SDK.Gfx.IWebGLRenderer) {
|
||||||
const projectFile = instance.GetProject().GetProjectFileByName(path);
|
const projectFile = instance.GetProject().GetProjectFileBySID(sid);
|
||||||
if (!projectFile) return null;
|
if (!projectFile) return null;
|
||||||
|
|
||||||
const blob = projectFile.GetBlob();
|
const blob = projectFile.GetBlob();
|
||||||
@ -77,31 +75,31 @@ export class AssetLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async loadSpineTextureEditor (pageName: string, pma = false, instance: SDK.IWorldInstance) {
|
public async loadSpineTextureEditor (pageName: string, pma = false, instance: SDK.IWorldInstance) {
|
||||||
const projectFile = instance.GetProject().GetProjectFileByName(pageName);
|
const projectFile = instance.GetProject().GetProjectFileByExportPath(pageName);
|
||||||
if (!projectFile) {
|
if (!projectFile) {
|
||||||
throw new Error(`An error occured while loading the texture: ${pageName}`);
|
throw new Error(`An error occured while loading the texture: ${pageName}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = projectFile.GetBlob();
|
const content = projectFile.GetBlob();
|
||||||
return this.createImageBitmapFromBlob(content, pma);
|
return AssetLoader.createImageBitmapFromBlob(content, pma);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loadSkeletonRuntime (path: string, textureAtlas: TextureAtlas, scale = 1, instance: IRuntime) {
|
public async loadSkeletonRuntime (path: string, textureAtlas: TextureAtlas, scale = 1, instance: IRuntime) {
|
||||||
const fullPath = await instance.assets.getProjectFileUrl(path);
|
const fullPath = await instance.assets.getProjectFileUrl(path);
|
||||||
if (!fullPath) return null;
|
if (!fullPath) return null;
|
||||||
|
|
||||||
const content = await instance.assets.fetchArrayBuffer(fullPath);
|
|
||||||
if (!content) return null;
|
|
||||||
|
|
||||||
const atlasLoader = new AtlasAttachmentLoader(textureAtlas);
|
const atlasLoader = new AtlasAttachmentLoader(textureAtlas);
|
||||||
|
|
||||||
const isBinary = path.endsWith(".skel");
|
const isBinary = path.endsWith(".skel");
|
||||||
if (isBinary) {
|
if (isBinary) {
|
||||||
|
const content = await instance.assets.fetchArrayBuffer(fullPath);
|
||||||
|
if (!content) return null;
|
||||||
const skeletonLoader = new SkeletonBinary(atlasLoader);
|
const skeletonLoader = new SkeletonBinary(atlasLoader);
|
||||||
skeletonLoader.scale = scale;
|
skeletonLoader.scale = scale;
|
||||||
return skeletonLoader.readSkeletonData(content);
|
return skeletonLoader.readSkeletonData(content);
|
||||||
}
|
}
|
||||||
|
const content = await instance.assets.fetchJson(fullPath);
|
||||||
|
if (!content) return null;
|
||||||
const skeletonLoader = new SkeletonJson(atlasLoader);
|
const skeletonLoader = new SkeletonJson(atlasLoader);
|
||||||
skeletonLoader.scale = scale;
|
skeletonLoader.scale = scale;
|
||||||
return skeletonLoader.readSkeletonData(content);
|
return skeletonLoader.readSkeletonData(content);
|
||||||
@ -133,10 +131,10 @@ export class AssetLoader {
|
|||||||
const content = await instance.assets.fetchBlob(fullPath);
|
const content = await instance.assets.fetchBlob(fullPath);
|
||||||
if (!content) return null;
|
if (!content) return null;
|
||||||
|
|
||||||
return this.createImageBitmapFromBlob(content, pma);
|
return AssetLoader.createImageBitmapFromBlob(content, pma);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createImageBitmapFromBlob (blob: Blob, pma: boolean): Promise<ImageBitmap | null> {
|
static async createImageBitmapFromBlob (blob: Blob, pma: boolean): Promise<ImageBitmap | null> {
|
||||||
try {
|
try {
|
||||||
return createImageBitmap(blob, { premultiplyAlpha: pma ? "none" : "premultiply" });
|
return createImageBitmap(blob, { premultiplyAlpha: pma ? "none" : "premultiply" });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ spine.Skeleton.yDown = true;
|
|||||||
class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
||||||
propAtlas = "";
|
propAtlas = "";
|
||||||
propSkel = "";
|
propSkel = "";
|
||||||
|
propLoaderScale = 1;
|
||||||
propSkin: string[] = [];
|
propSkin: string[] = [];
|
||||||
propAnimation?: string;
|
propAnimation?: string;
|
||||||
propOffsetX = 0;
|
propOffsetX = 0;
|
||||||
@ -33,21 +34,23 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
|
|
||||||
const properties = this._getInitProperties();
|
const properties = this._getInitProperties();
|
||||||
if (properties) {
|
if (properties) {
|
||||||
|
console.log(properties);
|
||||||
this.propAtlas = properties[0] as string;
|
this.propAtlas = properties[0] as string;
|
||||||
this.propSkel = properties[1] as string;
|
this.propSkel = properties[1] as string;
|
||||||
const skinProp = properties[2] as string;
|
this.propLoaderScale = properties[2] as number;
|
||||||
|
const skinProp = properties[3] as string;
|
||||||
this.propSkin = skinProp === "" ? [] : skinProp.split(",");
|
this.propSkin = skinProp === "" ? [] : skinProp.split(",");
|
||||||
this.propAnimation = properties[3] as string;
|
this.propAnimation = properties[4] as string;
|
||||||
|
|
||||||
this.propOffsetX = properties[7] as number;
|
this.propOffsetX = properties[7] as number;
|
||||||
this.propOffsetY = properties[8] as number;
|
this.propOffsetY = properties[8] as number;
|
||||||
this.propOffsetAngle = properties[9] as number;
|
this.propOffsetAngle = properties[9] as number;
|
||||||
this.propScaleX = properties[10] as number;
|
this.propScaleX = properties[10] as number;
|
||||||
this.propScaleY = properties[11] as number;
|
this.propScaleY = properties[11] as number;
|
||||||
console.log(properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.assetLoader = new spine.AssetLoader("runtime");
|
this.assetLoader = new spine.AssetLoader();
|
||||||
this.skeletonRenderer = new spine.SkeletonRendererCore();
|
this.skeletonRenderer = new spine.SkeletonRendererCore();
|
||||||
|
|
||||||
this._setTicking(true);
|
this._setTicking(true);
|
||||||
@ -81,7 +84,7 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
const propValue = this.propSkel;
|
const propValue = this.propSkel;
|
||||||
|
|
||||||
if (this.atlasLoaded && this.textureAtlas) {
|
if (this.atlasLoaded && this.textureAtlas) {
|
||||||
const skeletonData = await this.assetLoader.loadSkeletonRuntime(propValue, this.textureAtlas, 1, this.plugin.runtime);
|
const skeletonData = await this.assetLoader.loadSkeletonRuntime(propValue, this.textureAtlas, this.propLoaderScale, this.plugin.runtime);
|
||||||
if (!skeletonData) return;
|
if (!skeletonData) return;
|
||||||
|
|
||||||
this.skeleton = new spine.Skeleton(skeletonData);
|
this.skeleton = new spine.Skeleton(skeletonData);
|
||||||
@ -96,13 +99,9 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
|
|||||||
|
|
||||||
this.update(0);
|
this.update(0);
|
||||||
|
|
||||||
// Initially, width and height are values set on C3 Editor side that allows to determine the right scale
|
|
||||||
this.skeleton.scaleX = this.propScaleX;
|
this.skeleton.scaleX = this.propScaleX;
|
||||||
this.skeleton.scaleY = this.propScaleY;
|
this.skeleton.scaleY = this.propScaleY;
|
||||||
|
|
||||||
// this.setSize(this._spineBounds.width * this.skeleton.scaleX, this._spineBounds.height * -this.skeleton.scaleY);
|
|
||||||
// this.setOrigin(-this._spineBounds.x * this.skeleton.scaleX / this.width, this._spineBounds.y * this.skeleton.scaleY / this.height);
|
|
||||||
|
|
||||||
this.skeletonLoaded = true;
|
this.skeletonLoaded = true;
|
||||||
this._trigger(C3.Plugins.EsotericSoftware_SpineConstruct3.Cnds.OnSkeletonLoaded);
|
this._trigger(C3.Plugins.EsotericSoftware_SpineConstruct3.Cnds.OnSkeletonLoaded);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,52 +14,53 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
private layoutView?: SDK.UI.ILayoutView;
|
private layoutView?: SDK.UI.ILayoutView;
|
||||||
private renderer?: SDK.Gfx.IWebGLRenderer;
|
private renderer?: SDK.Gfx.IWebGLRenderer;
|
||||||
|
|
||||||
private currentSkelName = "";
|
private currentAtlasFileSID = -1;
|
||||||
private currentAtlasName = "";
|
|
||||||
private textureAtlas?: TextureAtlas;
|
private textureAtlas?: TextureAtlas;
|
||||||
|
|
||||||
skeleton?: Skeleton;
|
skeleton?: Skeleton;
|
||||||
state?: AnimationState;
|
state?: AnimationState;
|
||||||
skins: string[] = [];
|
skins: string[] = [];
|
||||||
currentSkinString?: string;
|
|
||||||
animation?: string;
|
animation?: string;
|
||||||
|
|
||||||
private assetLoader: AssetLoader;
|
private assetLoader: AssetLoader;
|
||||||
private skeletonRenderer: SkeletonRendererCore;
|
private skeletonRenderer: SkeletonRendererCore;
|
||||||
|
|
||||||
private positioningBounds = false;
|
|
||||||
private spineBoundsProviderType: SpineBoundsProviderType = "setup";
|
|
||||||
private spineBoundsProvider?: SpineBoundsProvider;
|
|
||||||
private initialBounds = {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// position mode
|
// position mode
|
||||||
|
private positioningBounds = false;
|
||||||
private positionModePrevX = 0;
|
private positionModePrevX = 0;
|
||||||
private positionModePrevY = 0;
|
private positionModePrevY = 0;
|
||||||
private positionModePrevAngle = 0;
|
private positionModePrevAngle = 0;
|
||||||
|
private spineBounds = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
// utils for drawing
|
||||||
private tempVertices = new Float32Array(4096);
|
private tempVertices = new Float32Array(4096);
|
||||||
|
|
||||||
|
// errors
|
||||||
|
private errors: Record<string, string> = {};
|
||||||
|
|
||||||
constructor (sdkType: SDK.ITypeBase, inst: SDK.IWorldInstance) {
|
constructor (sdkType: SDK.ITypeBase, inst: SDK.IWorldInstance) {
|
||||||
super(sdkType, inst);
|
super(sdkType, inst);
|
||||||
|
|
||||||
if (!spine) spine = globalThis.spine;
|
if (!spine) spine = globalThis.spine;
|
||||||
spine.Skeleton.yDown = true;
|
spine.Skeleton.yDown = true;
|
||||||
|
|
||||||
this.assetLoader = new spine.AssetLoader("editor");
|
this.assetLoader = new spine.AssetLoader();
|
||||||
this.skeletonRenderer = new spine.SkeletonRendererCore();
|
this.skeletonRenderer = new spine.SkeletonRendererCore();
|
||||||
|
|
||||||
|
(this._inst as any).errors = this.errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
Release () {
|
Release () {
|
||||||
}
|
}
|
||||||
|
|
||||||
OnCreate () {
|
OnCreate () {
|
||||||
console.log("OnCreate");
|
|
||||||
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE, false);
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE, false);
|
||||||
|
this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON) as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
OnPlacedInLayout () {
|
OnPlacedInLayout () {
|
||||||
@ -70,7 +71,6 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
this.layoutView ||= iDrawParams.GetLayoutView();
|
this.layoutView ||= iDrawParams.GetLayoutView();
|
||||||
this.renderer ||= iRenderer;
|
this.renderer ||= iRenderer;
|
||||||
|
|
||||||
|
|
||||||
this.loadAtlas();
|
this.loadAtlas();
|
||||||
this.loadSkeleton();
|
this.loadSkeleton();
|
||||||
|
|
||||||
@ -89,8 +89,8 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
offsetY += rectY;
|
offsetY += rectY;
|
||||||
offsetAngle += rectAngle;
|
offsetAngle += rectAngle;
|
||||||
|
|
||||||
const baseScaleX = this._inst.GetWidth() / this.initialBounds.width;
|
const baseScaleX = this._inst.GetWidth() / this.spineBounds.width;
|
||||||
const baseScaleY = this._inst.GetHeight() / this.initialBounds.height;
|
const baseScaleY = this._inst.GetHeight() / this.spineBounds.height;
|
||||||
this.skeleton.scaleX = baseScaleX;
|
this.skeleton.scaleX = baseScaleX;
|
||||||
this.skeleton.scaleY = baseScaleY;
|
this.skeleton.scaleY = baseScaleY;
|
||||||
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_X, baseScaleX);
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_X, baseScaleX);
|
||||||
@ -129,7 +129,9 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
vertices[dstIndex + 2] = 0;
|
vertices[dstIndex + 2] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iRenderer.ResetColor();
|
||||||
iRenderer.SetAlphaBlend();
|
iRenderer.SetAlphaBlend();
|
||||||
|
iRenderer.SetTextureFillMode();
|
||||||
iRenderer.SetTexture(command.texture.texture);
|
iRenderer.SetTexture(command.texture.texture);
|
||||||
iRenderer.DrawMesh(
|
iRenderer.DrawMesh(
|
||||||
vertices.subarray(0, numVertices * 3),
|
vertices.subarray(0, numVertices * 3),
|
||||||
@ -145,59 +147,96 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
iRenderer.SetColorRgba(0.25, 0, 0, 0.25);
|
iRenderer.SetColorRgba(0.25, 0, 0, 0.25);
|
||||||
iRenderer.LineQuad(this._inst.GetQuad());
|
iRenderer.LineQuad(this._inst.GetQuad());
|
||||||
iRenderer.Line(rectX, rectY, offsetX, offsetY);
|
iRenderer.Line(rectX, rectY, offsetX, offsetY);
|
||||||
console.log(offsetX, offsetY);
|
|
||||||
|
if (this.hasErrors()) {
|
||||||
|
iRenderer.SetColorFillMode();
|
||||||
|
iRenderer.SetColorRgba(1, 0, 0, .5);
|
||||||
|
iRenderer.Quad(this._inst.GetQuad());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// render placeholder
|
|
||||||
iRenderer.SetAlphaBlend();
|
|
||||||
iRenderer.SetColorFillMode();
|
|
||||||
|
|
||||||
if (this.HadTextureError())
|
const sdkType = (this._sdkType as any);
|
||||||
iRenderer.SetColorRgba(0.25, 0, 0, 0.25);
|
|
||||||
else
|
|
||||||
iRenderer.SetColorRgba(0, 0, 0.1, 0.1);
|
const logo = sdkType.getSpineLogo(iRenderer);
|
||||||
|
if (logo) {
|
||||||
|
iRenderer.ResetColor();
|
||||||
|
iRenderer.SetAlphaBlend();
|
||||||
|
iRenderer.SetTexture(logo);
|
||||||
|
iRenderer.Quad(this._inst.GetQuad());
|
||||||
|
} else {
|
||||||
|
iRenderer.SetAlphaBlend();
|
||||||
|
iRenderer.SetColorFillMode();
|
||||||
|
|
||||||
|
if (this.HadTextureError())
|
||||||
|
iRenderer.SetColorRgba(0.25, 0, 0, 0.25);
|
||||||
|
else
|
||||||
|
iRenderer.SetColorRgba(0, 0, 0.1, 0.1);
|
||||||
|
|
||||||
|
iRenderer.Quad(this._inst.GetQuad());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
iRenderer.Quad(this._inst.GetQuad());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async OnPropertyChanged (id: string, value: EditorPropertyValueType) {
|
async OnPropertyChanged (id: string, value: EditorPropertyValueType) {
|
||||||
console.log(`Prop change - Name: ${id} - Value: ${value}`);
|
console.log(`Prop change - Name: ${id} - Value: ${value}`);
|
||||||
|
|
||||||
switch (id) {
|
if (id === PLUGIN_CLASS.PROP_ATLAS) {
|
||||||
case PLUGIN_CLASS.PROP_ATLAS:
|
this.textureAtlas?.dispose();
|
||||||
this.layoutView?.Refresh();
|
this.textureAtlas = undefined;
|
||||||
break;
|
this.skins = [];
|
||||||
case PLUGIN_CLASS.PROP_SKELETON:
|
this.layoutView?.Refresh();
|
||||||
this.layoutView?.Refresh();
|
return;
|
||||||
break;
|
}
|
||||||
case PLUGIN_CLASS.PROP_SKIN:
|
|
||||||
this.layoutView?.Refresh();
|
if (id === PLUGIN_CLASS.PROP_SKELETON) {
|
||||||
break;
|
this.skeleton = undefined;
|
||||||
case PLUGIN_CLASS.PROP_ANIMATION:
|
this.skins = [];
|
||||||
this.layoutView?.Refresh();
|
this.layoutView?.Refresh();
|
||||||
break;
|
return;
|
||||||
case PLUGIN_CLASS.PROP_BOUNDS_PROVIDER:
|
}
|
||||||
this.setC3Bounds(true);
|
|
||||||
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X, 0);
|
if (id === PLUGIN_CLASS.PROP_LOADER_SCALE) {
|
||||||
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y, 0);
|
this.skeleton = undefined;
|
||||||
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE, 0);
|
this.skins = [];
|
||||||
this.layoutView?.Refresh();
|
this.layoutView?.Refresh();
|
||||||
break;
|
return;
|
||||||
case PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE: {
|
}
|
||||||
value = value as boolean
|
|
||||||
if (value) {
|
if (id === PLUGIN_CLASS.PROP_SKIN) {
|
||||||
this.positionModePrevX = this._inst.GetX();
|
this.skins = [];
|
||||||
this.positionModePrevY = this._inst.GetY();
|
this.setSkin();
|
||||||
this.positionModePrevAngle = this._inst.GetAngle();
|
this.layoutView?.Refresh();
|
||||||
} else {
|
return;
|
||||||
const scaleX = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_X) as number;
|
}
|
||||||
const scaleY = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_Y) as number;
|
|
||||||
this.initialBounds.width = this._inst.GetWidth() / scaleX;
|
if (id === PLUGIN_CLASS.PROP_ANIMATION) {
|
||||||
this.initialBounds.height = this._inst.GetHeight() / scaleY;
|
this.layoutView?.Refresh();
|
||||||
}
|
return;
|
||||||
this.positioningBounds = value;
|
}
|
||||||
break;
|
|
||||||
|
if (id === PLUGIN_CLASS.PROP_BOUNDS_PROVIDER) {
|
||||||
|
this.resetBounds();
|
||||||
|
this.layoutView?.Refresh();
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id === PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE) {
|
||||||
|
value = value as boolean
|
||||||
|
if (value) {
|
||||||
|
this.positionModePrevX = this._inst.GetX();
|
||||||
|
this.positionModePrevY = this._inst.GetY();
|
||||||
|
this.positionModePrevAngle = this._inst.GetAngle();
|
||||||
|
} else {
|
||||||
|
const scaleX = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_X) as number;
|
||||||
|
const scaleY = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_Y) as number;
|
||||||
|
this.spineBounds.width = this._inst.GetWidth() / scaleX;
|
||||||
|
this.spineBounds.height = this._inst.GetHeight() / scaleY;
|
||||||
}
|
}
|
||||||
|
this.positioningBounds = value;
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Prop change end");
|
console.log("Prop change end");
|
||||||
@ -209,13 +248,12 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
|
|
||||||
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKIN) as string;
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKIN) as string;
|
||||||
|
|
||||||
if (this.currentSkinString === propValue) return;
|
|
||||||
this.currentSkinString = propValue;
|
|
||||||
|
|
||||||
const skins = propValue === "" ? [] : propValue.split(",");
|
const skins = propValue === "" ? [] : propValue.split(",");
|
||||||
this.skins = skins;
|
this.skins = skins;
|
||||||
|
|
||||||
if (skins.length === 1) {
|
if (skins.length === 0) {
|
||||||
|
skeleton.setSkin(null);
|
||||||
|
} else if (skins.length === 1) {
|
||||||
const skinName = skins[0];
|
const skinName = skins[0];
|
||||||
const skin = skeleton.data.findSkin(skinName);
|
const skin = skeleton.data.findSkin(skinName);
|
||||||
if (!skin) {
|
if (!skin) {
|
||||||
@ -223,7 +261,7 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
skeleton.setSkin(skins[0]);
|
skeleton.setSkin(skins[0]);
|
||||||
} else if (skins.length > 1) {
|
} else {
|
||||||
const customSkin = new spine.Skin(propValue);
|
const customSkin = new spine.Skin(propValue);
|
||||||
for (const s of skins) {
|
for (const s of skins) {
|
||||||
const skin = skeleton.data.findSkin(s)
|
const skin = skeleton.data.findSkin(s)
|
||||||
@ -238,71 +276,103 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
|
|
||||||
skeleton.setupPose();
|
skeleton.setupPose();
|
||||||
this.update(0);
|
this.update(0);
|
||||||
|
|
||||||
this.setC3Bounds();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadSkeleton () {
|
private async loadSkeleton () {
|
||||||
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON) as string;
|
if (!this.renderer || !this.textureAtlas) return;
|
||||||
const projectFile = this._inst.GetProject().GetProjectFileByName(propValue);
|
if (this.skeleton) return;
|
||||||
|
|
||||||
if (projectFile && this.textureAtlas) {
|
console.log("Loading skeleton");
|
||||||
if (this.currentSkelName === propValue) return;
|
|
||||||
this.currentSkelName = propValue;
|
|
||||||
|
|
||||||
const skeletonData = await this.assetLoader.loadSkeletonEditor(propValue, this.textureAtlas, this._inst);
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON) as number;
|
||||||
if (!skeletonData) return;
|
const loaderScale = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_LOADER_SCALE) as number;
|
||||||
|
const skeletonData = await this.assetLoader.loadSkeletonEditor(propValue, this.textureAtlas, loaderScale, this._inst);
|
||||||
|
console.log(skeletonData);
|
||||||
|
if (!skeletonData) return;
|
||||||
|
|
||||||
this.skeleton = new spine.Skeleton(skeletonData);
|
this.skeleton = new spine.Skeleton(skeletonData);
|
||||||
const animationStateData = new spine.AnimationStateData(skeletonData);
|
const animationStateData = new spine.AnimationStateData(skeletonData);
|
||||||
this.state = new spine.AnimationState(animationStateData);
|
this.state = new spine.AnimationState(animationStateData);
|
||||||
|
|
||||||
this.update(0);
|
this.setSkin();
|
||||||
|
this.update(0);
|
||||||
|
this.setBoundsFromBoundsProvider();
|
||||||
|
this.initBounds();
|
||||||
|
|
||||||
this.setSkin();
|
this.layoutView?.Refresh();
|
||||||
|
console.log("SKELETON LOADED");
|
||||||
const offsetX = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X) as number;
|
|
||||||
const offsetY = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y) as number;
|
|
||||||
const offsetAngle = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE) as number;
|
|
||||||
const scaleX = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_X) as number;
|
|
||||||
const scaleY = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_Y) as number;
|
|
||||||
const init = offsetX !== 0 && offsetY !== 0 && offsetAngle !== 0 && scaleX !== 1 && scaleY !== 1;
|
|
||||||
this.setC3Bounds(init);
|
|
||||||
|
|
||||||
this.layoutView?.Refresh();
|
|
||||||
console.log("SKELETON LOADED");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private setC3Bounds (init = false) {
|
private async loadAtlas () {
|
||||||
|
if (!this.renderer) return;
|
||||||
|
|
||||||
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_ATLAS) as number;
|
||||||
|
console.log("Loading atlas");
|
||||||
|
|
||||||
|
if (this.currentAtlasFileSID === propValue) return;
|
||||||
|
this.currentAtlasFileSID = propValue;
|
||||||
|
|
||||||
|
const textureAtlas = await this.assetLoader.loadAtlasEditor(propValue, this._inst, this.renderer);
|
||||||
|
if (!textureAtlas) return;
|
||||||
|
|
||||||
|
this.textureAtlas = textureAtlas;
|
||||||
|
this.layoutView?.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setBoundsFromBoundsProvider () {
|
||||||
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER) as SpineBoundsProviderType;
|
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER) as SpineBoundsProviderType;
|
||||||
|
|
||||||
|
let spineBoundsProvider: SpineBoundsProvider;
|
||||||
if (propValue === "animation-skin") {
|
if (propValue === "animation-skin") {
|
||||||
const { skins, animation } = this;
|
const { skins, animation } = this;
|
||||||
if ((skins && skins.length > 0) || animation) {
|
if ((skins && skins.length > 0) || animation) {
|
||||||
this.spineBoundsProvider = new spine.SkinsAndAnimationBoundsProvider(animation, skins);
|
spineBoundsProvider = new spine.SkinsAndAnimationBoundsProvider(animation, skins);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("One among skin and animation needs to have a value to set this bounds provider.");
|
return false;
|
||||||
}
|
}
|
||||||
} else if (propValue === "setup") {
|
} else if (propValue === "setup") {
|
||||||
this.spineBoundsProvider = new spine.SetupPoseBoundsProvider();
|
spineBoundsProvider = new spine.SetupPoseBoundsProvider();
|
||||||
} else {
|
} else {
|
||||||
this.spineBoundsProvider = new spine.AABBRectangleBoundsProvider(0, 0, 100, 100);
|
spineBoundsProvider = new spine.AABBRectangleBoundsProvider(0, 0, 100, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initialBounds = this.spineBoundsProvider.calculateBounds(this);
|
this.spineBounds = spineBoundsProvider.calculateBounds(this);
|
||||||
|
|
||||||
const { x, y, width, height } = this.initialBounds;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (width <= 0 || height <= 0 || !init) return;
|
private resetBounds () {
|
||||||
|
this.setBoundsFromBoundsProvider();
|
||||||
|
|
||||||
|
if (this.hasErrors()) return;
|
||||||
|
const { x, y, width, height } = this.spineBounds;
|
||||||
|
|
||||||
|
this._inst.SetOrigin(-x / width, -y / height);
|
||||||
this._inst.SetSize(width, height);
|
this._inst.SetSize(width, height);
|
||||||
|
|
||||||
if (propValue === "AABB") {
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X, 0);
|
||||||
this._inst.SetOrigin(.5, .5);
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y, 0);
|
||||||
} else {
|
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE, 0);
|
||||||
this._inst.SetOrigin(-x / width, -y / height);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private initBounds () {
|
||||||
|
const offsetX = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X) as number;
|
||||||
|
const offsetY = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y) as number;
|
||||||
|
const offsetAngle = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE) as number;
|
||||||
|
const shiftedBounds = offsetX !== 0 || offsetY !== 0 || offsetAngle !== 0;
|
||||||
|
|
||||||
|
const scaleX = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_X) as number;
|
||||||
|
const scaleY = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_Y) as number;
|
||||||
|
const scaledBounds = scaleX !== 1 || scaleY !== 1;
|
||||||
|
|
||||||
|
if (shiftedBounds || scaledBounds) {
|
||||||
|
this.spineBounds.width = this._inst.GetWidth() / scaleX;
|
||||||
|
this.spineBounds.height = this._inst.GetHeight() / scaleY;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.resetBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private update (delta: number) {
|
private update (delta: number) {
|
||||||
@ -316,19 +386,31 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
skeleton.updateWorldTransform(spine.Physics.update);
|
skeleton.updateWorldTransform(spine.Physics.update);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadAtlas () {
|
private setError (key: string, condition: boolean, message: string) {
|
||||||
if (!this.renderer) return;
|
if (condition) {
|
||||||
|
this.errors[key] = message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete this.errors[key];
|
||||||
|
}
|
||||||
|
private hasErrors () {
|
||||||
|
const { errors, skins, animation, spineBounds } = this;
|
||||||
|
|
||||||
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_ATLAS) as string;
|
const boundsType = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER) as SpineBoundsProviderType;
|
||||||
|
this.setError(
|
||||||
|
"boundsAnimationSkinType",
|
||||||
|
boundsType === "animation-skin" && ((!skins || skins.length === 0) && !animation),
|
||||||
|
"Animation/Skin bounds provider requires one between skin and animation to be set."
|
||||||
|
);
|
||||||
|
|
||||||
if (this.currentAtlasName === propValue) return;
|
const { width, height } = spineBounds;
|
||||||
this.currentAtlasName = propValue;
|
this.setError(
|
||||||
|
"boundsNoDimension",
|
||||||
|
width <= 0 || height <= 0,
|
||||||
|
"A bounds cannot have negative dimension"
|
||||||
|
);
|
||||||
|
|
||||||
const textureAtlas = await this.assetLoader.loadAtlasEditor(propValue, this._inst, this.renderer);
|
return Object.keys(errors).length > 0;
|
||||||
if (!textureAtlas) return;
|
|
||||||
|
|
||||||
this.textureAtlas = textureAtlas;
|
|
||||||
this.layoutView?.Refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GetTexture () {
|
GetTexture () {
|
||||||
@ -341,20 +423,15 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GetOriginalWidth () {
|
GetOriginalWidth () {
|
||||||
if (!this.initialBounds) return 100;
|
return this.spineBounds.width;
|
||||||
return this.initialBounds.width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GetOriginalHeight () {
|
GetOriginalHeight () {
|
||||||
if (!this.initialBounds) return 100;
|
return this.spineBounds.height;
|
||||||
return this.initialBounds.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OnMakeOriginalSize () {
|
OnMakeOriginalSize () {
|
||||||
if (!this.initialBounds)
|
this._inst.SetSize(this.spineBounds.width, this.spineBounds.height);
|
||||||
this._inst.SetSize(100, 100);
|
|
||||||
else
|
|
||||||
this._inst.SetSize(this.initialBounds.width, this.initialBounds.height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HasDoubleTapHandler () {
|
HasDoubleTapHandler () {
|
||||||
|
|||||||
@ -18,9 +18,9 @@
|
|||||||
"name": "Atlas",
|
"name": "Atlas",
|
||||||
"desc": "Atlas file"
|
"desc": "Atlas file"
|
||||||
},
|
},
|
||||||
"spine-skeleton-file-blob": {
|
"spine-loader-scale": {
|
||||||
"name": "Skel Blob",
|
"name": "Loader scale",
|
||||||
"desc": "Skel file blob"
|
"desc": "Loader scale"
|
||||||
},
|
},
|
||||||
"spine-animation": {
|
"spine-animation": {
|
||||||
"name": "animation",
|
"name": "animation",
|
||||||
@ -30,6 +30,10 @@
|
|||||||
"name": "skin",
|
"name": "skin",
|
||||||
"desc": "skin"
|
"desc": "skin"
|
||||||
},
|
},
|
||||||
|
"spine-errors": {
|
||||||
|
"name": "Errors",
|
||||||
|
"desc": "errors"
|
||||||
|
},
|
||||||
"spine-bounds-provider-group": {
|
"spine-bounds-provider-group": {
|
||||||
"name": "Bounds provider",
|
"name": "Bounds provider",
|
||||||
"desc": "Select the desired buound provider and fill the respective properties."
|
"desc": "Select the desired buound provider and fill the respective properties."
|
||||||
@ -38,9 +42,8 @@
|
|||||||
"name": "Bounds provider",
|
"name": "Bounds provider",
|
||||||
"desc": "The bounds provider to use.",
|
"desc": "The bounds provider to use.",
|
||||||
"items": {
|
"items": {
|
||||||
"AABB": "AABB Rectangle",
|
|
||||||
"setup": "Setup pose bounds",
|
"setup": "Setup pose bounds",
|
||||||
"animation-skin": "Animation + Skin bounds"
|
"animation-skin": "Animation/Skin bounds"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spine-bounds-provider-move": {
|
"spine-bounds-provider-move": {
|
||||||
|
|||||||
@ -15,17 +15,15 @@ const PLUGIN_ID = "EsotericSoftware_SpineConstruct3";
|
|||||||
|
|
||||||
const PLUGIN_CATEGORY = "general";
|
const PLUGIN_CATEGORY = "general";
|
||||||
|
|
||||||
let app = null;
|
|
||||||
|
|
||||||
const PLUGIN_CLASS = class MyDrawingPlugin extends SDK.IPluginBase {
|
const PLUGIN_CLASS = class MyDrawingPlugin extends SDK.IPluginBase {
|
||||||
static PROP_ATLAS = "spine-atlas-file";
|
static PROP_ATLAS = "spine-atlas-file";
|
||||||
static PROP_SKELETON = "spine-skeleton-file";
|
static PROP_SKELETON = "spine-skeleton-file";
|
||||||
|
static PROP_LOADER_SCALE = "spine-loader-scale";
|
||||||
static PROP_SKIN = "spine-skin";
|
static PROP_SKIN = "spine-skin";
|
||||||
static PROP_ANIMATION = "spine-animation";
|
static PROP_ANIMATION = "spine-animation";
|
||||||
static PROP_ERRORS = "spine-errors";
|
static PROP_ERRORS = "spine-errors";
|
||||||
static PROP_RATIO_WIDTH = "spine-restore-ratio-width";
|
static PROP_RATIO_WIDTH = "spine-restore-ratio-width";
|
||||||
static PROP_RATIO_HEIGHT = "spine-restore-ratio-height";
|
static PROP_RATIO_HEIGHT = "spine-restore-ratio-height";
|
||||||
static PROP_SKELETON_BLOB = "spine-skeleton-file-blob";
|
|
||||||
static PROP_BOUNDS_PROVIDER_GROUP = "spine-bounds-provider-group";
|
static PROP_BOUNDS_PROVIDER_GROUP = "spine-bounds-provider-group";
|
||||||
static PROP_BOUNDS_PROVIDER = "spine-bounds-provider";
|
static PROP_BOUNDS_PROVIDER = "spine-bounds-provider";
|
||||||
static PROP_BOUNDS_PROVIDER_MOVE = "spine-bounds-provider-move";
|
static PROP_BOUNDS_PROVIDER_MOVE = "spine-bounds-provider-move";
|
||||||
@ -37,12 +35,10 @@ const PLUGIN_CLASS = class MyDrawingPlugin extends SDK.IPluginBase {
|
|||||||
|
|
||||||
static TYPE_BOUNDS_SETUP = "setup";
|
static TYPE_BOUNDS_SETUP = "setup";
|
||||||
static TYPE_BOUNDS_ANIMATION_SKIN = "animation-skin";
|
static TYPE_BOUNDS_ANIMATION_SKIN = "animation-skin";
|
||||||
static TYPE_BOUNDS_AABB = "AABB";
|
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
super(PLUGIN_ID);
|
super(PLUGIN_ID);
|
||||||
|
|
||||||
|
|
||||||
SDK.Lang.PushContext("plugins." + PLUGIN_ID.toLowerCase());
|
SDK.Lang.PushContext("plugins." + PLUGIN_ID.toLowerCase());
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -69,37 +65,26 @@ const PLUGIN_CLASS = class MyDrawingPlugin extends SDK.IPluginBase {
|
|||||||
SDK.Lang.PushContext(".properties");
|
SDK.Lang.PushContext(".properties");
|
||||||
|
|
||||||
this._info.SetProperties([
|
this._info.SetProperties([
|
||||||
new SDK.PluginProperty("text", MyDrawingPlugin.PROP_ATLAS, ""),
|
new SDK.PluginProperty("projectfile", MyDrawingPlugin.PROP_ATLAS, ""),
|
||||||
new SDK.PluginProperty("text", MyDrawingPlugin.PROP_SKELETON, ""),
|
new SDK.PluginProperty("projectfile", MyDrawingPlugin.PROP_SKELETON, ""),
|
||||||
|
new SDK.PluginProperty("float", MyDrawingPlugin.PROP_LOADER_SCALE, 1),
|
||||||
new SDK.PluginProperty("text", MyDrawingPlugin.PROP_SKIN, ""),
|
new SDK.PluginProperty("text", MyDrawingPlugin.PROP_SKIN, ""),
|
||||||
new SDK.PluginProperty("text", MyDrawingPlugin.PROP_ANIMATION, ""),
|
new SDK.PluginProperty("text", MyDrawingPlugin.PROP_ANIMATION, ""),
|
||||||
new SDK.PluginProperty("projectfile", MyDrawingPlugin.PROP_SKELETON_BLOB, ""),
|
|
||||||
new SDK.PluginProperty("info", MyDrawingPlugin.PROP_ERRORS, {
|
new SDK.PluginProperty("info", MyDrawingPlugin.PROP_ERRORS, {
|
||||||
infoCallback (inst) {
|
infoCallback (inst) {
|
||||||
const atlas = inst.GetInstance().GetPropertyValue(MyDrawingPlugin.PROP_ATLAS);
|
const errors = (inst.GetInstance() as unknown as { errors: Record<string, string> }).errors;
|
||||||
const skeleton = inst.GetInstance().GetPropertyValue(MyDrawingPlugin.PROP_SKELETON);
|
return Object.values(errors).reduce((acc, next) => {
|
||||||
|
return acc === "" ? next : `${acc}\n${next}`;
|
||||||
let error = "";
|
}, "");
|
||||||
if (atlas && skeleton) {
|
|
||||||
error = "You can't set both .skel and .json skeleton file.";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!atlas && !skeleton) {
|
|
||||||
error = "Missing skeleton file.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
||||||
new SDK.PluginProperty("group", MyDrawingPlugin.PROP_BOUNDS_PROVIDER_GROUP),
|
new SDK.PluginProperty("group", MyDrawingPlugin.PROP_BOUNDS_PROVIDER_GROUP),
|
||||||
new SDK.PluginProperty("combo", MyDrawingPlugin.PROP_BOUNDS_PROVIDER, {
|
new SDK.PluginProperty("combo", MyDrawingPlugin.PROP_BOUNDS_PROVIDER, {
|
||||||
initialValue: "setup",
|
initialValue: "setup",
|
||||||
items: [
|
items: [
|
||||||
MyDrawingPlugin.TYPE_BOUNDS_SETUP,
|
MyDrawingPlugin.TYPE_BOUNDS_SETUP,
|
||||||
MyDrawingPlugin.TYPE_BOUNDS_ANIMATION_SKIN,
|
MyDrawingPlugin.TYPE_BOUNDS_ANIMATION_SKIN,
|
||||||
MyDrawingPlugin.TYPE_BOUNDS_AABB
|
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
new SDK.PluginProperty("check", MyDrawingPlugin.PROP_BOUNDS_PROVIDER_MOVE, false),
|
new SDK.PluginProperty("check", MyDrawingPlugin.PROP_BOUNDS_PROVIDER_MOVE, false),
|
||||||
|
|||||||
BIN
spine-ts/spine-construct3/src/spine_badge.png
Normal file
BIN
spine-ts/spine-construct3/src/spine_badge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user