@senspark/ee
Version:
utility library for cocos creator
218 lines (184 loc) • 7.32 kB
text/typescript
import assert = require('assert');
const { ccclass, disallowMultiple, executeInEditMode, menu } = cc._decorator;
const assembler = {
useModel: false,
createData(sprite: cc.Sprite): any {
const renderData = sprite.requestRenderData();
// 0-4 for local verts
// 5-20 for world verts
renderData.dataLength = 20;
renderData.vertexCount = 16;
renderData.indiceCount = 54;
return renderData;
},
updateRenderData(sprite: cc.Sprite, batchData: any): void {
const frame = sprite.spriteFrame;
const dynamicAtlasManager = (cc as any).dynamicAtlasManager;
// TODO: Material API design and export from editor could affect the material activation process
// need to update the logic here
if (frame) {
if (!(frame as any)._original && dynamicAtlasManager) {
dynamicAtlasManager.insertSpriteFrame(frame);
}
if (sprite._material._texture !== (frame as any)._texture) {
(sprite as any)._activateMaterial();
}
}
const renderData = sprite._renderData;
if (renderData && frame) {
const vertDirty = renderData.vertDirty;
if (vertDirty) {
this.updateVerts(sprite);
this.updateWorldVerts(sprite);
}
}
},
updateVerts(sprite: cc.Sprite): void {
const renderData = sprite._renderData;
const data = renderData._data;
const node = sprite.node;
const width = node.width;
const height = node.height;
const appx = node.anchorX * width;
const appy = node.anchorY * height;
const frame = sprite.spriteFrame;
const leftWidth = frame.insetLeft;
const rightWidth = frame.insetRight;
const topHeight = frame.insetTop;
const bottomHeight = frame.insetBottom;
let sizableWidth = width - leftWidth - rightWidth;
let sizableHeight = height - topHeight - bottomHeight;
let xScale = width / (leftWidth + rightWidth);
let yScale = height / (topHeight + bottomHeight);
xScale = (isNaN(xScale) || xScale > 1) ? 1 : xScale;
yScale = (isNaN(yScale) || yScale > 1) ? 1 : yScale;
sizableWidth = sizableWidth < 0 ? 0 : sizableWidth;
sizableHeight = sizableHeight < 0 ? 0 : sizableHeight;
data[0].x = -appx;
data[0].y = -appy;
data[1].x = leftWidth * xScale - appx;
data[1].y = bottomHeight * yScale - appy;
data[2].x = data[1].x + sizableWidth;
data[2].y = data[1].y + sizableHeight;
data[3].x = width - appx;
data[3].y = height - appy;
renderData.vertDirty = false;
},
fillBuffers(sprite: cc.Sprite, renderer: any): void {
if (renderer.worldMatDirty) {
this.updateWorldVerts(sprite);
}
const renderData = sprite._renderData;
const data = renderData._data;
const buffer = renderer._meshBuffer;
let vertexOffset = buffer.byteOffset >> 2;
const vertexCount = renderData.vertexCount;
let indiceOffset = buffer.indiceOffset;
const vertexId = buffer.vertexOffset;
// Recalculate uv sliced if needed.
const frame = sprite.spriteFrame as any;
const insetL = frame.insetLeft;
const insetR = frame.insetRight;
const insetT = frame.insetTop;
const insetB = frame.insetBottom;
const sizableWidth = sprite.node.width - insetL - insetR;
const sizableHeight = sprite.node.height - insetT - insetB;
const oldInsets = [...frame._capInsets];
let needRecalculation = false;
if (sizableWidth < 0) {
const insetWidth = insetL + insetR;
frame._capInsets[0] += sizableWidth * insetL / insetWidth;
frame._capInsets[2] += sizableWidth * insetR / insetWidth;
needRecalculation = true;
}
if (sizableHeight < 0) {
const insetHeight = insetT + insetB;
frame._capInsets[1] += sizableHeight * insetT / insetHeight;
frame._capInsets[3] += sizableHeight * insetB / insetHeight;
needRecalculation = true;
}
const oldUvSliced = [];
if (needRecalculation) {
oldUvSliced.push(...frame.uvSliced);
frame._calculateSlicedUV();
}
const uvSliced = frame.uvSliced;
buffer.request(vertexCount, renderData.indiceCount);
// buffer data may be realloc, need get reference after request.
const vbuf = buffer._vData;
const ibuf = buffer._iData;
for (let i = 4; i < 20; ++i) {
const vert = data[i];
const uvs = uvSliced[i - 4];
vbuf[vertexOffset++] = vert.x;
vbuf[vertexOffset++] = vert.y;
vbuf[vertexOffset++] = uvs.u;
vbuf[vertexOffset++] = uvs.v;
}
for (let r = 0; r < 3; ++r) {
for (let c = 0; c < 3; ++c) {
const start = vertexId + r * 4 + c;
ibuf[indiceOffset++] = start;
ibuf[indiceOffset++] = start + 1;
ibuf[indiceOffset++] = start + 4;
ibuf[indiceOffset++] = start + 1;
ibuf[indiceOffset++] = start + 5;
ibuf[indiceOffset++] = start + 4;
}
}
// Restore.
if (needRecalculation) {
frame._capInsets = oldInsets;
frame.uvSliced = oldUvSliced;
}
},
updateWorldVerts(sprite: cc.Sprite): void {
const node = sprite.node;
const data = sprite._renderData._data;
const matrix = (node as any)._worldMatrix;
const a = matrix.m00;
const b = matrix.m01;
const c = matrix.m04;
const d = matrix.m05;
const tx = matrix.m12;
const ty = matrix.m13;
for (let row = 0; row < 4; ++row) {
const rowD = data[row];
for (let col = 0; col < 4; ++col) {
const colD = data[col];
const world = data[4 + row * 4 + col];
world.x = colD.x * a + rowD.y * c + tx;
world.y = colD.x * b + rowD.y * d + ty;
}
}
},
};
export class Scale9 extends cc.Component {
public update(delta: number): void {
const component = this.getComponent(cc.Sprite);
const type = component.type;
if (type === cc.Sprite.Type.SLICED) {
if (component._assembler !== assembler) {
// Use custom assembler.
this.updateAssembler();
}
}
}
private updateAssembler(): void {
const component = this.getComponent(cc.Sprite);
assert(component !== null);
if (component._assembler !== assembler) {
component._assembler = assembler;
component._renderData = null;
}
if (!component._renderData) {
component._renderData = component._assembler.createData(component);
component._renderData.material = component._material;
component.markForUpdateRenderData(true);
}
}
}