原图

效果图

不知道还有没有bug
#ifndef _CoScale9_H_
#define _CoScale9_H_
#include "cocos2d.h"
USING_NS_CC;
//#ifndef CC_TEXTURE_ATLAS_USE_VAO
// #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
// #define CC_TEXTURE_ATLAS_USE_VAO 1
// #else
// /* Some Windows display adapter driver cannot support VAO. */
// /* Some android devices cannot support VAO very well, so we disable it by default for android platform. */
// /* Blackberry also doesn't support this feature. */
// #define CC_TEXTURE_ATLAS_USE_VAO 0
// #endif
//#endif
// 注意记得找到 CC_TEXTURE_ATLAS_USE_VAO 把 0 设置为 1
// 纹理必须是2的幂次方
// 纹理不能旋转 (TexturePacker Allow rotation 去掉)
//////////////////////////////////////////////////////////////////////////
class CoScale9 : public Node
{
public:
CREATE_FUNC(CoScale9);
public:
virtual void cleanup() override
{
glDeleteBuffers(1, &mVBO);
glDeleteBuffers(1, &mIBO);
glDeleteVertexArrays(1, &mVAO);
CC_SAFE_RELEASE(mFrame);
Node::cleanup();
} // cleanup
virtual bool init() override
{
mVAO = 0;
mVBO = 0;
mIBO = 0;
mFrame = 0;
if ( !Node::init() )
return false;
//////////////////////////////////////////////////////////////////////////
// vertex shader
static const char* vert = " \n\
attribute vec4 aPosition; \n\
attribute vec2 aTexCoord; \n\
\n\
varying vec4 vColor; \n\
varying vec2 vTexCoord; \n\
\n\
uniform vec4 uColor; \n\
uniform vec4 uTexScale; \n\
uniform vec4 uPosScale; \n\
void main() \n\
{ \n\
vec4 posC = aPosition; \n\
posC.x = posC.x * uPosScale.z; \n\
posC.y = posC.y * uPosScale.w; \n\
\n\
gl_Position = CC_MVPMatrix * posC; \n\
vec2 texC = aTexCoord; \n\
texC = texC * uTexScale.zw + uTexScale.xy; \n\
vTexCoord = texC; \n\
vColor = uColor; \n\
}";
// fragment shader
static const char* frag = " \n\
#ifdef GL_ES \n\
precision mediump float; \n\
#endif \n\
uniform vec4 uInsets; \n\
varying vec4 vColor; \n\
varying vec2 vTexCoord; \n\
\n\
void main() \n\
{ \n\
vec2 texC = vTexCoord; \n\
texC.x = texC.x - uInsets.x; \n\
texC.y = texC.y - uInsets.y; \n\
texC.x = mod(texC.x, uInsets.z); \n\
texC.y = mod(texC.y, uInsets.w); \n\
texC.x = texC.x + uInsets.x; \n\
texC.y = texC.y + uInsets.y; \n\
vec4 col = texture2D(CC_Texture0, texC); \n\
gl_FragColor = vColor*col; \n\
}";
// 创建着色器程序
auto program = new GLProgram;
program->initWithByteArrays(vert, frag);
program->link();
program->updateUniforms();
program->autorelease();
this->setGLProgram(program);
// set uniform
GLint uColorLoc = glGetUniformLocation(program->getProgram(), "uColor");
glUniform4f(uColorLoc, 1, 1, 1, 1);
CHECK_GL_ERROR_DEBUG();
GLint uTexScaleLoc = glGetUniformLocation(program->getProgram(), "uTexScale");
glUniform4f(uTexScaleLoc, 0, 0, 1, 1);
CHECK_GL_ERROR_DEBUG();
GLint uPosScaleLoc = glGetUniformLocation(program->getProgram(), "uPosScale");
glUniform4f(uPosScaleLoc, 0, 0, 1, 1);
CHECK_GL_ERROR_DEBUG();
GLint uInsetsLocation = glGetUniformLocation(program->getProgram(), "uInsets");
glUniform4f(uInsetsLocation, 0, 0, 1, 1);
CHECK_GL_ERROR_DEBUG();
GLint positionLoc = glGetAttribLocation(program->getProgram(), "aPosition");
GLint texCoordLoc = glGetAttribLocation(program->getProgram(), "aTexCoord");
//////////////////////////////////////////////////////////////////////////
// 创建Vertex Array Object
glGenVertexArrays(1, &mVAO);
glBindVertexArray(mVAO);
CHECK_GL_ERROR_DEBUG();
// 创建统一 VBO
typedef struct {
float position;
float texCoord;
} Vertex;
float unit = 0.5f;
Vertex datas] = {
{{-unit, -unit, 0}, {0, 1}},
{{ unit, -unit, 0}, {1, 1}},
{{-unit, unit, 0}, {0, 0}},
{{ unit, unit, 0}, {1, 0}},
}; // datas
glGenBuffers(1, &mVBO);
glBindBuffer(GL_ARRAY_BUFFER, mVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(datas), datas, GL_STATIC_DRAW);
CHECK_GL_ERROR_DEBUG();
//指定将要绘制的顶点数据
glEnableVertexAttribArray(positionLoc);
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, position));
CHECK_GL_ERROR_DEBUG();
//指定每一个顶点的纹理坐标
glEnableVertexAttribArray(texCoordLoc);
glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, texCoord));
CHECK_GL_ERROR_DEBUG();
// 创建顶点索引
GLubyte indices]={0,1,2, 2,3,1};
glGenBuffers(1, &mIBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
CHECK_GL_ERROR_DEBUG();
return true;
} // init
void setSpriteFrameByName(const std::string& name, float l, float t, float r, float b)
{
SpriteFrame* frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name);
setSpriteFrame(frame, l, t, r, b);
}
void setSpriteFrame(SpriteFrame* frame, float l, float t, float r, float b)
{
CC_SAFE_RETAIN(frame);
CC_SAFE_RELEASE(mFrame);
mFrame = frame;
mOriginRect = mFrame->getRect();
mLeftPadding = l;
mTopPadding = t;
mRightPadding = r;
mBottomPadding = b;
mHCenterPadding = mOriginRect.size.width - mLeftPadding - mRightPadding;
mVCenterPadding = mOriginRect.size.height - mTopPadding - mBottomPadding;
setInnerSize(mHCenterPadding, mVCenterPadding);
GL::bindTexture2D(mFrame->getTexture()->getName());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
}
void setInnerSize(float width, float height)
{
mInnerWidth = width;
mInnerHeight = height;
Node::setContentSize(Size(mInnerWidth+mLeftPadding+mRightPadding, mInnerHeight+mTopPadding+mBottomPadding));
}
virtual void setContentSize(const Size& size) override
{
setInnerSize(size.width - mLeftPadding - mRightPadding, size.height - mTopPadding - mBottomPadding);
}
float getLeftPadding() const
{
return mLeftPadding;
}
float getTopPadding() const
{
return mTopPadding;
}
float getRightPadding() const
{
return mRightPadding;
}
float getBottomPadding() const
{
return mBottomPadding;
}
virtual void visit(Renderer *renderer, const Mat4& parentTransform, uint32_t parentFlags) override
{
if (mFrame == nullptr)
return;
if (getContentSize().width == 0 || getContentSize().height == 0)
return;
mCustomCommand.init(_globalZOrder);
mCustomCommand.func = CC_CALLBACK_0(CoScale9::onDraw, this);
renderer->addCommand(&mCustomCommand);
Node::visit(renderer, parentTransform, parentFlags);
} // visit
private:
void setCenterTexRange()
{
setTexRange(
mOriginRect.origin.x + mLeftPadding,
mOriginRect.origin.y + mTopPadding,
mHCenterPadding,
mVCenterPadding);
}
void setLeftTexRange()
{
setTexRange(
mOriginRect.origin.x,
mOriginRect.origin.y+ mTopPadding,
mLeftPadding,
mVCenterPadding);
}
void setRightTexRange()
{
setTexRange(
mOriginRect.origin.x+mOriginRect.size.width - mRightPadding,
mOriginRect.origin.y+ mTopPadding,
mRightPadding,
mVCenterPadding);
}
void setTopTexRange()
{
setTexRange(
mOriginRect.origin.x+mLeftPadding,
mOriginRect.origin.y,
mHCenterPadding,
mTopPadding);
}
void setBottomTexRange()
{
setTexRange(
mOriginRect.origin.x+mLeftPadding,
mOriginRect.origin.y+mOriginRect.size.height - mBottomPadding,
mHCenterPadding,
mBottomPadding);
}
void setLeftTopTexRange()
{
setTexRange(
mOriginRect.origin.x,
mOriginRect.origin.y,
mLeftPadding,
mTopPadding);
}
void setRightTopTexRange()
{
setTexRange(
mOriginRect.origin.x+mOriginRect.size.width - mRightPadding,
mOriginRect.origin.y,
mRightPadding,
mTopPadding);
}
void setRightBottomTexRange()
{
setTexRange(
mOriginRect.origin.x+mOriginRect.size.width - mRightPadding,
mOriginRect.origin.y+mOriginRect.size.height - mBottomPadding,
mRightPadding,
mBottomPadding);
}
void setLeftBottomTexRange()
{
setTexRange(
mOriginRect.origin.x,
mOriginRect.origin.y+mOriginRect.size.height - mBottomPadding,
mLeftPadding,
mBottomPadding);
}
void setTexRange(float tX, float tY, float tW, float tH)
{
Size oSize = mFrame->getTexture()->getContentSize();
float invSx = 1 / oSize.width;
float invSy = 1 / oSize.height;
float x = tX*invSx;
float y = tY*invSy;
float w = tW*invSx;
float h = tH*invSy;
auto program = getGLProgram();
GLint uInsetsLocation = glGetUniformLocation(program->getProgram(), "uInsets");
glUniform4f(uInsetsLocation, x, y, w, h);
CHECK_GL_ERROR_DEBUG();
}
void setTextureRect(float x, float y, float w, float h)
{
auto program = getGLProgram();
//////////////////////////////////////////////////////////////////////////
// 计算纹理坐标
float atlasWidth = (float)mFrame->getTexture()->getPixelsWide();
float atlasHeight = (float)mFrame->getTexture()->getPixelsHigh();
float texX = x / atlasWidth;
float texY = y / atlasHeight;
float texW = w / atlasWidth;
float texH = h / atlasHeight;
GLint uTexScaleLoc = glGetUniformLocation(program->getProgram(), "uTexScale");
glUniform4f(uTexScaleLoc, texX, texY, texW, texH);
CHECK_GL_ERROR_DEBUG();
//////////////////////////////////////////////////////////////////////////
static Size winSize = Director::getInstance()->getWinSize();
GLint uPosScaleLoc = glGetUniformLocation(program->getProgram(), "uPosScale");
glUniform4f(uPosScaleLoc, 0, 0, w, h);
CHECK_GL_ERROR_DEBUG();
}
// opengl 绘制
void onDraw()
{
Director::getInstance()->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
//use vao,因为vao记录了每一个顶点属性和缓冲区的状态,所以只需要绑定就可以使用了
//(还要绑定buffer,不然报错)
glBindVertexArray(mVAO);
glBindBuffer(GL_ARRAY_BUFFER, mVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIBO);
CHECK_GL_ERROR_DEBUG();
//获得当前shader
auto glProgram = getGLProgram();
//使用shader
glProgram->use();
// 绑定纹理
GL::bindTexture2D(mFrame->getTexture()->getName());
// 绘制
const Mat4 &mat = this->getNodeToWorldTransform();
Mat4 matT;
// center
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, mat);
glProgram->setUniformsForBuiltins();
setCenterTexRange();
setTextureRect(mOriginRect.origin.x+mLeftPadding, mOriginRect.origin.y+mTopPadding, mInnerWidth, mInnerHeight);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
CHECK_GL_ERROR_DEBUG();
// left
matT.set(mat);
matT.translate(-(mLeftPadding+mInnerWidth)/2, 0, 0);
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, matT);
glProgram->setUniformsForBuiltins();
setLeftTexRange();
setTextureRect(mOriginRect.origin.x+0, mOriginRect.origin.y+mTopPadding, mLeftPadding, mInnerHeight);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
// top
matT.set(mat);
matT.translate(0, (mTopPadding+mInnerHeight)/2, 0);
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, matT);
glProgram->setUniformsForBuiltins();
setTopTexRange();
setTextureRect(mOriginRect.origin.x+mLeftPadding, mOriginRect.origin.y, mInnerWidth, mTopPadding);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
// right
matT.set(mat);
matT.translate((mRightPadding+mInnerWidth)/2, 0, 0);
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, matT);
glProgram->setUniformsForBuiltins();
setRightTexRange();
setTextureRect(mOriginRect.origin.x+mOriginRect.size.width-mRightPadding, mOriginRect.origin.y+mTopPadding, mRightPadding, mInnerHeight);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
// bottom
matT.set(mat);
matT.translate(0, -(mBottomPadding+mInnerHeight)/2, 0);
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, matT);
glProgram->setUniformsForBuiltins();
setBottomTexRange();
setTextureRect(mOriginRect.origin.x+mLeftPadding, mOriginRect.origin.y+mOriginRect.size.height-mBottomPadding, mInnerWidth, mBottomPadding);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
// right top
matT.set(mat);
matT.translate((mRightPadding+mInnerWidth)/2, (mTopPadding+mInnerHeight)/2, 0);
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, matT);
glProgram->setUniformsForBuiltins();
setRightTopTexRange();
setTextureRect(mOriginRect.origin.x+mOriginRect.size.width-mRightPadding, mOriginRect.origin.y+0, mRightPadding, mTopPadding);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
// left top
matT.set(mat);
matT.translate(-(mLeftPadding+mInnerWidth)/2, (mTopPadding+mInnerHeight)/2, 0);
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, matT);
glProgram->setUniformsForBuiltins();
setLeftTopTexRange();
setTextureRect(mOriginRect.origin.x+0, mOriginRect.origin.y+0, mLeftPadding, mTopPadding);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
// right bottom
matT.set(mat);
matT.translate((mRightPadding+mInnerWidth)/2, -(mBottomPadding+mInnerHeight)/2, 0);
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, matT);
glProgram->setUniformsForBuiltins();
setRightBottomTexRange();
setTextureRect(
mOriginRect.origin.x+mOriginRect.size.width-mRightPadding,
mOriginRect.origin.y+mOriginRect.size.height-mBottomPadding,
mRightPadding,
mBottomPadding);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
// left bottom
matT.set(mat);
matT.translate(-(mLeftPadding+mInnerWidth)/2, -(mBottomPadding+mInnerHeight)/2, 0);
Director::getInstance()->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, matT);
glProgram->setUniformsForBuiltins();
setLeftBottomTexRange();
setTextureRect(
mOriginRect.origin.x,
mOriginRect.origin.y+mOriginRect.size.height-mBottomPadding,
mLeftPadding,
mBottomPadding);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (GLvoid*)0);
glBindVertexArray(0);
CHECK_GL_ERROR_DEBUG();
// 提示用
CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, 6);
Director::getInstance()->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
} // onDraw
private:
CustomCommand mCustomCommand;
GLuint mVAO;
GLuint mVBO;
GLuint mIBO;
Rect mOriginRect;
SpriteFrame* mFrame;
float mLeftPadding;
float mTopPadding;
float mRightPadding;
float mBottomPadding;
float mHCenterPadding;
float mVCenterPadding;
float mInnerWidth;
float mInnerHeight;
}; // CoScale9
#endif // _CoScale9_H_
```