Assembler2D
fillBuffers 方法解读
/**
* Assembler 意为装配器
*
* fillBuffer()是简单的字节流拷贝,只关心数据长度,不关心数据内容
*
* fillBuffer()每帧都会被调用,是热点代码,需要关注效率。但是_renderData顶点数据不是每一帧都会更新
*/
fillBuffers (comp, renderer) {
// 如果节点的世界坐标发生变化,重新从当前节点的世界坐标计算一次顶点数据
if (renderer.worldMatDirty) {
this.updateWorldVerts(comp);
}
// 获取准备好的顶点数据
let renderData = this._renderData;
// vData包含pos、uv、color数据
// vData的类型是 Float32Array
// 一张simple图片,vData数值如下(其实Label也是一样的,会作为一个图片来渲染,所以顶点信息也一样),长度为20(4个顶点数据,每个顶点数据包含posX,posY,uvX,uvY,color 共5个值,数组长度为 4*5 = 20)
// 数组内容: [10.180180549621582, 270, 0.0009765625, 0.001953125, NaN, 110.18018341064453, 270, 0.001953125, 0.001953125, NaN, 10.180180549621582, 370, 0.0009765625, 0.0009765625, NaN, 110.18018341064453, 370, 0.001953125, 0.0009765625, NaN]
// 每个值的含义:posX posY uvX uvY color
// 4个顶点数据的顺序是 : 左下 -> 右下 ->左上 ->右上 四个点
// 对于posX,posY 的值,是确确实实的该点在 cocos全局坐标系下的值。也就是 左下角为(0,0)
// 我们可以posX 和posY 的值进行修改,那么就会得到了扭曲的变形的图片。那么我们其实可以通过定义顶点填充,来展示不同的效果。
// 如果我们设置为slice type九宫格, 那么顶点数为 4*4 = 16个顶点。vData 的长度就是 16 * 5 = 80
let vData = renderData.vDatas[0];
// iData包含三角剖分后的顶点索引数据
// iData类型为 Uint16Array
// 测试一张图片的数值如下,一个图片,也就是一个四边形(有四个顶点)。需要拆分为两个三角形。那么需要六个索引号。
// [0, 1, 2, 1, 3, 2]
// 我们可以做如下测试,比如将[0, 1, 2, 1, 3, 2] 顶点索引改掉,那么会显示不同的裁剪结果显示出来。
// 如果我们设置为slice type九宫格, 一个四边形为6的索引,那么9个四边形 顶点索引数为 9 * 6 = 54
let iData = renderData.iDatas[0];
// 获取顶点缓存
let buffer = this.getBuffer(renderer);
// 获取当前节点的顶点数据对应最终buffer的偏移量
// 可以简单理解为当前节点和其他同格式节点的数据,都将按顺序追加到这个大buffer里
// 一张simple图片,verticesCount 就为 4 ,代表顶点数量。 indicesCount 就为6,代表顶点索引数量。
// 如果我们设置为slice type九宫格的一张图片, verticesCount 就为 16 ,代表顶点数量。 indicesCount 54;
// 目前还没搞懂tiled的顶点数量怎么计算的,就是特别多。
let offsetInfo = buffer.request(this.verticesCount, this.indicesCount);
// 填充顶点信息 fill vertices
// vbuf 是最终产出的 顶点数据集合(多个图片)
// vbuf 是一个5120长度的 Float32Array,默认里面全部值都是0。若是普通的图片,那么每20个长度,就刚好保存一个完整的四边形pos,uv,color数据
// 比如我们有两个图片,那么vbuf index 0~19 表示第一个图的顶点数据。20~39表示第二个图的顶点数据
// 这里是每个图片,获取到的自己在数组中的偏移量,然后偏移量开始将新的顶点数据填进去(刷新或填入数据)
let vertexOffset = offsetInfo.byteOffset >> 2,
vbuf = buffer._vData;
// 将准备好的vData拷贝到vbuf:VetexBuffer里。
if (vData.length + vertexOffset > vbuf.length) {
// 这里会判断如果buffer装不下了,vData会被截断一部分。
// 通常不会出现装不下这种情况,因为buffer.request中会分配足够大的空间;如果出现,则当前组件只能被渲染一部分
vbuf.set(vData.subarray(0, vbuf.length - vertexOffset), vertexOffset);
} else {
// 直接拷覆盖进去,vertexOffset为偏移值(可以认为是数组的index,填充长度当然就是 vData的长度)。
vbuf.set(vData, vertexOffset);
}
// 将准备好的iData拷贝到IndiceBuffer里
// ibuf 是最终产出的 顶点数据索引集合
// ibuf 是一个1536长度的 Uint16Array,默认里面全部值都是0。若是普通的图片,那么每6个长度,就刚好保存一个完整的四边形顶点索引数据
// 比如我们有两个图片,那么ibuf index 0~5 表示第一个图的顶点索引数据。6~11表示第二个图的顶点索引数据
// 注意存储的值有一点不一样。比如每张图的顶点索引肯定是 0 1 2 3 ,但是最终填入ibuf的时候,是做了处理的:
// 第一个图的索引为 0~ 3 ,第二个图的索引为 4 ~ 7 。按此规律做偏移的。
let ibuf = buffer._iData,
indiceOffset = offsetInfo.indiceOffset,
vertexId = offsetInfo.vertexOffset;
for (let i = 0, l = iData.length; i < l; i++) {
ibuf[indiceOffset++] = vertexId + iData[i];
}
}
顶点数据格式
var vfmtPosUvColor = new gfx.VertexFormat([
// 节点的世界坐标,占2个float32
{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
// 节点的纹理uv坐标,占2个float32
// 如果节点使用了独立的纹理(未合图),这里的uv值通常是0或1
// 合图后的纹理,这里的uv对应其在图集里的相对位置,取值范围在[0,1)内
{ name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
// 节点颜色值,cc.Sprite组件上可以设置。占4个uint8 = 1个float32
{ name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
]);
参考
GT大佬 https://forum.cocos.org/t/demo/95087