I'm trying to create an abstract base class that allows implementations to render into a sf::RenderTexture using raw OpenGL calls. A bit like this:
class GLBaseWidget : public Drawable, public Transformable {
public:
void update(); // Rerenders into texture if dirty bit set
void draw(sf::RenderTarget&, sf::RenderStates) const override; // Draws texture sprite to target
math::Vec2i size() const;
void rotateBy(const math::Vec3f&);
protected:
GLWidget(math::Vec2i size);
void setDirtyBit();
virtual void doRendering() = 0;
private:
sf::RenderTexture texture_;
bool dirty_bit_;
float rotation_matrix_[16];
};
This is a simplified version. The base class holds some state (the rotation matrix for example) and methods to modify the state. The update() method is where I am having trouble.
Obviously, the update() method checks for the dirty bit first. Then I want it to set up the projection and modelview matrices before calling the virtual doRendering() method. Afterwards, the OpenGL state should be back to the way it was before calling update().
I've written similar code using OpenGL Renderbuffers once, but not with gl::RenderTexture. Can someone guide me to the necessary steps for this to work? Ideally, the implementing classes will only push the actual vertices into the pipeline and everything will work.
I'm sorry for not adding more information immediately, I was in the process of trying some stuff, but I did not have anything tangential enough that I felt it would be worth showing. I can post the current state of things.
Here are current implementation details for the base class:
GLBaseWidget::GLBaseWidget(int width, int height) : dirty_bit_(true) {
texture_.create(width, height, true);
}
void GLBaseWidget::update() {
if (dirty_bit_) {
texture_.setActive(true);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
float aspect = static_cast<float>(texture_.getTexture().getSize().x) / static_cast<float>(texture_.getTexture().getSize().y);
float fov = 60.f;
float near = 1.f / std::tan(fov / 2.f * (M_PI / 180.f)) / 100.f;
glFrustum(-.01f, .01f, -.01f / aspect, .01f / aspect, near, 100.f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(.0f, .0f, -4.0f);
// TODO: rotation here
renderToTexture();
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
texture_.setActive(false);
dirty_bit_ = false;
}
}
void GLBaseWidget::draw(sf::RenderTarget& target, sf::RenderStates states) const {
sf::Sprite sprite(texture_.getTexture());
states.transform *= getTransform();
target.draw(sprite, states);
}
Here is an example implementation that tries to draw just a triangle (PtWidget publicly inherits GLBaseWidget):
PtWidget::PtWidget(int width, int height)
: CubeWidget(width, height) {}
void PtWidget::renderToTexture() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glVertex3f( 1.0f, -1.0f, 0.0f);
glEnd();
}
This is the main function that calls this code:
int main() {
sf::RenderWindow window(sf::VideoMode(512, 256), "Cubetest", sf::Style::Titlebar|sf::Style::Close);
PtWidget pt_widget(256, 256);
pt_widget.setPosition(0, 0);
while (window.isOpen()) {
window.pushGLStates();
window.clear();
window.draw(pt_widget);
window.display();
window.popGLStates();
sf::Event event;
while (window.waitEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
pt_widget.update();
}
return 0;
}
When I run it, I get some random textures from memory (from other applications or parts of the OS) in the left half of the window. When I close the window, the application "quits unexpectedly".
I'm not sure if this is absolutely minimal, since something in here might be causing a side effect. This code does reproduce my error though. It shows a window of 512x256, with the left half being filled with what looks like random areas of video memory.
#include <cmath>
#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>
int main() {
sf::RenderWindow window(sf::VideoMode(512, 256), "Cubetest", sf::Style::Titlebar|sf::Style::Close);
sf::RenderTexture texture;
texture.create(256, 256, true);
texture.setActive(true);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
float fov = 60.f;
float near = 1.f / std::tan(fov / 2.f * (M_PI / 180.f)) / 100.f;
glFrustum(-.01f, .01f, -.01f, .01f, near, 100.f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(.0f, .0f, -4.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 1.0f, 1.0f);
glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 0.0f);
glVertex3f( 1.0f, -1.0f, 0.0f);
glEnd();
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
texture.setActive(false);
window.pushGLStates();
window.clear();
sf::Sprite sprite(texture.getTexture());
window.draw(sprite);
window.display();
window.popGLStates();
sf::Event event;
while (window.waitEvent(event) && event.type != sf::Event::Closed) {}
window.close();
return 0;
}