// // REDPolygonClippingNode.cpp // cocos2d_libs // // Created by RedInfinity on 2024/1/17. // #include "REDPolygonClippingNode.hpp" ///项目代码 #include "base/ccMacros.h" #include "base/CCDirector.h" #include "base/CCStencilStateManager.h" #include "renderer/CCGLProgram.h" #include "renderer/CCGLProgramState.h" #include "renderer/ccGLStateCache.h" #include "platform/CCGL.h" #include "platform/CCFileUtils.h" #include "common/json11.hpp" NS_CC_BEGIN ///顶点着色器 const char* kPolygonClippingNodeVsh = R"( attribute vec4 a_position; void main() { gl_Position = CC_MVPMatrix * a_position; } )"; ///片段着色器 const char* kPolygonClippingNodeFsh = R"( void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } )"; PolygonClippingNode* PolygonClippingNode::create() { PolygonClippingNode* ret = new PolygonClippingNode(); if (ret->_initWithData({}, false, false)) { ret->autorelease(); } else { delete ret; ret = nullptr; } return ret; } PolygonClippingNode* PolygonClippingNode::createWithData(const std::vector& verts, const bool& close, const bool& clockwise) { PolygonClippingNode* ret = new PolygonClippingNode(); if (ret->_initWithData(verts, close, clockwise)) { ret->autorelease(); } else { delete ret; ret = nullptr; } return ret; } PolygonClippingNode* PolygonClippingNode::createWithFile(const std::string& fileName) { PolygonClippingNode* ret = new PolygonClippingNode(); if (ret->_initWithFile(fileName)) { ret->autorelease(); } else { delete ret; ret = nullptr; } return ret; } void PolygonClippingNode::setPolygonVerts(const std::vector& verts, const bool& close, const bool& clockwise) { ///通过数值赋值则将顶点数据文件设空 _polygonFile = ""; std::vector tVerts = verts; ///如果为多边形顶点数组闭合,则把最后一个元素舍弃。 if (close) { tVerts.pop_back(); } ///如果为顶点为顺时针填充,则需要置反使用OpenGL的逆时针顺序 if (clockwise) { std::reverse(tVerts.begin(), tVerts.end()); } ///更新OpenGL绘制的顶点数据 _updateOpenGLDrawVerts(tVerts); } void PolygonClippingNode::setInverted(bool inverted) { _stencilStateManager->setInverted(inverted); } bool PolygonClippingNode::isInverted() const { return _stencilStateManager->isInverted(); } void PolygonClippingNode::setPolygonFile(const std::string& fileName) { _polygonFile = fileName; ///解析数据文件内容 std::vector verts = _parseFile(_polygonFile); ///更新OpenGL绘制的顶点数据 _updateOpenGLDrawVerts(verts); } const std::string& PolygonClippingNode::getPolygonFile() const { return _polygonFile; } void PolygonClippingNode::visit(Renderer* renderer, const Mat4& parentTransform, uint32_t parentFlags) { ///节点不可见不绘制 if (_visible == false) { return; } uint32_t flags = processParentFlags(parentTransform, parentFlags); Director* director = Director::getInstance(); director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform); ///添加渲染组 _groupCommand.init(_globalZOrder); renderer->addCommand(&_groupCommand); renderer->pushGroup(_groupCommand.getRenderQueueID()); ///设置模版功能 _beforeVisitCmd.init(_globalZOrder, _modelViewTransform, flags); _beforeVisitCmd.func = CC_CALLBACK_0(StencilStateManager::onBeforeVisit, _stencilStateManager); renderer->addCommand(&_beforeVisitCmd); ///绘制自身的模版多边形 bool visibleByCamera = isVisitableByVisitingCamera(); if (visibleByCamera) { draw(renderer, _modelViewTransform, flags); } ///为渲染启用模板功能 _afterDrawCmd.init(_globalZOrder, _modelViewTransform, flags); _afterDrawCmd.func = CC_CALLBACK_0(StencilStateManager::onAfterDrawStencil, _stencilStateManager); renderer->addCommand(&_afterDrawCmd); ///遍历执行子节点的visit if (_children.empty() == false) { sortAllChildren(); for (auto iter = _children.cbegin(); iter != _children.cend(); ++iter) { (*iter)->visit(renderer, _modelViewTransform, flags); } } ///关闭模版功能 _afterVisitCmd.init(_globalZOrder, _modelViewTransform, flags); _afterVisitCmd.func = CC_CALLBACK_0(StencilStateManager::onAfterVisit, _stencilStateManager); renderer->addCommand(&_afterVisitCmd); renderer->popGroup(); director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); } void PolygonClippingNode::draw(Renderer* renderer, const Mat4 &transform, uint32_t flags) { if (_vertsCnt == 0) { return; } _customDrawCmd.init(_globalZOrder, transform, flags); _customDrawCmd.func = [this, transform](void) { ///顶点坐标发生变化 if (_vertsDirty) { _bindVAOAndVBO(); _vertsDirty = false; } GLProgramState* programState = getGLProgramState(); programState->apply(transform); GL::bindVAO(_vao); glDrawArrays(GL_TRIANGLES, 0, _vertsCnt); GL::bindVAO(0); }; renderer->addCommand(&_customDrawCmd); } PolygonClippingNode::PolygonClippingNode() { _verts = new Vec2[_vertsCnt]; _stencilStateManager = new StencilStateManager(); glGenVertexArrays(1, &_vao); glGenBuffers(1, &_vbo); } PolygonClippingNode::~PolygonClippingNode() { glDeleteBuffers(1, &_vbo); glDeleteVertexArrays(1, &_vao); delete _stencilStateManager; delete [] _verts; } bool PolygonClippingNode::_initWithData(const std::vector& verts, const bool& close, const bool& clockwise) { ///设置闭合多边形的顶点数据 setPolygonVerts(verts, close, clockwise); ///创建着色器程序 _createGLProgram(); ///绑定vao和vbo _bindVAOAndVBO(); return Node::init(); } bool PolygonClippingNode::_initWithFile(const std::string& fileName) { ///设置闭合多边形的顶点数据文件 setPolygonFile(fileName); ///创建着色器程序 _createGLProgram(); ///绑定vao和vbo _bindVAOAndVBO(); return Node::init(); } std::vector PolygonClippingNode::_parseFile(const std::string& fileName) { std::vector ret; const std::string& content = FileUtils::getInstance()->getStringFromFile(fileName); if (content.empty() == false) { std::string error = ""; const json11::Json& jDoc = json11::Json::parse(content, error); if (error.empty()) { const json11::Json::object& jData = jDoc.object_items(); const json11::Json::array& jVerts = jData.at("verts").array_items(); for (int i = 0; i < jVerts.size(); ++i) { const json11::Json::array& jVert = jVerts.at(i).array_items(); const float& x = jVert.at(0).number_value(); const float& y = jVert.at(1).number_value(); ret.emplace_back(x, y); } const bool& close = jData.at("close").bool_value(); if (close) { ret.pop_back(); } const bool& clockwise = jData.at("clockwise").bool_value(); if (clockwise) { std::reverse(ret.begin(), ret.end()); } } else { CCASSERT(false, "解析多边形顶点数据文件错误"); } } return ret; } void PolygonClippingNode::_createGLProgram() { cocos2d::GLProgram* program = cocos2d::GLProgram::createWithByteArrays(kPolygonClippingNodeVsh, kPolygonClippingNodeFsh); cocos2d::GLProgramState* programState = cocos2d::GLProgramState::getOrCreateWithGLProgram(program); setGLProgramState(programState); } void PolygonClippingNode::_bindVAOAndVBO() { GL::bindVAO(_vao); glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(Vec2) * _vertsCnt, _verts, GL_STATIC_DRAW); glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(Vec2), nullptr); glBindBuffer(GL_ARRAY_BUFFER, 0); GL::bindVAO(0); } void PolygonClippingNode::_updateOpenGLDrawVerts(std::vector verts) { if (verts.size() < 3) { delete [] _verts; _vertsCnt = 0; _verts = new Vec2[_vertsCnt]; _vertsDirty = true; return; } ///删除旧顶点数据 delete [] _verts; _vertsCnt = 0; ///将多边形顶点分割成绘制三角形 std::vector triangleVerts; while (verts.size() > 3) { ///找到一个凸位顶点 const int& convexVertexIdx = _findConvexVertex(verts); ///分割一个凸位顶点构成的多边形内嵌三角形 const int& vertsSize = static_cast(verts.size()); const int& leftVertIdx = (convexVertexIdx - 1 + vertsSize) % vertsSize; const int& rightVertIdx = (convexVertexIdx + 1) % vertsSize; triangleVerts.emplace_back(verts.at(leftVertIdx)); triangleVerts.emplace_back(verts.at(convexVertexIdx)); triangleVerts.emplace_back(verts.at(rightVertIdx)); ///凸位顶点完成三角形的构建则从多边形顶点数组中移除 verts.erase(verts.begin() + convexVertexIdx); } ///添加最后一组三角形 triangleVerts.emplace_back(verts.at(0)); triangleVerts.emplace_back(verts.at(1)); triangleVerts.emplace_back(verts.at(2)); ///转成OpenGL使用的数据 _vertsCnt = static_cast(triangleVerts.size()); _verts = new Vec2[_vertsCnt]; for (int i = 0; i < _vertsCnt; ++i) { _verts[i] = triangleVerts.at(i); } _vertsDirty = true; } int PolygonClippingNode::_findConvexVertex(const std::vector& verts) { int ret = 0; const int& vertsSize = static_cast(verts.size()); for (int i = 0; i < vertsSize; ++i) { ///取左索引使用 i - 1 + vertsSize ,能保证当 i = 0时左索引为数组最后一个元素的索引 const int& leftVertIdx = (i - 1 + vertsSize) % vertsSize; const int& rightVertIdx = (i + 1) % vertsSize; ///获取三个相邻的顶点 const Vec2& vert = verts.at(i); const Vec2& leftVert = verts.at(leftVertIdx); const Vec2& rightVert = verts.at(rightVertIdx); ///如果三个顶点构成的三角形为正面,表示三角形一定不在多边形外。 ///如果三个顶点构成的三角形为多边形的内嵌三角形,表示当前顶点为凸位顶点。 if (_isPositiveTriangle(leftVert, vert, rightVert) && _isEmbeddedTriangle(verts, leftVert, vert, rightVert)) { ret = i; break; } } return ret; } bool PolygonClippingNode::_isPositiveTriangle(const Vec2& vert1, const Vec2& vert2, const Vec2& vert3) { const float& val = (vert2.x - vert1.x) * (vert3.y - vert1.y) - (vert2.y - vert1.y) * (vert3.x - vert1.x); return val >= 0.0f; } bool PolygonClippingNode::_isEmbeddedTriangle(const std::vector& verts, const Vec2& vert1, const Vec2& vert2, const Vec2& vert3) { bool ret = true; for (const Vec2& vert : verts) { ///跳过自检 if (vert == vert1 || vert == vert2 || vert == vert3) { continue; } ///多边形中存在任意一个其他顶点位于构造出的三角形内 if (_isInteriorPoint(vert, vert1, vert2, vert3)) { ret = false; break; } } return ret; } bool PolygonClippingNode::_isInteriorPoint(const Vec2& point, const Vec2& vert1, const Vec2& vert2, const Vec2& vert3) { bool ret = false; ///以这个点为起始顶点按三角形顶点顺序依次取两个顶点绘制三角形,如果都为正面三角形则这个点在三角形内部 if (_isPositiveTriangle(point, vert1, vert2) && _isPositiveTriangle(point, vert2, vert3) && _isPositiveTriangle(point, vert3, vert1)) { ret = true; } return ret; } NS_CC_END