Pages

Thursday, 5 February 2015

Custom mesh rendering with texture in cocos2d-x


Introduction

Custom drawing can be done in cocos2dx by deriving from Node class and using the shaders available in cocos2dx or by creating shader by our self.

Create a base class for Mesh

Lets create a class which will act as a base for mesh. You would be deriving from this class and providing required information to get the mesh drawn.

Class skeleton is show below,
class CustomShape
{
public:
    virtual ~CustomShape();

    virtual void init() = 0;

    virtual float * vertices() = 0;
    virtual float * texCoords() = 0;
    virtual float * colors() = 0;

    virtual int primitive() = 0;

    virtual int verticesCount() = 0;

    virtual int coordSize() = 0;
};



Create a Node class implementation

Create a class called TexturedMeshNode which is deriving from Node class.  

Class sekeleton is show below,

class TexturedMeshNode: public Node
{
public:
    bool init();

    void setShape(CustomShape *shape)
    {
        mShape = shape;
    }

    void draw(Renderer *renderer, const Mat4& transform, uint32_t flags);
    void onDraw(const Mat4 &transform, uint32_t flags);

    void setTexture(Texture2D *texture)
    {
        mTexture = texture;
    }

    CREATE_FUNC(TexturedMeshNode);

private:
    CustomShape *mShape;
    Texture2D * mTexture;
    CustomCommand mCustomCommand;
};

Method implementation

init

In the init method will set the shader for the class.
bool TexturedMeshNode::init()
{
    setGLProgram(ShaderCache::getInstance()->getGLProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE));
}

draw


void TexturedMeshNode::draw(Renderer *renderer, const Mat4& transform, uint32_t flags)
{
    if (mShape) {
        mCustomCommand.init(_globalZOrder);
        mCustomCommand.func = CC_CALLBACK_0(TexturedMeshNode::onDraw, this, transform, flags);
        renderer->addCommand(&mCustomCommand);
    }
}

onDraw


void TexturedMeshNode::onDraw(const Mat4 &transform, uint32_t flags)
{
    auto glProgram = getGLProgram();
    glProgram->use();
    glProgram->setUniformsForBuiltins(transform);
    if (mShape) {
        GL::bindTexture2D(mTexture->getName());
        GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_TEX_COORD);
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, mShape->coordSize(), GL_FLOAT, GL_FALSE, 0, mShape->vertices());
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, 0, mShape->texCoords());
        glDrawArrays(mShape->primitive(), 0, mShape->verticesCount());
    }
    CC_INCREMENT_GL_DRAWS(1);
}

A simple rectangle mesh implementation

Class declaration

class Rectangle : public CustomShape
{
private:
    float mX;
    float mY;
    float mWidth;
    float mHeight;
    std::vector mVertices;
    std::vector mTexCoords;
public:
    Rectangle(float x, float y, float w, float h)
     : mX(x),
       mY(y),
       mWidth(w),
       mHeight(h)
    {}
    float * vertices()
    {
        return mVertices.data();
    }
    float * texCoords()
    {
        if (mTexCoords.empty()) {
            return 0;
        }
        return mTexCoords.data();
    }
    float * colors()
    {
        return 0;
    }
    int verticesCount()
    {
        return mVertices.size() / coordSize();
    }
    int primitive();
    void init();
    int coordSize()
    {
        return 3;
    }
};

Class implementation

void Rectangle::init()
{
    mVertices.clear();
    mTexCoords.clear();

    float v[] = {
        mX, mY, 0.0f,
        mX+mWidth, mY, 0.0f,
        mX+mWidth, mY+mHeight, 0.0f,
        mX, mY+mHeight, 0.0f
    };
    float t[] = {
        0, 1,
        1, 1,
        1, 0,
        0, 0
    };

    mVertices.insert(mVertices.begin(), v, v + sizeof(v)/sizeof(v[0]));
    mTexCoords.insert(mTexCoords.begin(), t, t + sizeof(t)/sizeof(t[0]));
}

int Rectangle::primitive()
{
    return GL_TRIANGLE_FAN;
}

Using the textured mesh class

The TexturedMeshNode can be used from other classes as shwo below,
CustomShape *shape = new Rectangle(10, 10, 100, 200);

TexturedMeshNode *texturedMeshNode = TexturedMeshNode::create();
texturedMeshNode->setShape(shape);
texturedMeshNode->setTexture(texture);

this->addChild(texturedMeshNode);

No comments:

Post a Comment