With help from shadowphiar
http://discuss.cocos2d-x.org/t/sprite3d-how-to-display-program-generated-geometry/25568/5?u=serebii01
I was able to achieve draw call batching of static 3d cube models like this:
void MapController::mergeCubes(){
auto sprite3D = Sprite3D::create();
std::vector< MeshVertexAttrib > attributes = {
{ 3, GL_FLOAT, GLProgram::VERTEX_ATTRIB_POSITION, 3 * sizeof(float) },
{ 3, GL_FLOAT, GLProgram::VERTEX_ATTRIB_NORMAL, 3 * sizeof(float) },
// { 3, GL_FLOAT, GLProgram::VERTEX_ATTRIB_TANGENT, 3*sizeof(float) },
// { 3, GL_FLOAT, GLProgram::VERTEX_ATTRIB_BINORMAL, 3*sizeof(float) },
{ 2, GL_FLOAT, GLProgram::VERTEX_ATTRIB_TEX_COORD, 2 * sizeof(float) },
};
int perVertexSizeInFloat = 8; // 3+3+2
std::vector< float > vertices;
MeshData::IndexArray indices;
auto size = 1.f;
addCube(vertices, indices, 20.f, 6.f, 0.f, size);
addCube(vertices, indices, 2.f, 10.f, 0.f, size);
addCube(vertices, indices, 8.f, 2.f, 0.f, size);
sprite3D->addMesh(Mesh::create(vertices, perVertexSizeInFloat, indices, attributes));
sprite3D->setMaterial(Sprite3DMaterial::createBuiltInMaterial(Sprite3DMaterial::MaterialType::UNLIT, false));
sprite3D->setPosition3D(cocos2d::Vec3(1.f,1.f, 1.f));
sprite3D->setTexture("textures/paper_house.png");
gameScene->addChild(sprite3D);
}
void MapController::addCube(std::vector<float> &vertices, cocos2d::MeshData::IndexArray& indices, float x, float y, float z, float s){
int perVertexSizeInFloat = 8; // 3+3+2
size_t startindex = vertices.size() / perVertexSizeInFloat;
vertices.insert ( vertices.end(), {
// position normal tangent binormal uvTextCoords
// +x
x+s,y-s,z-s, 1,0,0, /*0,0,-1, 0,-1,0,*/ 0.2765, 0.3439,
x+s,y+s,z-s, 1,0,0, /*0,0,-1, 0,-1,0,*/ 0.5004, 0.3439,
x+s,y-s,z+s, 1,0,0, /*0,0,-1, 0,-1,0,*/ 0.2765, 0.5679,
x+s,y+s,z+s, 1,0,0, /*0,0,-1, 0,-1,0,*/ 0.5004, 0.5679,
// +y
x-s,y+s,z-s, 0,1,0, /*1,0,0, 0,0,1,*/ 0.2765, 0.3444,
x+s,y+s,z-s, 0,1,0, /*1,0,0, 0,0,1,*/ 0.0525, 0.3444,
x-s,y+s,z+s, 0,1,0, /*1,0,0, 0,0,1,*/ 0.2765, 0.5684,
x+s,y+s,z+s, 0,1,0, /*1,0,0, 0,0,1,*/ 0.0525, 0.5684,
// +z
x-s,y-s,z+s, 0,0,1, /*/*0,0,-1, 1,0,0,*/ 0.2780f, 0.7943f,
x+s,y-s,z+s, 0,0,1, /*0,0,-1, 1,0,0,*/ 0.2780f, 0.5703f,
x-s,y+s,z+s, 0,0,1, /*0,0,-1, 1,0,0,*/ 0.5019f, 0.7943f,
x+s,y+s,z+s, 0,0,1, /*0,0,-1, 1,0,0,*/ 0.5019f, 0.5703f,
// -x
x-s,y-s,z-s, -1,0,0, /*0,0,1, 0,-1,0,*/ 0.9482, 0.3459,
x-s,y+s,z-s, -1,0,0, /*0,0,1, 0,-1,0,*/ 0.9482, 0.5699,
x-s,y-s,z+s, -1,0,0, /*0,0,1, 0,-1,0,*/ 0.7243, 0.5699,
x-s,y+s,z+s, -1,0,0, /*0,0,1, 0,-1,0,*/ 0.7243, 0.3459,
// -y
x-s,y-s,z-s, 0,-1,0, /*-1,0,0, 0,0,1,*/ 0.5014, 0.5694,
x+s,y-s,z-s, 0,-1,0, /*-1,0,0, 0,0,1,*/ 0.5014, 0.3454,
x-s,y-s,z+s, 0,-1,0, /*-1,0,0, 0,0,1,*/ 0.7253, 0.3454,
x+s,y-s,z+s, 0,-1,0, /*-1,0,0, 0,0,1,*/ 0.7253, 0.5694,
// -z
x-s,y-s,z-s, 0,0,-1, /*0,0,1, 1,0,0,*/ 0.4974, 0.3454,
x+s,y-s,z-s, 0,0,-1, /*0,0,1, 1,0,0,*/ 0.4974, 0.1215,
x-s,y+s,z-s, 0,0,-1, /*0,0,1, 1,0,0,*/ 0.7214, 0.3454,
x+s,y+s,z-s, 0,0,-1, /*0,0,1, 1,0,0,*/ 0.7214, 0.1215,
} );
MeshData::IndexArray meshindices = { 0, 3, 2, 1, 3, 0,
4, 6, 7, 4, 7, 5,
8, 11, 10, 9, 11, 8,
12, 14, 15, 13, 12, 15,
16, 19, 18, 17, 19, 16,
20, 22, 23, 21, 20, 23
};
for (auto i : meshindices)
{
indices.push_back((unsigned short)(startindex+i));
}
}
This code merges 3 cubes that are same sizes, same texture, different position 3d.
Those 3 cubes get drawn just with 1 draw call instead of 3 draw calls.
Now if I want that those 3 cubes would have different textures I have following options:
- Merge several textures into one picture (textureAtlas). And redefine texture coordinates in addCube() method. But this comes with limitation that you should create texture atlases not bigger than 1024x1024 because of the performance issue on older devices (like iPhone 3). So it may be a problem to merge several textures into one picture if the texture size is reasonably big.
- If I would have for example 100 cubes and 10 different textures for them, I could merge cubes for each texture, so it would be 10 Sprite3D nodes each merged from 10 meshes each with material of different texture. So instead of 100 Gl draw calls I would get only 10 calls to render all 100 cubes.