Simple way to use ClippingNode?

Hello,

I am migrating from V2.2 to V3.2 and I need to use cocos2d-x ClippingNode.

In V2.2, I was using a custom ClippingNode, which was defined as such:
ClippingNode.h

//use to clip children to the zone defined by contentsize
class ClippingNode : public CCNode, public CCActionTweenDelegate
{
public:
    virtual void visit();
    static ClippingNode* create();
    virtual void updateTweenAction(float value, const char* key);
};

ClippingNode.cpp

void ClippingNode::visit()
{
    if (!this->isVisible()) {
        return;
    }
    glEnable(GL_SCISSOR_TEST);
    
    CCPoint bottomLeft = this->convertToWorldSpaceAR(ccp(this->getContentSize().width * this->getAnchorPoint().x * -1, this->getContentSize().height  * this->getAnchorPoint().y * -1));
    CCPoint topRight = this->convertToWorldSpaceAR(ccp(this->getContentSize().width * (1 - this->getAnchorPoint().x), this->getContentSize().height * (1 - this->getAnchorPoint().y)));
    topRight = ccpSub(topRight, bottomLeft);
    glScissor(bottomLeft.x,
              bottomLeft.y,
              topRight.x,
              topRight.y);
    CCNode::visit();
    glDisable(GL_SCISSOR_TEST);
}


ClippingNode* ClippingNode::create()
{
    ClippingNode* newNode = new ClippingNode();
    newNode->autorelease();
    return newNode;
}

void ClippingNode::updateTweenAction(float value, const char* key)
{
    if(strcmp(key, "width") == 0)
    {
        setContentSize(CCSize(value, getContentSize().height));
    }
    else if(strcmp(key, "height") == 0)
    {
        setContentSize(CCSize(getContentSize().width, value));
    }
}

My two basic needs:

  • have the ClippingNode clip its children to what’s inside its ContentSize
  • be able to change that ContentSize with an ActionTween

In V3.2, there is a ClippingNode in cocos2d-x. The problem is it requires a stencil. Using a sprite is not practical, since I either need a specific texture the right size, or I have to re-calculate the scale, which seems ugly to me (and add a dependency on a texture I do not want).

I looked at cpp-test, and it uses a DrawNode somewhere. The problem is: I can’t get it to work.

Working code, where myNode is a CCSprite loaded from CocosBuilder, which have the same position/anchorpoint/scale/parent as my ClippingNode, and opacity 0 (since I don’t want to actually show it):

myNode->setVisible(true);
clipNode->setStencil(myNode);

Code not working, nothing of what’s inside the ClippingNode is shown:

auto stencil = DrawNode::create();
Vec2 rectangle[4];
rectangle[0] = Vec2(0, 0);
rectangle[1] = Vec2(clipNode->getContentSize().width, 0);
rectangle[2] = Vec2(clipNode->getContentSize().width, clipNode->getContentSize().height);
rectangle[3] = Vec2(0, clipNode->getContentSize().height);
        
Color4F white(1, 1, 1, 1);
stencil->drawPolygon(rectangle, 4, white, 1, white);
stencil->setPosition(clipNode->getPosition());
stencil->setAnchorPoint(clipNode->getAnchorPoint());
stencil->setContentSize(clipNode->getContentSize());
clipNode->getParent()->addChild(stencil);
clipNode->setStencil(stencil);

What’s wrong with that code?
As for the ActionTween, I guess I’ll move it to the stencil, which will be a subclass of DrawNode.

Have you added the clipNode to the layer? You added the stencil but not the clipNode in your code. Instead of adding the stencil to the layer you can add it to the clipNode as child to see if the stencil is drawn.

Yes, the clipNode is already added in the hierarchy. As I said, it already works with a Sprite used as a stencil.

I tried adding the DrawNode stencil to the clipNode as well (instead of its parent), but I can’t see it either.

Do I use DrawNode correctly?

Yes, at least I cannot see an error.

How do you create clipNode? Does it have the correct dimensions?
Is it just ClippingNode::create();? Have you set a contentSize, cause you are using that for creating your stencil?

Normally you just create the rectangle by defining points:

Point quad[4];
quad[0] = Point(0, -30);
quad[1] = Point(0, 30);
quad[2] = Point(-40, 30);
quad[3] = Point(-40, -30);

And you don’t have to do those:

stencil->setPosition(clipNode->getPosition());
stencil->setAnchorPoint(clipNode->getAnchorPoint());
stencil->setContentSize(clipNode->getContentSize());

Position and anchor points are set on the clipNode.
Content size does not have to be set on the stencil, as you draw a polygon into the stencil.

Try out this example here(strip the CC prefixes on the class names):
http://www.onemoresoftwareblog.com/2013/12/cocos2d-x-ccclippingnode-triple-c.html

clipNode is created using

ClippingNode* clipNode = ClippingNode::create();

Then, I copy other parameters using another Node which is loaded from CocosBuilder. I did set a contentSize

I tried out the example: I have correctly set GL_DEPTH24_STENCIL8_OES and did the same code, but it still doesn’t work.

I also tried to show the DrawNode as-is, and I cannot see it on-screen. The same code I used for the DrawNode works for an empty project, so there must be something else that prevent the DrawNode from being shown.

I’ll continue to search, clearly the problem comes from somewhere else in my code.

Thanks for the help @iQD

Quick update in case someone land here: the problem came from a modification of cocos2d-x I made.

I changed Vec2 and made it a subclass of Ref to be able to pass it around. Somehow, this cause a big problem with DrawNode: DrawNode won’t show at all with this modification.

I have a clear cocos2d-x 3.2 (haven’t modified anything myself) and I have the same problem with DrawNode. It works ok if I use it directly on screen, but doesn’t work as a stencil for ClippingNode (on iOS - it works on Mac). When I used a Sprite as a stencil it worked as supposed.

Do you ( or someone ) have any other suggestions as to what might be a problem?

Did you check that step ?

  1. Remember to enable OpenGL stencil buffer, by changing depthFormat to GL_DEPTH24_STENCIL8_OES!!! On iOS modify the AppController.mm

Yep - I triple-checked it, and for completeness sake I even checked other options avaiable there with zero luck.

This is my current setup :

    // Init the CCEAGLView
CCEAGLView *eaglView = [CCEAGLView viewWithFrame: [window bounds]
                                 pixelFormat: kEAGLColorFormatRGBA8
                                 depthFormat: GL_DEPTH24_STENCIL8_OES
                          preserveBackbuffer: NO
                                  sharegroup: nil
                               multiSampling: NO
                             numberOfSamples: 0];

Sorry to bump this but I guess you guys know stuff…

I use a clipping node, but it crashes as soon as I set a new stencil on the clippingnode. I just use a normal sprite from a spritesheet…

Specifically I get this assert:

Assert failed: Node still marked as running on node destruction! Was base class onExit() called in derived class onExit() implementations?

I get it when running the setStencil line.

this->m_cropnode->setStencil(stencilnew);

Maybe I should just assume that setting a new stencil is just not supported…