// // TiledPolygonSprite.cpp // building // // Created by yanqian on 2023/4/17. // #include "TiledPolygonSprite.hpp" #include "common/CocosConfig.h" NS_CC_BEGIN GLProgram* TiledPolygonSprite::_tiledProgram = nullptr; TiledPolygonSprite* TiledPolygonSprite::create() { TiledPolygonSprite *sprite = new (std::nothrow) TiledPolygonSprite(); if (sprite && sprite->init()) { sprite->autorelease(); return sprite; } CC_SAFE_DELETE(sprite); return nullptr; } void TiledPolygonSprite::setPbvertsFile(const std::string& pbvertsFilePath) { if (_vertsFullPath == pbvertsFilePath) { return; } _vertsFullPath = pbvertsFilePath; _triangleInfo.Clear(); std::string fullPath = FileUtils::getInstance()->fullPathForFilename(pbvertsFilePath); if (fullPath.empty() || !FileUtils::getInstance()->isFileExist(fullPath)) { // 如果不选择文件或者文件不存在, 则认为要清空顶点数据和画面 _clearVertexBuffer(); return; } Data data = FileUtils::getInstance()->getDataFromFile(fullPath); if (data.isNull()) { return; } // 反序列化Protobuf消息 if (_triangleInfo.ParseFromArray(data.getBytes(), static_cast(data.getSize()))) { // 如果texture已设置,初始化渲染所需的参数 if (_texture != nullptr) { _initParamForRender(); } } } void TiledPolygonSprite::setSpriteFrame(const std::string &spriteFrameName) { SpriteFrameCache *cache = SpriteFrameCache::getInstance(); SpriteFrame *spriteFrame = cache->getSpriteFrameByName(spriteFrameName); if (CocosConfig::getAutoAddSingleImage2Cache()) { if (spriteFrame == nullptr) { Texture2D * texture = Director::getInstance()->getTextureCache()->addImage(spriteFrameName.c_str()); if (texture != nullptr) { Rect bounds = Rect(0, 0, texture->getContentSize().width, texture->getContentSize().height); spriteFrame = SpriteFrame::createWithTexture(texture, bounds); cache->addSpriteFrame(spriteFrame, spriteFrameName.c_str()); } } } setSpriteFrame(spriteFrame); } void TiledPolygonSprite::setSpriteFrame(SpriteFrame* spriteFrame) { if (_spriteFrame != spriteFrame) { CC_SAFE_RELEASE(_spriteFrame); _spriteFrame = spriteFrame; CC_SAFE_RETAIN(spriteFrame); if (_customMaterial && _spriteFrame) { _customMaterial->setParamsTexture(_spriteFrame); } } // 加载纹理 Texture2D *texture = spriteFrame->getTexture(); if (texture == nullptr || (_texture && (texture->getName() == _texture->getName()))) { return; } _texture = texture; _spSize = spriteFrame->getOriginalSize(); // 设置纹理参数 Texture2D::TexParams texParams; texParams.magFilter = GL_LINEAR; // 线性滤波 texParams.minFilter = GL_LINEAR; texParams.wrapS = GL_REPEAT; // 重复贴图 texParams.wrapT = GL_REPEAT; // 设置图集中纹理的矩形偏移和尺寸信息 Rect rect = spriteFrame->getRectInPixels(); Size atlasSize = spriteFrame->getTexture()->getContentSizeInPixels(); _texRectOffset = Vec2(rect.origin.x / atlasSize.width, rect.origin.y / atlasSize.height); _texRectSize = Vec2(rect.size.width / atlasSize.width, rect.size.height / atlasSize.height); _texture->setTexParameters(texParams); if (_triangleInfo.vertexcoords_size() != 0) { _initParamForRender(); } } void TiledPolygonSprite::_initParamForRender() { // 解析三角形的顶点数据 _initTriangleInfo(); // 初始化着色器、VAO、VBO _setupVBOAndVAO(); } std::pair TiledPolygonSprite::_calBoundingBox(const google::protobuf::RepeatedPtrField& vertices) { Vec2 minPoint(std::numeric_limits::max(), std::numeric_limits::max()); // 左下角的坐标 Vec2 maxPoint(std::numeric_limits::min(), std::numeric_limits::min()); // 右上角的坐标 for (int i = 0; i < vertices.size(); i++) { if (i % 2 == 0) { minPoint.x = std::min(minPoint.x, vertices[i]); maxPoint.x = std::max(maxPoint.x, vertices[i]); } else { minPoint.y = std::min(minPoint.y, vertices[i]); maxPoint.y = std::max(maxPoint.y, vertices[i]); } } return std::make_pair(minPoint, maxPoint); } void TiledPolygonSprite::_initTriangleInfo() { _verticesNumber = _triangleInfo.vertexindex_size(); delete[] _vertices; _vertices = new TriangleVertexInfo[_verticesNumber]; const google::protobuf::RepeatedPtrField verticesPtr(_triangleInfo.vertexcoords().begin(), _triangleInfo.vertexcoords().end()); Vec2 minPoint, maxPoint; std::tie(minPoint, maxPoint) = _calBoundingBox(verticesPtr); // 创建顶点和纹理坐标数组,uv根据最小包围盒计算 for (int i = 0; i < _verticesNumber; i++) { int index = _triangleInfo.vertexindex(i); Vec2 vertex = Vec2(_triangleInfo.vertexcoords(index * 2), _triangleInfo.vertexcoords(index * 2 + 1)); _vertices[i].vertices = vertex; // 计算纹理坐标 float u = (vertex.x - minPoint.x) / (_spSize.width); float v = (maxPoint.y - vertex.y) / (_spSize.height); _vertices[i].texCoords = Tex2F(u, v); } } void TiledPolygonSprite::_setupVBOAndVAO() { const char* tilePolygonVert = R"( attribute vec4 a_position; attribute vec2 a_texCoord; attribute vec4 a_color; varying vec4 v_fragmentColor; varying vec4 v_scale; #ifdef GL_ES varying highp vec2 v_uv0; #else varying vec2 v_uv0; #endif uniform mat4 u_modelview; void main() { gl_Position = CC_PMatrix * u_modelview * a_position; v_fragmentColor = a_color; v_uv0 = a_texCoord; } )"; const char* tilePolygonFrag = R"( varying vec4 v_fragmentColor; varying vec4 v_scale; #ifdef GL_ES varying highp vec2 v_uv0; #else varying vec2 v_uv0; #endif uniform sampler2D u_tex_r; uniform vec2 u_texRectOffset; uniform vec2 u_texRectSize; void main() { vec2 texCoord = fract(v_uv0) * u_texRectSize + u_texRectOffset; vec4 r = texture2D(u_tex_r, texCoord); gl_FragColor = vec4((r).rgb, 1.0); } )"; // 创建着色器程序 if (_tiledProgram == nullptr) { _tiledProgram = GLProgram::createWithByteArrays(tilePolygonVert, tilePolygonFrag); _tiledProgram->retain(); } setGLProgramState(GLProgramState::getOrCreateWithGLProgram(_tiledProgram)); // 创建并绑定顶点数组对象 glGenVertexArrays(1, &_vao); glBindVertexArray(_vao); // 创建并绑定顶点缓冲对象 glGenBuffers(1, &_vbo); glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(TriangleVertexInfo) * _verticesNumber, _vertices, GL_STATIC_DRAW); // 启用并指定顶点坐标属性 glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(TriangleVertexInfo), (GLvoid *)offsetof(TriangleVertexInfo, vertices)); // 启用并指定纹理坐标属性 glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_TEX_COORD); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, sizeof(TriangleVertexInfo), (GLvoid *)offsetof(TriangleVertexInfo, texCoords)); } void TiledPolygonSprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) { // 将自定义的渲染命令添加到渲染队列中 _customCommand.init(_globalZOrder, transform, flags); _customCommand.func = CC_CALLBACK_0(TiledPolygonSprite::onDraw, this, transform, flags); renderer->addCommand(&_customCommand); } void TiledPolygonSprite::onDraw(const Mat4 &transform, uint32_t flags) { if (_customMaterial != nullptr && _materialEnable == true) { for (auto technique: _customMaterial->getTechniques()) { for (auto pass: technique->getPasses()) { GLProgramState* passProgramState = pass->getGLProgramState(); if (passProgramState != nullptr) { // 保存一下sprite创建时默认的state, 关闭材质球功能时可以恢复原有着色器 if (_defaultProgramState == nullptr) { _defaultProgramState = getGLProgramState(); } setGLProgramState(passProgramState); } float deltaTime = _director->getDeltaTime(); _customMaterial->updateUniformTime(deltaTime); } } } else { // 中途关闭材质球后的处理, 如果不启用材质的话, 也不会进入到这个if中 if (_defaultProgramState != nullptr && getGLProgramState() != _defaultProgramState) { setGLProgramState(_defaultProgramState); } } GLProgramState* programState = getGLProgramState(); // 设置着色器中的变换矩阵 programState->setUniformMat4("u_modelview", transform); programState->apply(transform); // 设置纹理在图集中的偏移和尺寸 programState->setUniformVec2("u_texRectOffset", _texRectOffset); programState->setUniformVec2("u_texRectSize", _texRectSize); // 绑定顶点数组对象 glBindVertexArray(_vao); // 没有材质时,为默认着色器绑定纹理 if (_customMaterial == nullptr || _materialEnable == false) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _texture->getName()); // 设置着色器中的uniform变量 GLint textureLocation = glGetUniformLocation(programState->getGLProgram()->getProgram(), "u_tex_r"); glUniform1i(textureLocation, 0); } // 绘制 glDrawArrays(GL_TRIANGLES, 0, _verticesNumber); GLenum error = glGetError(); if (error != GL_NO_ERROR) { CCASSERT(false, "多边形平铺出现OPENGL错误"); CCLOG("OpenGL error 0x%04X in %s %s %d\n", error, __FILE__, __FUNCTION__, __LINE__); } // 取消绑定 glBindVertexArray(0); if (_customMaterial == nullptr || _materialEnable == false) { glBindTexture(GL_TEXTURE_2D, 0); } } void TiledPolygonSprite::_clearVertexBuffer() { glDeleteVertexArrays(1, &_vao); glDeleteBuffers(1, &_vbo); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } TiledPolygonSprite::~TiledPolygonSprite() { _clearVertexBuffer(); delete [] _vertices; } NS_CC_END