Search code examples
cocos2d-xclippingcocos2d-x-3.0

Is it possible to clip a Layer with a clipping box using cocos2d-x 3.2?


I need to clip the drawing area of a custom layer so that it will only draw inside a box instead of drawing into the whole window.

I've come up with this solution, but it is not working:

void GameLayer::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags) {
  auto director = Director::getInstance();
  director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

  glEnable(GL_SCISSOR_TEST);

  static Rect clippingRegion = {0,0,200,200};
  director->getOpenGLView()->setScissorInPoints(
      clippingRegion.origin.x + origin.x, clippingRegion.origin.y + origin.y,
      clippingRegion.size.width, clippingRegion.size.height);

  Layer::visit(renderer, parentTransform, parentFlags);

  glDisable(GL_SCISSOR_TEST);
  director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}

The layer will draw as if this code wasn't there. But if I remove the glDisable(GL_SCISSOR_TEST) the whole window will be clipped to the {0,0,200,200} rect.

Is it possible to clip a Layer using this approach?


Solution

  • I've found a solution to this problem looking at how the Layout class in cocos2d-x implements the clipping rect. All you have to do is add these two variables to the class:

    CustomCommand _beforeVisitCmdScissor;
    CustomCommand _afterVisitCmdScissor;
    

    and then you can modify the visit(...) method like this:

    void GameLayer::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags) {
    
      _beforeVisitCmdScissor.init(_globalZOrder);
      _beforeVisitCmdScissor.func = CC_CALLBACK_0(GameLayer::onBeforeVisitScissor, this);
      renderer->addCommand(&_beforeVisitCmdScissor);
    
      Layer::visit(renderer, parentTransform, parentFlags);
    
      _afterVisitCmdScissor.init(_globalZOrder);
      _afterVisitCmdScissor.func = CC_CALLBACK_0(GameLayer::onAfterVisitScissor, this);
      renderer->addCommand(&_afterVisitCmdScissor);
    }
    
    void GameLayer::onBeforeVisitScissor()
    {
      static float size = Tile::size;
      static Rect clippingRegion = {0,0,200,200};
      glEnable(GL_SCISSOR_TEST);
    
      Director::getInstance()->getOpenGLView()->setScissorInPoints(
        clippingRegion.origin.x + origin.x, clippingRegion.origin.y + origin.y,
        clippingRegion.size.width, clippingRegion.size.height);
    }
    
    void GameLayer::onAfterVisitScissor()
    {
      glDisable(GL_SCISSOR_TEST);
    }