Sprite3D: How to display program-generated geometry?

Hello,
I’ve been wondering if it’s possible to generate your own geometry for a Sprite3D instead of loading it from a model file?

I’ve constructed an example which runs without generating runtime errors, but never actually appears to draw anything. (I see an increase in the number of draw calls and vertices in the debug stats, but I don’t see my cube on the screen). Have I missed something obvious?

Size size = Director::getInstance()->getWinSize();
camera = Camera::createPerspective(30.0f, size.width / size.height, 1.0f, 1000.0f);
camera->setPosition3D(Vec3(0.0f, 80.0f, 80.0f));
camera->lookAt(Vec3(0.0f, 0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f));
camera->setCameraFlag(CameraFlag::USER1);
this->addChild(camera);

camera->setBackgroundBrush(CameraBackgroundBrush::createSkyboxBrush("skybox/posx.jpg", "skybox/negx.jpg","skybox/posy.jpg", "skybox/negy.jpg","skybox/posz.jpg", "skybox/negz.jpg"));

auto sprite3d = Sprite3D::create();
sprite3d->setPosition3D(Vec3(0.0,0.0,0));
sprite3d->setCameraMask((unsigned short)CameraFlag::USER1);

std::vector< MeshVertexAttrib > attribs = {
    { 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 =  14;  // 3+3+3+3+2

float x=0;
float y=0;
float z=0;
float s=20;
std::vector< float > vertices = {
    // position  normal  tangent binormal uv
    // +x
    x+s,y-s,z-s, 1,0,0,  0,0,-1, 0,-1,0, 0,0,
    x+s,y+s,z-s, 1,0,0,  0,0,-1, 0,-1,0, 1,0,
    x+s,y-s,z+s, 1,0,0,  0,0,-1, 0,-1,0, 0,1,
    x+s,y+s,z+s, 1,0,0,  0,0,-1, 0,-1,0, 1,1,
    // +y
    x-s,y+s,z-s, 0,1,0,  1,0,0,  0,0,1,  0,0,
    x+s,y+s,z-s, 0,1,0,  1,0,0,  0,0,1,  1,0,
    x-s,y+s,z+s, 0,1,0,  1,0,0,  0,0,1,  0,1,
    x+s,y+s,z+s, 0,1,0,  1,0,0,  0,0,1,  1,1,
    // +z
    x-s,y-s,z+s, 0,0,1,  0,0,-1, 1,0,0,  0,0,
    x+s,y-s,z+s, 0,0,1,  0,0,-1, 1,0,0,  1,0,
    x-s,y+s,z+s, 0,0,1,  0,0,-1, 1,0,0,  0,1,
    x+s,y+s,z+s, 0,0,1,  0,0,-1, 1,0,0,  1,1,
    // -x
    x-s,y-s,z-s, -1,0,0, 0,0,1,  0,-1,0, 0,0,
    x-s,y+s,z-s, -1,0,0, 0,0,1,  0,-1,0, 1,0,
    x-s,y-s,z+s, -1,0,0, 0,0,1,  0,-1,0, 0,1,
    x-s,y+s,z+s, -1,0,0, 0,0,1,  0,-1,0, 1,1,
    // -y
    x-s,y-s,z-s, 0,-1,0, -1,0,0, 0,0,1,  0,0,
    x+s,y-s,z-s, 0,-1,0, -1,0,0, 0,0,1,  1,0,
    x-s,y-s,z+s, 0,-1,0, -1,0,0, 0,0,1,  0,1,
    x+s,y-s,z+s, 0,-1,0, -1,0,0, 0,0,1,  1,1,
    // -z
    x-s,y-s,z-s, 0,0,-1,  0,0,1, 1,0,0,  0,0,
    x+s,y-s,z-s, 0,0,-1,  0,0,1, 1,0,0,  1,0,
    x-s,y+s,z-s, 0,0,-1,  0,0,1, 1,0,0,  0,1,
    x+s,y+s,z-s, 0,0,-1,  0,0,1, 1,0,0,  1,1,
};
MeshData::IndexArray indices = { 0, 0, 1, 2, 3, 3,  //+x
                                 4, 4, 5, 6, 7, 7,  //+y
                                 8, 8, 9, 10,11,11, //+z
                                 12,12,13,14,15,15, //-x
                                 16,16,17,18,19,19, //-y
                                 20,20,21,22,23,23  //-z
};

auto mesh = Mesh::create(vertices, perVertexSizeInFloat, indices, attribs);
sprite3d->addMesh(mesh);

sprite3d->setMaterial(Sprite3DMaterial::createBuiltInMaterial(Sprite3DMaterial::MaterialType::UNLIT, false));
sprite3d->setTexture("HelloWorld.png");

this->addChild(sprite3d);

(Background: Each level of my game will contain a bunch of similar objects which each contain a single mesh, and can share one material and texture, and never move relative to one another. I believe it should be possible to draw them all with a single call and substantially improve mobile performance. Is this a valid approach, or will cocos at some point be able to batch my Sprite3D objects together automatically, and save me the trouble of implementing it myself?)

I tried similar as well. I finally got a colored box to successfully render, but I ended up modifying the Sprite3D class itself where I setup node/material/mesh-datas and fed them to the internal constructor.

I used the box.c3t file to test by converting it to code and noticed that one of the transforms was inverted (or one of the mesh triangles, or maybe it was a CW/CCW issue. Regardless it is possible, but the API is not setup to allow this easily at this time.

I spent a bit more time investigating this. It turns out that my construction was fine, my generated indexes were faulty because I’d made a faulty assumption about how the IndexArray would be interpreted.

If I rewrite the indices like this, it works much better:

MeshData::IndexArray indices = { 0, 3, 2, 1, 3, 0,
				 4, 7, 6, 5, 7, 4,
				 8, 11, 10, 9, 11, 8,
				 12,  15,  14,  13,  15,  12,
				 16,  19,  18,  17,  19,  16,
				 20,  23,  22,  21,  23,  20
};

I’m considering tidying up the code a bit and formatting it like the code examples in cpp-tests. Anybody have a view on whether that would be likely to get accepted upstream?

can you share your source code?

Well it’s all up there, really. But I did tidy it up a bit and submitted a pull request at the time, although it wasn’t accepted (I don’t think the admins saw the point of including it). Feel free to have a look.

1 Like

very nice example, thank you.

From this I was able to achieve static Sprite3D model batching.
http://discuss.cocos2d-x.org/t/dynamic-and-static-sprite3d-draw-call-batching/36548/3?u=serebii01

Do you know what additional parameters I need to set in order to add the same texture to all of tetrahedrons and cubes?

Would this be the correct GL attributes?

std::vector< MeshVertexAttrib > attributes = {
        { 3, GL_FLOAT, GLProgram::VERTEX_ATTRIB_POSITION, 3 * sizeof(float) },
        { 3, GL_FLOAT, GLProgram::VERTEX_ATTRIB_NORMAL, 3 * sizeof(float) },
        { 2, GL_FLOAT, GLProgram::VERTEX_ATTRIB_TEX_COORD, 8 },
    };

Is it possible to copy UV texture map from one Sprite3d object to these tetrahedrons and cubes?

Yes, I think those are suitable attributes.
Sorry, I’ve never tried copying texture data out of a Sprite3D.