Pages

Gin Rummy Indian Rummy Nine Men's Morris and more Air Attack

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);

1 comment:

  1. Hi There,

    Brilliant article, glad I slogged through the Custom mesh rendering with texture in cocos2d-x it seems that a whole lot of the details really come back to from my past project.

    I made a basic calculator using this codes. What i want to learn is how to make a loop after the program ask "try again y/n"? it will return to choose an operation.
    Code:
    #include
    #include
    int main(){
    int num1, num2, choice;

    printf("Choose an operation\n\n");
    printf("[1] Add\n[2] Subtract\n[3] Multiply\n[4] Divide\n[5] Exit\n");
    scanf("%d", &choice);

    switch(choice){

    case 1:
    printf("Enter 1st number:\n");
    scanf("%d", &num1);
    printf("Enter 2nd number:\n");
    scanf("%d", &num2);
    printf("\n%d", (num1+num2));
    break;
    case 2:
    printf("Enter 1st number:\n");
    scanf("%d", &num1);
    printf("Enter 2nd number:\n");
    scanf("%d", &num2);
    printf("\n%d ", (num1-num2));
    break;
    case 3:
    printf("Enter 1st number:\n");
    scanf("%d", &num1);
    printf("Enter 2nd number:\n");
    scanf("%d", &num2);
    printf("\n%d", (num1*num2));
    break;
    case 4:
    printf("Enter 1st number:\n");
    scanf("\n%d", &num1);
    printf("Enter 2nd number:\n");
    scanf("%d", &num2);
    printf("\n%d", (num1/num2));
    break;
    case 5:
    return 0;

    default:
    printf("That is not a valid choice.");
    break;
    }
    getch();
    }
    Awesome! Thanks for putting this all in one place. Very useful!

    Thanks a heaps,
    Preethi

    ReplyDelete