Improved reset bounds management.

This commit is contained in:
Davide Tantillo 2025-08-14 17:37:34 +02:00
parent 45d94a3201
commit 46920ff54f
7 changed files with 262 additions and 176 deletions

View File

@ -32,32 +32,30 @@ import { C3Texture, C3TextureEditor } from "./C3Texture";
export class AssetLoader {
constructor (private type: "editor" | "runtime") {
}
public async loadSkeletonEditor (path: string, textureAtlas: TextureAtlas, instance: SDK.IWorldInstance) {
const projectFile = instance.GetProject().GetProjectFileByName(path);
public async loadSkeletonEditor (sid: number, textureAtlas: TextureAtlas, scale = 1, instance: SDK.IWorldInstance) {
const projectFile = instance.GetProject().GetProjectFileBySID(sid);
if (!projectFile) return null;
const blob = projectFile.GetBlob();
const atlasLoader = new AtlasAttachmentLoader(textureAtlas);
const isBinary = path.endsWith(".skel");
const isBinary = projectFile.GetName().endsWith(".skel");
if (isBinary) {
const skeletonFile = await blob.arrayBuffer();
const skeletonLoader = new SkeletonBinary(atlasLoader);
skeletonLoader.scale = 1;
skeletonLoader.scale = scale;
return skeletonLoader.readSkeletonData(skeletonFile);
}
const skeletonFile = await blob.text();
const skeletonLoader = new SkeletonJson(atlasLoader);
skeletonLoader.scale = 1;
skeletonLoader.scale = scale;
return skeletonLoader.readSkeletonData(skeletonFile);
}
public async loadAtlasEditor (path: string, instance: SDK.IWorldInstance, renderer: SDK.Gfx.IWebGLRenderer) {
const projectFile = instance.GetProject().GetProjectFileByName(path);
public async loadAtlasEditor (sid: number, instance: SDK.IWorldInstance, renderer: SDK.Gfx.IWebGLRenderer) {
const projectFile = instance.GetProject().GetProjectFileBySID(sid);
if (!projectFile) return null;
const blob = projectFile.GetBlob();
@ -77,31 +75,31 @@ export class AssetLoader {
}
public async loadSpineTextureEditor (pageName: string, pma = false, instance: SDK.IWorldInstance) {
const projectFile = instance.GetProject().GetProjectFileByName(pageName);
const projectFile = instance.GetProject().GetProjectFileByExportPath(pageName);
if (!projectFile) {
throw new Error(`An error occured while loading the texture: ${pageName}`);
}
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) {
const fullPath = await instance.assets.getProjectFileUrl(path);
if (!fullPath) return null;
const content = await instance.assets.fetchArrayBuffer(fullPath);
if (!content) return null;
const atlasLoader = new AtlasAttachmentLoader(textureAtlas);
const isBinary = path.endsWith(".skel");
if (isBinary) {
const content = await instance.assets.fetchArrayBuffer(fullPath);
if (!content) return null;
const skeletonLoader = new SkeletonBinary(atlasLoader);
skeletonLoader.scale = scale;
return skeletonLoader.readSkeletonData(content);
}
const content = await instance.assets.fetchJson(fullPath);
if (!content) return null;
const skeletonLoader = new SkeletonJson(atlasLoader);
skeletonLoader.scale = scale;
return skeletonLoader.readSkeletonData(content);
@ -133,10 +131,10 @@ export class AssetLoader {
const content = await instance.assets.fetchBlob(fullPath);
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 {
return createImageBitmap(blob, { premultiplyAlpha: pma ? "none" : "premultiply" });
} catch (e) {

View File

@ -8,6 +8,7 @@ spine.Skeleton.yDown = true;
class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
propAtlas = "";
propSkel = "";
propLoaderScale = 1;
propSkin: string[] = [];
propAnimation?: string;
propOffsetX = 0;
@ -33,21 +34,23 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
const properties = this._getInitProperties();
if (properties) {
console.log(properties);
this.propAtlas = properties[0] 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.propAnimation = properties[3] as string;
this.propAnimation = properties[4] as string;
this.propOffsetX = properties[7] as number;
this.propOffsetY = properties[8] as number;
this.propOffsetAngle = properties[9] as number;
this.propScaleX = properties[10] 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._setTicking(true);
@ -81,7 +84,7 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
const propValue = this.propSkel;
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;
this.skeleton = new spine.Skeleton(skeletonData);
@ -96,13 +99,9 @@ class DrawingInstance extends globalThis.ISDKWorldInstanceBase {
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.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._trigger(C3.Plugins.EsotericSoftware_SpineConstruct3.Cnds.OnSkeletonLoaded);
}

View File

@ -14,52 +14,53 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
private layoutView?: SDK.UI.ILayoutView;
private renderer?: SDK.Gfx.IWebGLRenderer;
private currentSkelName = "";
private currentAtlasName = "";
private currentAtlasFileSID = -1;
private textureAtlas?: TextureAtlas;
skeleton?: Skeleton;
state?: AnimationState;
skins: string[] = [];
currentSkinString?: string;
animation?: string;
private assetLoader: AssetLoader;
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
private positioningBounds = false;
private positionModePrevX = 0;
private positionModePrevY = 0;
private positionModePrevAngle = 0;
private spineBounds = {
x: 0,
y: 0,
width: 100,
height: 100,
};
// utils for drawing
private tempVertices = new Float32Array(4096);
// errors
private errors: Record<string, string> = {};
constructor (sdkType: SDK.ITypeBase, inst: SDK.IWorldInstance) {
super(sdkType, inst);
if (!spine) spine = globalThis.spine;
spine.Skeleton.yDown = true;
this.assetLoader = new spine.AssetLoader("editor");
this.assetLoader = new spine.AssetLoader();
this.skeletonRenderer = new spine.SkeletonRendererCore();
(this._inst as any).errors = this.errors;
}
Release () {
}
OnCreate () {
console.log("OnCreate");
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_PROVIDER_MOVE, false);
this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON) as number;
}
OnPlacedInLayout () {
@ -70,7 +71,6 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
this.layoutView ||= iDrawParams.GetLayoutView();
this.renderer ||= iRenderer;
this.loadAtlas();
this.loadSkeleton();
@ -89,8 +89,8 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
offsetY += rectY;
offsetAngle += rectAngle;
const baseScaleX = this._inst.GetWidth() / this.initialBounds.width;
const baseScaleY = this._inst.GetHeight() / this.initialBounds.height;
const baseScaleX = this._inst.GetWidth() / this.spineBounds.width;
const baseScaleY = this._inst.GetHeight() / this.spineBounds.height;
this.skeleton.scaleX = baseScaleX;
this.skeleton.scaleY = baseScaleY;
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_SKELETON_SCALE_X, baseScaleX);
@ -129,7 +129,9 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
vertices[dstIndex + 2] = 0;
}
iRenderer.ResetColor();
iRenderer.SetAlphaBlend();
iRenderer.SetTextureFillMode();
iRenderer.SetTexture(command.texture.texture);
iRenderer.DrawMesh(
vertices.subarray(0, numVertices * 3),
@ -145,59 +147,96 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
iRenderer.SetColorRgba(0.25, 0, 0, 0.25);
iRenderer.LineQuad(this._inst.GetQuad());
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 {
// render placeholder
iRenderer.SetAlphaBlend();
iRenderer.SetColorFillMode();
if (this.HadTextureError())
iRenderer.SetColorRgba(0.25, 0, 0, 0.25);
else
iRenderer.SetColorRgba(0, 0, 0.1, 0.1);
const sdkType = (this._sdkType as any);
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) {
console.log(`Prop change - Name: ${id} - Value: ${value}`);
switch (id) {
case PLUGIN_CLASS.PROP_ATLAS:
this.layoutView?.Refresh();
break;
case PLUGIN_CLASS.PROP_SKELETON:
this.layoutView?.Refresh();
break;
case PLUGIN_CLASS.PROP_SKIN:
this.layoutView?.Refresh();
break;
case PLUGIN_CLASS.PROP_ANIMATION:
this.layoutView?.Refresh();
break;
case PLUGIN_CLASS.PROP_BOUNDS_PROVIDER:
this.setC3Bounds(true);
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X, 0);
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y, 0);
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE, 0);
this.layoutView?.Refresh();
break;
case 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.initialBounds.width = this._inst.GetWidth() / scaleX;
this.initialBounds.height = this._inst.GetHeight() / scaleY;
}
this.positioningBounds = value;
break;
if (id === PLUGIN_CLASS.PROP_ATLAS) {
this.textureAtlas?.dispose();
this.textureAtlas = undefined;
this.skins = [];
this.layoutView?.Refresh();
return;
}
if (id === PLUGIN_CLASS.PROP_SKELETON) {
this.skeleton = undefined;
this.skins = [];
this.layoutView?.Refresh();
return;
}
if (id === PLUGIN_CLASS.PROP_LOADER_SCALE) {
this.skeleton = undefined;
this.skins = [];
this.layoutView?.Refresh();
return;
}
if (id === PLUGIN_CLASS.PROP_SKIN) {
this.skins = [];
this.setSkin();
this.layoutView?.Refresh();
return;
}
if (id === PLUGIN_CLASS.PROP_ANIMATION) {
this.layoutView?.Refresh();
return;
}
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");
@ -209,13 +248,12 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKIN) as string;
if (this.currentSkinString === propValue) return;
this.currentSkinString = propValue;
const skins = propValue === "" ? [] : propValue.split(",");
this.skins = skins;
if (skins.length === 1) {
if (skins.length === 0) {
skeleton.setSkin(null);
} else if (skins.length === 1) {
const skinName = skins[0];
const skin = skeleton.data.findSkin(skinName);
if (!skin) {
@ -223,7 +261,7 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
return;
}
skeleton.setSkin(skins[0]);
} else if (skins.length > 1) {
} else {
const customSkin = new spine.Skin(propValue);
for (const s of skins) {
const skin = skeleton.data.findSkin(s)
@ -238,71 +276,103 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
skeleton.setupPose();
this.update(0);
this.setC3Bounds();
}
private async loadSkeleton () {
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON) as string;
const projectFile = this._inst.GetProject().GetProjectFileByName(propValue);
if (!this.renderer || !this.textureAtlas) return;
if (this.skeleton) return;
if (projectFile && this.textureAtlas) {
if (this.currentSkelName === propValue) return;
this.currentSkelName = propValue;
console.log("Loading skeleton");
const skeletonData = await this.assetLoader.loadSkeletonEditor(propValue, this.textureAtlas, this._inst);
if (!skeletonData) return;
const propValue = this._inst.GetPropertyValue(PLUGIN_CLASS.PROP_SKELETON) as number;
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);
const animationStateData = new spine.AnimationStateData(skeletonData);
this.state = new spine.AnimationState(animationStateData);
this.skeleton = new spine.Skeleton(skeletonData);
const animationStateData = new spine.AnimationStateData(skeletonData);
this.state = new spine.AnimationState(animationStateData);
this.update(0);
this.setSkin();
this.update(0);
this.setBoundsFromBoundsProvider();
this.initBounds();
this.setSkin();
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");
}
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;
let spineBoundsProvider: SpineBoundsProvider;
if (propValue === "animation-skin") {
const { skins, animation } = this;
if ((skins && skins.length > 0) || animation) {
this.spineBoundsProvider = new spine.SkinsAndAnimationBoundsProvider(animation, skins);
spineBoundsProvider = new spine.SkinsAndAnimationBoundsProvider(animation, skins);
} 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") {
this.spineBoundsProvider = new spine.SetupPoseBoundsProvider();
spineBoundsProvider = new spine.SetupPoseBoundsProvider();
} 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);
if (propValue === "AABB") {
this._inst.SetOrigin(.5, .5);
} else {
this._inst.SetOrigin(-x / width, -y / height);
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_X, 0);
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_Y, 0);
this._inst.SetPropertyValue(PLUGIN_CLASS.PROP_BOUNDS_OFFSET_ANGLE, 0);
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) {
@ -316,19 +386,31 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
skeleton.updateWorldTransform(spine.Physics.update);
}
private async loadAtlas () {
if (!this.renderer) return;
private setError (key: string, condition: boolean, message: string) {
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;
this.currentAtlasName = propValue;
const { width, height } = spineBounds;
this.setError(
"boundsNoDimension",
width <= 0 || height <= 0,
"A bounds cannot have negative dimension"
);
const textureAtlas = await this.assetLoader.loadAtlasEditor(propValue, this._inst, this.renderer);
if (!textureAtlas) return;
this.textureAtlas = textureAtlas;
this.layoutView?.Refresh();
return Object.keys(errors).length > 0;
}
GetTexture () {
@ -341,20 +423,15 @@ class MyDrawingInstance extends SDK.IWorldInstanceBase {
}
GetOriginalWidth () {
if (!this.initialBounds) return 100;
return this.initialBounds.width;
return this.spineBounds.width;
}
GetOriginalHeight () {
if (!this.initialBounds) return 100;
return this.initialBounds.height;
return this.spineBounds.height;
}
OnMakeOriginalSize () {
if (!this.initialBounds)
this._inst.SetSize(100, 100);
else
this._inst.SetSize(this.initialBounds.width, this.initialBounds.height);
this._inst.SetSize(this.spineBounds.width, this.spineBounds.height);
}
HasDoubleTapHandler () {

View File

@ -18,9 +18,9 @@
"name": "Atlas",
"desc": "Atlas file"
},
"spine-skeleton-file-blob": {
"name": "Skel Blob",
"desc": "Skel file blob"
"spine-loader-scale": {
"name": "Loader scale",
"desc": "Loader scale"
},
"spine-animation": {
"name": "animation",
@ -30,6 +30,10 @@
"name": "skin",
"desc": "skin"
},
"spine-errors": {
"name": "Errors",
"desc": "errors"
},
"spine-bounds-provider-group": {
"name": "Bounds provider",
"desc": "Select the desired buound provider and fill the respective properties."
@ -38,9 +42,8 @@
"name": "Bounds provider",
"desc": "The bounds provider to use.",
"items": {
"AABB": "AABB Rectangle",
"setup": "Setup pose bounds",
"animation-skin": "Animation + Skin bounds"
"animation-skin": "Animation/Skin bounds"
}
},
"spine-bounds-provider-move": {

View File

@ -15,17 +15,15 @@ const PLUGIN_ID = "EsotericSoftware_SpineConstruct3";
const PLUGIN_CATEGORY = "general";
let app = null;
const PLUGIN_CLASS = class MyDrawingPlugin extends SDK.IPluginBase {
static PROP_ATLAS = "spine-atlas-file";
static PROP_SKELETON = "spine-skeleton-file";
static PROP_LOADER_SCALE = "spine-loader-scale";
static PROP_SKIN = "spine-skin";
static PROP_ANIMATION = "spine-animation";
static PROP_ERRORS = "spine-errors";
static PROP_RATIO_WIDTH = "spine-restore-ratio-width";
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 = "spine-bounds-provider";
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_ANIMATION_SKIN = "animation-skin";
static TYPE_BOUNDS_AABB = "AABB";
constructor () {
super(PLUGIN_ID);
SDK.Lang.PushContext("plugins." + PLUGIN_ID.toLowerCase());
// @ts-ignore
@ -69,37 +65,26 @@ const PLUGIN_CLASS = class MyDrawingPlugin extends SDK.IPluginBase {
SDK.Lang.PushContext(".properties");
this._info.SetProperties([
new SDK.PluginProperty("text", MyDrawingPlugin.PROP_ATLAS, ""),
new SDK.PluginProperty("text", MyDrawingPlugin.PROP_SKELETON, ""),
new SDK.PluginProperty("projectfile", MyDrawingPlugin.PROP_ATLAS, ""),
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_ANIMATION, ""),
new SDK.PluginProperty("projectfile", MyDrawingPlugin.PROP_SKELETON_BLOB, ""),
new SDK.PluginProperty("info", MyDrawingPlugin.PROP_ERRORS, {
infoCallback (inst) {
const atlas = inst.GetInstance().GetPropertyValue(MyDrawingPlugin.PROP_ATLAS);
const skeleton = inst.GetInstance().GetPropertyValue(MyDrawingPlugin.PROP_SKELETON);
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;
const errors = (inst.GetInstance() as unknown as { errors: Record<string, string> }).errors;
return Object.values(errors).reduce((acc, next) => {
return acc === "" ? next : `${acc}\n${next}`;
}, "");
},
}),
new SDK.PluginProperty("group", MyDrawingPlugin.PROP_BOUNDS_PROVIDER_GROUP),
new SDK.PluginProperty("combo", MyDrawingPlugin.PROP_BOUNDS_PROVIDER, {
initialValue: "setup",
items: [
MyDrawingPlugin.TYPE_BOUNDS_SETUP,
MyDrawingPlugin.TYPE_BOUNDS_ANIMATION_SKIN,
MyDrawingPlugin.TYPE_BOUNDS_AABB
],
}),
new SDK.PluginProperty("check", MyDrawingPlugin.PROP_BOUNDS_PROVIDER_MOVE, false),

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

File diff suppressed because one or more lines are too long