Reputation: 1044
I'm trying to write a UI in OpenGL and have run into a problem when I resize a control.
As I shrink the panel, the text texture seems to shrink or get corrupted and eventually the buttons do the same. The buttons are not tied to the size of the window, so the problem isn't with calculating the sizes. I printed the size of the text texture and the button sizes and they stayed consistent during the test.
So every time I resize the window, here is what is happening:
onResize
Delete TexturedRectangle object
| Delete 9 sprites (including vertex data) used for the TexturedRectangle
| Delete the RenderTexture
New TexturedRectangle Object
| Create 9 sprites (new vertex data) for the textured rectangle
| Render the rectangle using the 9 sprites to a new RenderTexture the size of the window
I went through the code and made sure I am deleting the old data off the gpu before creating new buffers. Are my buffers getting corrupted or is the way I'm rendering to the RenderTextures incorrect? I checked glGetError() and get no errors during run time. Could the problem be with the OpenGL stack? I can't tell where the problem is because the buttons aren't changed at all when I resize the window.
Sprite::Sprite()
: ISprite(), mVbo(0) {
mDiffuse = ResourceCache::getSingleton().getTexture("Texture_NOTFOUND");
createVbo();
}
Sprite::Sprite(ITexture *diffuse, FloatRect textureBounds)
: ISprite(), mVbo(0) {
mDiffuse = diffuse;
if(textureBounds.x == 0 && textureBounds.y == 0) {
mTextureBounds = diffuse->getBounds();
} else {
mTextureBounds = textureBounds;
}
createVbo();
}
Sprite::~Sprite() {
glDeleteBuffers(1, &mVbo);
}
void Sprite::draw(IRenderTarget *target, RenderState *state) const {
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, mVbo);
switch(state->getRenderMode()) {
default:
case RenderState::DIFFUSE:
mDiffuse->bindTexture();
break;
case RenderState::NORMAL_MAP:
mNormalMap->bindTexture();
break;
case RenderState::HEIGHT_MAP:
mHeightMap->bindTexture();
break;
};
glPushMatrix();
glTranslatef(mPosition.x, mPosition.y, mPosition.z);
glColor4f(mColor.r, mColor.g, mColor.b, mColor.a);
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, x));
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, tx));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glPopMatrix();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
void Sprite::createVbo() {
if(mVbo != 0) {
glDeleteBuffers(1, &mVbo);
}
// Generate the VBO
glGenBuffers(1, &mVbo);
Vector2f size = getSize();
float texW = mDiffuse->getWidth();
float texH = mDiffuse->getHeight();
float srcW = size.x / texW;
float srcH = size.y / texH;
// Calculate the vertices
Vertex verts[] = {{0.f, 0.f, 0.f, mTextureBounds.x / texW, mTextureBounds.y / texH},
{size.x, 0.f, 0.f, (mTextureBounds.x / texW) + srcW, mTextureBounds.y / texH},
{0.f, size.y, 0.f, mTextureBounds.x / texW, (mTextureBounds.y / texH ) + srcH},
{size.x, size.y, 0.f, (mTextureBounds.x / texW) + srcW, (mTextureBounds.y / texH) + srcH}};
int vertSize = sizeof(verts);
// Bind the VBO
glBindBuffer(GL_ARRAY_BUFFER, mVbo);
// Submit the vertex data to the GPU
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 4, &verts[0].x, GL_STATIC_DRAW_ARB);
// Unbind the VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Repeating Sprite
RepeatingSprite::RepeatingSprite(Texture *diffuseTexture, FloatRect spriteBounds, int xRepeat, int yRepeat)
: ISprite(), mXRepeat(xRepeat), mYRepeat(yRepeat) {
mVbo = 0;
mDiffuse = diffuseTexture;
mTextureBounds = spriteBounds;
createVbo();
}
RepeatingSprite::~RepeatingSprite() {
glDeleteBuffers(1, &mVbo);
}
void RepeatingSprite::draw(IRenderTarget *target, RenderState *state) const {
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, mVbo);
switch(state->getRenderMode()) {
default:
case RenderState::DIFFUSE:
mDiffuse->bindTexture();
break;
case RenderState::NORMAL_MAP:
mNormalMap->bindTexture();
break;
case RenderState::HEIGHT_MAP:
mHeightMap->bindTexture();
break;
};
glPushMatrix();
glTranslatef(mPosition.x, mPosition.y, mPosition.z);
glColor4f(mColor.r, mColor.g, mColor.b, mColor.a);
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, x));
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, tx));
glDrawArrays(GL_QUADS, 0, (mXRepeat * mYRepeat) * 4);
glPopMatrix();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
void RepeatingSprite::createVbo() {
int totalRepeats = mXRepeat * mYRepeat;
float textureWidth = mDiffuse->getWidth();
float textureHeight = mDiffuse->getHeight();
Vertex *vertices = new Vertex[totalRepeats*4];
int counter = 0;
// For each sprite count, create a quad
for(float y = 0; y < mYRepeat; y++) {
for(float x = 0; x < mXRepeat; x++) {
Vertex v1 = {x * mTextureBounds.w,
y * mTextureBounds.h, 0.f,
mTextureBounds.x / textureWidth,
mTextureBounds.y / textureHeight};
Vertex v2 = {x * mTextureBounds.w + mTextureBounds.w,
y * mTextureBounds.h, 0.f,
(mTextureBounds.x / textureWidth) + (mTextureBounds.w / textureWidth),
mTextureBounds.y / textureHeight};
Vertex v3 = {x * mTextureBounds.w,
y * mTextureBounds.h + mTextureBounds.h, 0.f,
mTextureBounds.x / textureWidth,
(mTextureBounds.y / textureHeight) + (mTextureBounds.h / textureHeight)};
Vertex v4 = {x * mTextureBounds.w + mTextureBounds.w,
y * mTextureBounds.h + mTextureBounds.h, 0.f,
(mTextureBounds.x / textureWidth) + (mTextureBounds.w / textureWidth),
(mTextureBounds.y / textureHeight) + (mTextureBounds.h / textureHeight)};
vertices[counter] = v1;
counter++;
vertices[counter] = v2;
counter++;
vertices[counter] = v4;
counter++;
vertices[counter] = v3;
counter++;
}
}
glGenBuffers(1, &mVbo);
glBindBuffer(GL_ARRAY_BUFFER, mVbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * (totalRepeats*4), &vertices[0].x, GL_STATIC_DRAW_ARB);
glBindBuffer(GL_ARRAY_BUFFER, 0);
delete[] vertices;
}
Render Texture
RenderTexture::RenderTexture(float width, float height) {
mWidth = width;
mHeight = height;
// Create the color buffer
glGenTextures(1, &mId);
glBindTexture(GL_TEXTURE_2D, mId);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)mWidth, (int)mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// Create the framebuffer
glGenFramebuffers(1, &mFbo);
glBindFramebuffer(GL_FRAMEBUFFER, mFbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mId, 0);
GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER);
assert(err == GL_FRAMEBUFFER_COMPLETE); // Make sure texture is valid
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
RenderTexture::~RenderTexture() {
glDeleteBuffers(1, &mFbo);
glDeleteTextures(1, &mId);
mFbo = 0;
}
void RenderTexture::preDraw() {
// store the glViewport and glEnable states
glPushAttrib(GL_VIEWPORT_BIT);
// Bind the frame buffer
//glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, mFbo);
// Save the current matrix
glPushMatrix();
glLoadIdentity();
// Setup the projection matrix for the render target
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glViewport(0, 0, (int)mWidth, (int)mHeight);
glOrtho(0, mWidth, 0.f, mHeight, 0.f, 100.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
}
void RenderTexture::postDraw() {
// Pop the render target's projection matrix off the stack
glPopMatrix();
// Restore previouse projection matrix
glPopMatrix();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Restore the previous viewport settings
glPopAttrib();
}
Upvotes: 2
Views: 466
Reputation: 13877
In OpenGL, when you apply a transform of some sort to an object and see other objects being affected, a good place to start looking is your transform and stack manipulation logic.
So in RenderTexture::preDraw()
you have:
glPushMatrix();
// ...
glMatrixMode(GL_PROJECTION);
glPushMatrix();
and in RenderTexture::postDraw()
:
glPopMatrix();
// Restore previouse projection matrix
glPopMatrix();
without any call to glMatrixMode()
between them.
It won't work properly like this. Each matrix mode has its own stack, so that second glPopMatrix()
is popping off the wrong stack.
You will need to go something like:
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
Upvotes: 2