消消方块阵换皮表情
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
14 KiB

1 week ago
//*//
import { cclegacy, Color, Director, director, Game, game, gfx, Material, MeshBuffer, Node, renderer, resources, sp, Texture2D, UIRenderer } from 'cc';
import { EDITOR, JSB } from 'cc/env';
let _uid = 0;
let _texid = 0;
let _hash = -1;
let _texCount = 0;
let _spineCache = [];
//是否使用,shader传参变色效果
//如不需要,请设置false , 节省shader计算
const useShaderVar = true;
declare module "cc" {
interface Component {
shaderVar: number;
// 类型,0 不变,1 置灰,2闪白?,3待定?,4待定?
// 参数整数部分为类型,小数部分为插值,
// 例如 1.999,代表是 ,近似置灰100%
// 例如 2.5 代表是,闪白50%插值
// 默认原色 <=1
}
}
let _spineTexture: Texture2D = null;
let _spineBuffers: Float32Array = null;
let _texMaps: Map<number, number> = new Map();
let _materialMaps: Map<number, any> = new Map();
const _region = new gfx.BufferTextureCopy();
_region.texExtent.width = 128;
_region.texExtent.height = 128;
_region.texOffset.x = 0;
_region.texOffset.y = 0;
//@ts-ignore
Material.prototype.spineCache = [];
export const SpineBatch: any = {
enable: false,
parent: null,
next: function () {
_texMaps.clear();
_texCount = 0;
_texid = 0;
_hash = -1;
},
reset: function () {
this.next();
_uid = 0;
},
active: function () {
return this.enable && this.parent;
},
update: function () {
if (!this.active()) return;
// _spineTexture?.uploadData(_spineBuffers);
for (let value of _materialMaps.values()) {
if (value) {
value.n = 0;
}
}
if (_uid > 0 && _spineTexture) {
//@ts-ignore
const gfxDevice = _spineTexture._getGFXDevice();
const gfxTexture = _spineTexture.getGFXTexture();
const hight = (~~((_uid * 4 - 1) / 128)) + 1;
_region.texExtent.height = hight > 128 ? 128 : hight;
gfxDevice.copyBuffersToTexture([_spineBuffers], gfxTexture, [_region]);
}
}
};
const _getMaterialForBlendAndTint = function (comp: any, mat: any) {
// _mat = mat; //保存当前
let key = _hash = mat.hash;
let caches = _materialMaps.get(key);
if (caches === undefined) {
caches = { mats: [], n: 0 };
_materialMaps.set(key, caches);
}
let inc = caches.n++;
let materialCache = caches.mats;
let inst: any = materialCache[inc];
if (inst !== undefined) {
_spineCache = inst.spineCache;
return inst;
}
let type = key & 0xf;
let src = (key >> 4) & 0xf;
let dst = (key >> 8) & 0xf;
const material = SpineBatch.parent;
inst = new renderer.MaterialInstance({ parent: material });
materialCache[inc] = inst;
inst.overridePipelineStates({
blendState: {
blendColor: Color.WHITE,
targets: [{
blendEq: gfx.BlendOp.ADD,
blendAlphaEq: gfx.BlendOp.ADD,
blendSrc: src,
blendDst: dst,
blendSrcAlpha: src,
blendDstAlpha: dst,
}],
},
});
inst.recompileShaders({ TWO_COLORED: (type === 1) ? true : false, USE_LOCAL: false, USE_SHADER_VAR: useShaderVar });
inst.setProperty('spineData', _spineTexture);
_spineCache = inst['spineCache'] = [];
inst.addRef();
return inst;
}
//预加载多纹理材质
const loadMaterial = function () {
if (SpineBatch.parent) return;
resources.load("multSpines/Mult-spine", Material, (err, material) => {
if (!err) {
//material.passes[0].tryCompile();
SpineBatch.parent = material;
material
material.addRef();
}
});
}
const initSpineDatas = function () {
SpineBatch.enable = false;
const size = 128;
const gfxDevice = director.root!.device;
const hasFeatureFloatTexture = gfxDevice.getFormatFeatures(gfx.Format.RGBA32F) & gfx.FormatFeatureBit.SAMPLED_TEXTURE;
if (hasFeatureFloatTexture) {
SpineBatch.enable = true;
_spineTexture = new Texture2D();
_spineBuffers = new Float32Array(size * size * 4);
_spineTexture.setFilters(Texture2D.Filter.NEAREST, Texture2D.Filter.NEAREST);
_spineTexture.reset({ width: size, height: size, format: Texture2D.PixelFormat.RGBA32F, mipmapLevel: 0 });
_spineTexture.uploadData(_spineBuffers);
}
}
const injectSpine = function () {
const Skeleton: any = sp.Skeleton.prototype;
Skeleton.shaderVar = 0; //shader自定义参数
Skeleton.isMult = false; //是否多纹理合批
//强制关闭合批
Object.defineProperty(Skeleton, "enableBatch", {
get: function () {
if (this.customMaterial)
return this._enableBatch;
return false;
},
set: function (value: boolean) {
if (this.customMaterial) {
if (value !== this._enableBatch) {
this._enableBatch = value;
this._updateBatch();
}
}
}
});
//获取共享材质
const getMaterialForBlendAndTint = Skeleton.getMaterialForBlendAndTint;
Skeleton.getMaterialForBlendAndTint = function (src: any, dst: any, type: any) {
if (!SpineBatch.active() || this.customMaterial) {
this.isMult = false;
return getMaterialForBlendAndTint.call(this, src, dst, type);
}
let hash = type | (src << 4) | (dst << 8);
this.isMult = true;
return { hash: hash };
}
//绘画数据处理
const render = Skeleton._render;
Skeleton._render = function (batcher: any): void {
if (!this.isMult) return render.call(this, batcher);
let indicesCount = 0;
if (this.renderData && this._drawList.length > 0) {
const rd = this.renderData;
const chunk = rd.chunk;
const accessor = chunk.vertexAccessor;
const meshBuffer = rd.getMeshBuffer()!;
const origin = meshBuffer.indexOffset;
//@ts-ignore
let floatStride = meshBuffer.vertexFormatBytes >> 2;
let isMultDraw = this._drawList.length > 1;
let offset = chunk.vertexOffset;
let length = rd.vertexCount;
let vbuff = rd.chunk.vb;
let ibuff = rd.indices;
let isCommit = false;
// Fill index buffer
for (let i = 0; i < this._drawList.length; i++) {
const dc = this._drawList.data[i];
if (dc.texture) {
batcher.commitMiddleware(this, meshBuffer, origin + dc.indexOffset, dc.indexCount, dc.texture, dc.material, this._enableBatch);
isCommit = true;
if (isMultDraw) {
let j = dc.indexOffset;
length = dc.indexCount;
let id = _uid * 10 + _texid;
while (length--) {
vbuff[(ibuff[j++] - offset) * floatStride + 2] = id; //-offset
}
}
}
indicesCount += dc.indexCount;
}
if (!isMultDraw && isCommit) {
let id = _uid * 10 + _texid;
for (let i = 0, j = 0; i < length; i++) {
vbuff[j + 2] = id;
j += floatStride;
}
}
if (isCommit) {
let i = 16 * _uid++;
let datas = _spineBuffers;
let m = this.node.worldMatrix;
datas[i] = m.m00;
datas[i + 1] = m.m01;
datas[i + 2] = m.m02;
datas[i + 3] = this.shaderVar; //m.m03;
datas[i + 4] = m.m04;
datas[i + 5] = m.m05;
datas[i + 6] = m.m06;
datas[i + 7] = m.m07;
datas[i + 8] = m.m08;
datas[i + 9] = m.m09;
datas[i + 10] = m.m10;
datas[i + 11] = m.m11;
datas[i + 12] = m.m12;
datas[i + 13] = m.m13;
datas[i + 14] = m.m14;
datas[i + 15] = m.m15;
}
const subIndices = rd.indices!.subarray(0, indicesCount);
accessor.appendIndices(chunk.bufferId, subIndices);
accessor.getMeshBuffer(chunk.bufferId).setDirty();
}
}
}
//多纹理合批流程
const injectBatch = function () {
const Batcher2D: any = cclegacy.internal.Batcher2D.prototype;
const commitMiddleware = Batcher2D.commitMiddleware;
Batcher2D.commitMiddleware = function (
comp: UIRenderer,
meshBuffer: MeshBuffer,
indexOffset: number,
indexCount: number,
tex: any,
mat: any,
enableBatch: boolean,
) {
//@ts-ignore
if (!comp.isMult) {
return commitMiddleware.call(this, comp, meshBuffer, indexOffset, indexCount, tex, mat, enableBatch);
}
// check if need merge draw batch
const texture = tex.getGFXTexture();
let tid = texture.objectID;
_texid = _texMaps.get(tid);
let isFlush = (_texid === undefined && _texCount >= 8);
if (!isFlush && this._middlewareBuffer === meshBuffer && _hash === mat.hash
&& this._middlewareIndexStart + this._middlewareIndexCount === indexOffset
&& this._currLayer === comp.node.layer) {
this._middlewareIndexCount += indexCount;
} else {
this.autoMergeBatches(this._currComponent!);
this.resetRenderStates();
this._currComponent = comp;
this._currTexture = texture;
this._currSampler = tex.getGFXSampler();
this._currTextureHash = tex.getHash();
this._currLayer = comp.node.layer;
this._currSamplerHash = this._currSampler.hash;
this._currHash = 0;
this._currTransform = null;//enableBatch ? null : comp.node;
this._middlewareEnableBatch = false;//enableBatch;
this._middlewareBuffer = meshBuffer;
this._middlewareIndexStart = indexOffset;
this._middlewareIndexCount = indexCount;
this._currMaterial = _getMaterialForBlendAndTint(comp, mat);
}
this._currIsMiddleware = true;
if (_texCount === 0 || _texid === undefined) {
_texid = _texCount++;
_texMaps.set(tid, _texid);
}
if (_spineCache[_texid] !== tid) {
_spineCache[_texid] = tid;
this._currMaterial.setProperty("texture" + _texid, tex);
}
}
const mergeBatchesForMiddleware = Batcher2D.mergeBatchesForMiddleware;
Batcher2D.mergeBatchesForMiddleware = function (renderComp: UIRenderer) {
if (this._currIsMiddleware) SpineBatch.next();
mergeBatchesForMiddleware.call(this, renderComp);
}
}
//游戏启动前,务必加载多纹理材质
game.once(Game.EVENT_GAME_INITED, () => {
if (EDITOR || JSB || !sp.Skeleton) return;
initSpineDatas();
if (SpineBatch.enable) {
loadMaterial();
injectSpine();
injectBatch();
}
});
game.on(Game.EVENT_RESTART, () => {
if (EDITOR || JSB || !sp.Skeleton) return;
if (SpineBatch.enable) {
SpineBatch.parent = null;
loadMaterial();
}
});
director.on(Director.EVENT_AFTER_DRAW, (dt) => { SpineBatch.reset(); });
director.on(Director.EVENT_BEFORE_COMMIT, (dt) => { SpineBatch.update(); });
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//以下代码,仅供测试demo使用,请不要在项目中使用
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const changeBatch = function (node: Node, batch: boolean) {
if (!node) return;
let skeleton = node.getComponent(sp.Skeleton);
if (skeleton) {
skeleton.enableBatch = batch;
skeleton.material = null;
//@ts-ignore
skeleton._updateBatch();
}
let childs = node.children;
for (let i = 0; i < childs.length; i++) {
changeBatch(childs[i], batch);
}
}
export const switchMultSpine = function (useMult: boolean, useBatch: boolean) {
SpineBatch.enable = useMult;
const Skeleton: any = sp.Skeleton.prototype;
if (!useMult) {
Object.defineProperty(Skeleton, "enableBatch", {
get: function () {
return this._enableBatch;
},
set: function (value: boolean) {
if (value !== this._enableBatch) {
this._enableBatch = value;
this._updateBatch();
}
}
});
} else {
Object.defineProperty(Skeleton, "enableBatch", {
get: function () {
if (this.customMaterial)
return this._enableBatch;
return false;
},
set: function (value: boolean) {
if (this.customMaterial) {
if (value !== this._enableBatch) {
this._enableBatch = value;
this._updateBatch();
}
}
}
});
}
let scene = director.getScene() as Node;
if (scene) {
changeBatch(scene, useBatch);
}
}
// */