[engine] Spine 组件所有特性原生平台适配

This commit is contained in:
SmallMain 2022-07-05 11:06:46 +08:00
parent 0a9774f6e9
commit 3f8f7ffd34
3 changed files with 359 additions and 166 deletions

View File

@ -505,6 +505,12 @@ sp.Skeleton = cc.Class({
if (isMultiSupport) { if (isMultiSupport) {
if (!this.enableBatch) this.enableBatch = true; if (!this.enableBatch) this.enableBatch = true;
} }
if (CC_JSB) {
if (this._nativeSkeleton) {
this._nativeSkeleton.setUseMulti(isMultiSupport);
}
}
} }
this._materialCache = {}; this._materialCache = {};
}, },
@ -1012,29 +1018,21 @@ sp.Skeleton = cc.Class({
}, },
/** /**
* 获取 attachment region * 获取 attachment region 数据
*/ */
getRegion(slotName, attachmentName) { getRegionData(slotName, attachmentName) {
const attachment = this.getAttachment(slotName, attachmentName); const attachment = this.getAttachment(slotName, attachmentName);
if (attachment) { if (attachment) return new sp.RegionData(attachment);
return attachment.region;
}
return null; return null;
}, },
/** /**
* 修改 attachment region * 修改 attachment region 数据
*/ */
setRegion(slotName, attachmentName, region) { setRegionData(slotName, attachmentName, regionData) {
const attachment = this.getAttachment(slotName, attachmentName); const attachment = this.getAttachment(slotName, attachmentName);
if (attachment) { if (attachment) {
attachment.region = region; regionData.assignToAttachment(attachment);
if (attachment instanceof sp.spine.MeshAttachment) {
attachment.updateUVs();
} else if (attachment instanceof sp.spine.RegionAttachment) {
attachment.setRegion(region);
attachment.updateOffset();
}
this.setVertsDirty(); this.setVertsDirty();
return true; return true;
} }

View File

@ -28,6 +28,228 @@
*/ */
let SkeletonCache = !CC_JSB && require('./skeleton-cache').sharedCache; let SkeletonCache = !CC_JSB && require('./skeleton-cache').sharedCache;
/**
* Spine Attachment Region 数据
*/
class RegionData {
static middlewareTextureID = -1;
static updateUV(region) {
const texture = CC_JSB ? region.texture2D : region.texture._texture;
if (region.rotate) {
region.u = region.x / texture.width;
region.v = region.y / texture.height;
region.u2 = (region.x + region.height) / texture.width;
region.v2 = (region.y + region.width) / texture.height;
} else {
region.u = region.x / texture.width;
region.v = region.y / texture.height;
region.u2 = (region.x + region.width) / texture.width;
region.v2 = (region.y + region.height) / texture.height;
}
}
x;
y;
degrees;
texture;
texture2D;
u;
v;
u2;
v2;
width;
height;
rotate;
offsetX;
offsetY;
originalWidth;
originalHeight;
constructor(attachmentOrSpriteFrame) {
if (attachmentOrSpriteFrame instanceof cc.SpriteFrame) {
this.initWithSpriteFrame(attachmentOrSpriteFrame);
} else if (attachmentOrSpriteFrame != null) {
this.initWithAttachment(attachmentOrSpriteFrame);
}
}
initWithSpriteFrame(spriteFrame) {
const texture = spriteFrame.getTexture();
const rect = spriteFrame.getRect();
const origSize = spriteFrame.getOriginalSize();
const _offset = spriteFrame.getOffset();
const rotate = spriteFrame.isRotated();
const offset = cc.v2(
(origSize.width - rect.width) * 0.5 + _offset.x,
(origSize.height - rect.height) * 0.5 + _offset.y,
);
const degrees = rotate ? 270 : 0;
this.x = rect.x;
this.y = rect.y;
this.width = rect.width;
this.height = rect.height;
this.originalWidth = origSize.width;
this.originalHeight = origSize.height;
this.offsetX = offset.x;
this.offsetY = offset.y;
this.rotate = degrees != 0;
this.degrees = degrees;
this.updateWithTexture2D(texture);
}
initWithAttachment(attachment) {
if (CC_JSB) {
this.x = attachment.regionX;
this.y = attachment.regionY;
this.width = attachment.regionWidth;
this.height = attachment.regionHeight;
this.originalWidth = attachment.regionOriginalWidth;
this.originalHeight = attachment.regionOriginalHeight;
this.offsetX = attachment.regionOffsetX;
this.offsetY = attachment.regionOffsetY;
this.degrees = attachment.regionDegrees;
this.rotate = this.degrees !== 0;
this.texture = attachment.textureForJSB;
this.texture2D = attachment.getTexture2D();
this.updateUV();
} else {
const region = attachment.region;
this.x = region.x;
this.y = region.y;
this.width = region.width;
this.height = region.height;
this.originalWidth = region.originalWidth;
this.originalHeight = region.originalHeight;
this.offsetX = region.offsetX;
this.offsetY = region.offsetY;
this.rotate = region.rotate;
this.degrees = region.degrees;
this.texture = region.texture;
this.texture2D = region.texture._texture;
this.u = region.u;
this.u2 = region.u2;
this.v = region.v;
this.v2 = region.v2;
}
}
updateUV() {
RegionData.updateUV(this);
}
updateWithPackedFrame(packedFrame) {
this.x = packedFrame.x;
this.y = packedFrame.y;
this.updateWithTexture2D(packedFrame.texture);
}
updateWithTexture2D(texture2d) {
if (CC_JSB) {
const spTex = new middleware.Texture2D();
spTex.setRealTextureIndex(RegionData.middlewareTextureID--);
spTex.setPixelsWide(texture2d.width);
spTex.setPixelsHigh(texture2d.height);
spTex.setNativeTexture(texture2d.getImpl());
this.texture = spTex;
} else {
this.texture = new sp.SkeletonTexture({
width: texture2d.width,
height: texture2d.height,
});
this.texture.setRealTexture(texture2d);
}
this.texture2D = texture2d;
this.updateUV();
}
toSpriteFrame(strict) {
if (strict && (this.degrees !== 270 || this.degrees !== 0)) {
return null;
}
const frame = new cc.SpriteFrame(
this.texture2D,
cc.rect(this.x, this.y, this.width, this.height),
this.rotate, // 如果 region 不是 0 或 270 则会出现问题
cc.v2(this.offsetX - (this.originalWidth - this.width) * 0.5, this.offsetY - (this.originalHeight - this.height) * 0.5),
cc.size(this.originalWidth, this.originalHeight),
);
return frame;
}
assignToAttachment(attachment, strict = true, resetDynamicAtlas = true) {
if (CC_JSB) {
if (resetDynamicAtlas) {
// 如果有在使用动态合图则先还原
if (attachment && attachment._spriteFrame) {
const spriteFrame = attachment._spriteFrame;
attachment._spriteFrame = null;
spriteFrame.destroy();
}
}
attachment._texture2D = this.texture2D;
attachment.setRegionForJSB(this.texture, { x: this.x, y: this.y, w: this.width, h: this.height }, cc.size(this.originalWidth, this.originalHeight), cc.v2(this.offsetX, this.offsetY), this.degrees);
} else {
const region = attachment.region;
if (resetDynamicAtlas) {
// 如果有在使用动态合图则先还原
if (region && region._spriteFrame) {
const spriteFrame = region._spriteFrame;
region._spriteFrame = null;
spriteFrame.destroy();
}
}
if (strict) {
region.x = this.x;
region.y = this.y;
region.width = this.width;
region.height = this.height;
region.originalWidth = this.originalWidth;
region.originalHeight = this.originalHeight;
region.offsetX = this.offsetX;
region.offsetY = this.offsetY;
region.rotate = this.rotate;
region.degrees = this.degrees;
region.texture = this.texture;
region.u = this.u;
region.u2 = this.u2;
region.v = this.v;
region.v2 = this.v2;
}
if (attachment instanceof sp.spine.MeshAttachment) {
attachment.updateUVs();
} else if (attachment instanceof sp.spine.RegionAttachment) {
attachment.setRegion(region);
attachment.updateOffset();
}
}
}
reset() {
this.texture = null;
this.texture2D = null;
}
}
/** /**
* !#en The skeleton data of spine. * !#en The skeleton data of spine.
* !#zh Spine 骨骼数据 * !#zh Spine 骨骼数据
@ -139,74 +361,7 @@ let SkeletonData = cc.Class({
statics: { statics: {
preventDeferredLoadDependents: true, preventDeferredLoadDependents: true,
cloneId: 0,
createRegion(spriteFrame, original = undefined) {
const region = new sp.spine.TextureAtlasRegion();
const texture = spriteFrame.getTexture();
const rect = spriteFrame.getRect();
const origSize = spriteFrame.getOriginalSize();
const _offset = spriteFrame.getOffset();
const rotate = spriteFrame.isRotated();
const offset = cc.v2(
(origSize.width - rect.width) * 0.5 + _offset.x,
(origSize.height - rect.height) * 0.5 + _offset.y,
);
const degrees = rotate ? 270 : 0;
if (original) {
region.name = original.name;
region.page = original.page;
}
region.x = rect.x;
region.y = rect.y;
region.width = rect.width;
region.height = rect.height;
region.originalWidth = origSize.width;
region.originalHeight = origSize.height;
region.offsetX = offset.x;
region.offsetY = offset.y;
region.rotate = degrees != 0;
region.degrees = degrees;
const skelTex = new sp.SkeletonTexture({
width: texture.width,
height: texture.height,
});
skelTex.setRealTexture(texture);
region.texture = skelTex;
this.updateRegionUV(region);
return region;
},
updateRegionUV(region) {
const texture = region.texture._texture;
if (region.rotate) {
region.u = region.x / texture.width;
region.v = region.y / texture.height;
region.u2 = (region.x + region.height) / texture.width;
region.v2 = (region.y + region.width) / texture.height;
} else {
region.u = region.x / texture.width;
region.v = region.y / texture.height;
region.u2 = (region.x + region.width) / texture.width;
region.v2 = (region.y + region.height) / texture.height;
}
},
createSpriteFrame(region) {
const frame = new cc.SpriteFrame(
region.texture._texture,
cc.rect(region.x, region.y, region.width, region.height),
region.rotate, // 如果 region 不是 0 或 270 则会出现问题
cc.v2(region.offsetX - (region.originalWidth - region.width) * 0.5, region.offsetY - (region.originalHeight - region.height) * 0.5),
cc.size(region.originalWidth, region.originalHeight),
);
return frame;
},
}, },
// PUBLIC // PUBLIC
@ -234,7 +389,6 @@ let SkeletonData = cc.Class({
this._skinsEnum = null; this._skinsEnum = null;
this._animsEnum = null; this._animsEnum = null;
} }
this._cloneId = 0;
}, },
ensureTexturesLoaded (loaded, caller) { ensureTexturesLoaded (loaded, caller) {
@ -396,48 +550,55 @@ let SkeletonData = cc.Class({
*/ */
clone: function () { clone: function () {
const cloned = new SkeletonData(); const cloned = new SkeletonData();
cloned._cloneId = this._cloneId + 1; SkeletonData.cloneId++;
const suffix = '(clone ' + String(cloned._cloneId) + ')'; const suffix = '(clone ' + String(SkeletonData.cloneId) + ')';
cloned._uuid = this._uuid + suffix; cloned._uuid = this._uuid.split('(')[0] + suffix;
cloned.name = this.name + suffix; cloned.name = this.name + suffix;
cloned.scale = this.scale; cloned.scale = this.scale;
cloned.textureNames = this.textureNames;
cloned.textures = this.textures;
cloned._atlasText = this._atlasText; cloned._atlasText = this._atlasText;
cloned.textureNames = this.textureNames;
cloned._skeletonJson = this._skeletonJson; cloned._skeletonJson = this._skeletonJson;
cloned.textures = this.textures;
if (CC_JSB) {
const realUuid = cloned._uuid;
cloned._uuid = this._uuid;
cloned._nativeUrl = this._nativeUrl;
cloned._native = this._native;
cloned.nativeUrl; // 触发 nativeUrl getter
cloned._uuid = realUuid;
} else {
cloned._buffer = this._buffer; cloned._buffer = this._buffer;
}
cloned.getRuntimeData();
return cloned; return cloned;
}, },
destroy() { _destroyFromDynamicAtlas() {
// 删除动态图集
if (this._atlasCache) {
const regions = this._atlasCache.regions;
for (const region of regions) {
if (region._spriteFrame) {
region._spriteFrame.destroy();
region._spriteFrame = null;
}
}
}
if (this._skeletonCache) { if (this._skeletonCache) {
const skins = this._skeletonCache.skins; const skins = this._skeletonCache.skins;
for (const skin of skins) { for (const skin of skins) {
for (const attachments of skin.attachments) { for (const attachments of skin.attachments) {
for (const key in attachments) { for (const key in attachments) {
const region = attachments[key].region; const region = CC_JSB ? attachments[key] : attachments[key].region;
if (region && region._spriteFrame) { if (region && region._spriteFrame) {
region._spriteFrame.destroy(); const spriteFrame = region._spriteFrame;
region._spriteFrame = null; region._spriteFrame = null;
spriteFrame.destroy();
} }
} }
} }
} }
} }
},
destroy() {
this._destroyFromDynamicAtlas();
SkeletonCache.removeSkeleton(this._uuid); SkeletonCache.removeSkeleton(this._uuid);
this._super(); this._super();
}, },
}); });
sp.SkeletonData = module.exports = SkeletonData; sp.SkeletonData = module.exports = SkeletonData;
sp.RegionData = RegionData;

View File

@ -84,6 +84,7 @@ let _r, _g, _b, _fr, _fg, _fb, _fa, _dr, _dg, _db, _da;
let _comp, _buffer, _renderer, _node, _needColor, _vertexEffect; let _comp, _buffer, _renderer, _node, _needColor, _vertexEffect;
let _packedRegions = []; let _packedRegions = [];
let _tmpRegionData = new sp.RegionData();
function _getSlotMaterial (tex, blendMode) { function _getSlotMaterial (tex, blendMode) {
let src, dst; let src, dst;
@ -225,9 +226,19 @@ export default class SpineAssembler extends Assembler {
const skins = comp.skeletonData._skeletonCache.skins; const skins = comp.skeletonData._skeletonCache.skins;
root: for (const skin of skins) { root: for (const skin of skins) {
for (const attachment of skin.attachments) { for (const attachments of skin.attachments) {
for (const key in attachment) { for (const key in attachments) {
const region = attachment[key].region; if (CC_JSB) {
const attachment = attachments[key];
if (attachment && attachment.getTexture2D) {
const texture = attachment.getTexture2D(comp.skeletonData);
if (texture) {
this.checkAndSwitchMaterial(comp, texture, material);
break root;
}
}
} else {
const region = attachments[key].region;
if (region && region.texture) { if (region && region.texture) {
this.checkAndSwitchMaterial(comp, region.texture._texture, material); this.checkAndSwitchMaterial(comp, region.texture._texture, material);
break root; break root;
@ -236,41 +247,32 @@ export default class SpineAssembler extends Assembler {
} }
} }
} }
}
comp._dataDirty = false; comp._dataDirty = false;
} }
} }
bindPackedRegion(attachment, region) { updatePackedAttachment(attachment, strict) {
_tmpRegionData.assignToAttachment(attachment, strict, false);
const region = CC_JSB ? attachment : attachment.region;
const frame = region._spriteFrame; const frame = region._spriteFrame;
sp.SkeletonData.updateRegionUV(region);
if (attachment instanceof sp.spine.MeshAttachment) {
attachment.updateUVs();
} else {
attachment.setRegion(region);
attachment.updateOffset();
}
region._original._ref++; region._original._ref++;
frame.once("_resetDynamicAtlasFrame", () => { frame.once("_resetDynamicAtlasFrame", () => {
region.x = region._original._x; _tmpRegionData.initWithAttachment(attachment);
region.y = region._original._y;
region.texture = region._original._texture;
region._original._ref--;
_tmpRegionData.x = region._original._x;
_tmpRegionData.y = region._original._y;
_tmpRegionData.texture = region._original._texture;
if (CC_JSB) _tmpRegionData.texture2D = region._original._texture2D;
region._original._ref--;
if (region._original._ref <= 0) { if (region._original._ref <= 0) {
region._original = null; region._original = null;
} }
// update uv _tmpRegionData.assignToAttachment(attachment, true, false);
sp.SkeletonData.updateRegionUV(region); _tmpRegionData.reset();
if (attachment instanceof sp.spine.MeshAttachment) {
attachment.updateUVs();
} else {
attachment.setRegion(region);
attachment.updateOffset();
}
}); });
} }
@ -287,20 +289,57 @@ export default class SpineAssembler extends Assembler {
for (const attachments of skin.attachments) { for (const attachments of skin.attachments) {
for (const key in attachments) { for (const key in attachments) {
const attachment = attachments[key]; const attachment = attachments[key];
const region = attachment.region; if (attachment) {
if (CC_JSB) {
if (!attachment._original && attachment.getTexture2D) {
const texture = attachment.getTexture2D(comp.skeletonData);
if (texture && texture.packable) {
if (attachment._spriteFrame) {
const spriteFrame = attachment._spriteFrame;
attachment._spriteFrame = null;
spriteFrame.destroy();
}
_tmpRegionData.initWithAttachment(attachment);
const frame = _tmpRegionData.toSpriteFrame();
const packedFrame = cc.dynamicAtlasManager.insertSpriteFrame(frame);
if (packedFrame) {
frame._setDynamicAtlasFrame(packedFrame);
if (region) { attachment._original = {
if (region._original) { _texture2D: texture,
_texture: _tmpRegionData.texture,
_x: attachment.regionX,
_y: attachment.regionY,
_ref: 0,
};
attachment._spriteFrame = frame;
_tmpRegionData.updateWithPackedFrame(packedFrame);
this.updatePackedAttachment(attachment);
} else {
frame.destroy();
}
}
}
} else {
const region = attachment.region;
const alreadyInAtlas = !!region._original;
if (alreadyInAtlas) {
// 可能出现多个 attachment 共用同一个 region // 可能出现多个 attachment 共用同一个 region
if (_packedRegions.includes(region)) { if (_packedRegions.includes(region)) {
this.bindPackedRegion(attachment, region); this.updatePackedAttachment(attachment, false);
} }
} else if (region.texture && region.texture._texture.packable) { } else if (region.texture && region.texture._texture.packable) {
if (region._spriteFrame) { if (region._spriteFrame) {
region._spriteFrame.destroy(); const spriteFrame = region._spriteFrame;
region._spriteFrame = null; region._spriteFrame = null;
spriteFrame.destroy();
} }
const frame = sp.SkeletonData.createSpriteFrame(region); _tmpRegionData.initWithAttachment(attachment);
const frame = _tmpRegionData.toSpriteFrame();
const packedFrame = cc.dynamicAtlasManager.insertSpriteFrame(frame); const packedFrame = cc.dynamicAtlasManager.insertSpriteFrame(frame);
if (packedFrame) { if (packedFrame) {
frame._setDynamicAtlasFrame(packedFrame); frame._setDynamicAtlasFrame(packedFrame);
@ -312,18 +351,11 @@ export default class SpineAssembler extends Assembler {
_ref: 0, _ref: 0,
}; };
region.texture = new sp.SkeletonTexture({
width: packedFrame.texture.width,
height: packedFrame.texture.height,
});
region.texture.setRealTexture(packedFrame.texture);
region.x = packedFrame.x;
region.y = packedFrame.y;
// update uv
region._spriteFrame = frame; region._spriteFrame = frame;
this.bindPackedRegion(attachment, region);
_tmpRegionData.updateWithPackedFrame(packedFrame);
this.updatePackedAttachment(attachment);
_packedRegions.push(region); _packedRegions.push(region);
} else { } else {
@ -336,7 +368,9 @@ export default class SpineAssembler extends Assembler {
} }
} }
} }
}
_tmpRegionData.reset();
_packedRegions.length = 0; _packedRegions.length = 0;
} }