Is there a way properly setup the OpenGL version to 3+ without disabling some Openframeworks features?

I’m currently doing a university project and we just finished the first part. We had to switch the OpenGL version to 3+ to be able to use some shaders. Doing so caused some bugs in other parts of our apps. For example “ofSetLineWidth” stopped working. We also had to disable the use of VBOs in our of3dPrimitives because they were crashing with “unhandled exception” generated from within the OF code. This is the code we used to set our window and the OGL version:

	ofGLFWWindowSettings settings;
	settings.setGLVersion(3,3);
	settings.setSize(1900, 1000);
	settings.resizable = false;
	shared_ptr<ofAppBaseWindow> editorWindow = ofCreateWindow(settings);

	shared_ptr<MainEditor> mainEditorSharedPtr = make_shared<MainEditor>();
	MainEditorWindow mainEditorWindow(mainEditorSharedPtr);
	MainEditorController::getInstance()->setMainEditorWindowPtr(&mainEditorWindow);
	mainEditorSharedPtr->attachObserver(mainEditorWindow);
	mainEditorSharedPtr->setOfBaseWindowSharedPtr(editorWindow);

	ofRunApp(editorWindow, mainEditorSharedPtr);
	ofRunMainLoop();

Now at first i thought the problem was caused by the use of multiple windows and "ofGLFWWindowSettings " instead of “ofGLWindowSettings”. But we just had a chance to look at another student code, and he had the same problem with just one window and “ofGLWindowSettings”. I did some test using a fresh project, and the line width bug is there even if all we do is set the gl version and draw a rectangle. This is how i set the version in the new project:

    ofGLWindowSettings s;
    s.setGLVersion(3,3);
    ofCreateWindow(s);
    
    // this kicks off the running of my app
    // can be OF_WINDOW or OF_FULLSCREEN
    // pass in width and height too:
    ofRunApp( new ofApp());

And then in the “draw” version of “ofApp”:

void ofApp::draw(){  
    ofSetLineWidth(6);
    ofSetColor(ofColor::white);
    ofNoFill();
    ofDrawRectangle(10, 10, 200, 100); 
}

No matter what value i give to “ofSetLineWidth”, the line always draw with the base width. I also tried most version from 3+ and none of them seemed to correct the problem. So after all this… my question: is there a way to properly setup the OGL version to 3+ without braking some OF functionality? We could get rid of the multiple windows in our project if needed.

Hi @Thormind ,

OpenGL 3.3+ “Core Profile” trim previously utilitarian functionalities to prefer a more native approach to programming the rendering pipeline. So typically, instead of drawing lines with custom width you would rather draw directly a quad or - even better - a line with a custom geometry shader (outputing a triangle strip from a line for example).

Usually you can specify whether you want a core profile or a compatible profile for your OpenGL context at window creation but internally, on Desktop using GLFW, OpenFramework force OpenGL version 3.3+ to be Core Profile (see ofAppGLFWWindow.cpp).

So I’ll say either you stick to 3.2 or you extends / modify ofAppGLFWWindow.

1 Like

I think I remember reading some threads in the forum on this topic of “drawing thick lines” with shaders; one or two of them were pretty recent. So maybe search the forum and see if something relevant comes up. These two are the ones I was thinking of: Geometry shaders & the perennial "Fat Lines" Topic - #7 by zach and Scaling line width with matrix transformations . There might be an ofxAddon that you could use or adapt too.

tks, it’s not just the line width, that was probably the least problematic element. The worst part were the unhandled exceptions i would get from within the OF code which seemed to be related to vbos. I was able to prevent them with the of3dprimitive by turning off vbos, but i never found the solution for ofxAssimpModelLoader. I also had some weird behaviors from oflight. Some model would stop being lit completely if i changed the GL version.

Turned out the problem were linked somehow to the use of multiple windows. I brought everything in my project back to a single window and removed the shared context. Problems gone… We found this because the bugs were happening to every students who were using multiple windows in their project.

Also, all problems i had were present with every 3+ versions i tried, including 3.2.

Hey did you try multiple windows with “multiple apps”? This would pair each window with a shared_ptr<> , and a call to ofRunApp() for each pair in main.cpp. I only saw 1 call to ofRunApp() in the original code you posted. I’m wondering if the “unhandled exceptions” are arising from trying to use multiple windows with the same instance of ofApp. In the multipleWindowsExample, ofRunApp() is called for each (window, app) pair.

Hey @Thormind on which OS is this happening. My experience with OpenGL 3.x on macos has been always great and never had any of the problems you mention.

sry for the delay, it’s on win10

So is the problem related to having a shared context between windows?

I’m not 100% sure where it comes from. But with setups similar to the “first setup” below, the problems happen, even if i do not create multiple windows afterwards. With “second setup” everything seems ok.

First setup:

	ofGLFWWindowSettings settings;
	settings.setGLVersion(3,3);
	settings.setSize(1900, 1000);
	settings.resizable = false;
	shared_ptr<ofAppBaseWindow> editorWindow = ofCreateWindow(settings);

	// "MainEditor" extends "ofBaseApp"
	shared_ptr<MainEditor> mainEditorSharedPtr = make_shared<MainEditor>();

	ofRunApp(editorWindow, mainEditorSharedPtr);
	ofRunMainLoop();

Second setup:

	ofGLFWWindowSettings settings;
	settings.setGLVersion(3,3);
	settings.setSize(1900, 1000);
	settings.resizable = false;

	ofCreateWindow(settings);
	MainEditor* mainEditorPtr = new MainEditor();
	ofRunApp(mainEditorPtr);
1 Like

Mmm. Intereting.
So the only difference is that when you create the app if you use new it works while using make_shared dows not?

We were discussing about thia ahared pointers in the github issues. It will be a good idea to take a look at this. @theo @dimitre @2bit

1 Like

I’m not familiar enough with the code to know if it makes a difference, but there is also the “ofRunApp” that is different and the addition of “ofRunMainLoop”.

If someone ever decide to change how multiple windows work, if i may, i would have a suggestion for him. Make it so users can create new windows by creating a new object. This is how most frameworks i’ve used so far works. Just something like:

ofWindow secondWindow(an_object_from_a_class_that_extends_ofBaseApp);

And then, from your “ofBaseApp” class, you would be able to draw inside that second window like this:

draw() {
  secondWindow.begin();
  // bunch of command to draw in 2nd window
  secondWindow.end();
}

That way you wouldnt need to have multiple “ofBaseApp” nor to share them…

1 Like

Hey maybe I’m mis-reading, but when I look at the multiWindowOneAppExample, and how its designed, I would infer that there isn’t a way to use multiple windows with the update/draw cycle of one instance of ofApp, at least without an alternate mechanism (a listener in this case). Is this how oF works? If this is not the case, I would have expected this example to be designed differently, to illustrate how to use 2 windows with update/draw cycle of 1 ofApp.

The multiWindowExample has 1 window paired with one instance of ofApp (or equivalent); each pair has a call to ofRunApp() in main.cpp. So from this example, I would also infer that each instance of ofApp has 1 (and only 1) window associated with the update/draw cycle. But because of this thread, I’m curious to know if this is the case: 1 ofApp == 1 update/draw cycle for 1 window.

Intuitively, it seems to me that each ofApp should have 1 and only 1 window, that managing multiple windows in every ofApp could get really messy. It seems like an oF application should consist of 1 instance of ofApp, 1 update/draw cycle, and 1 window in which to render the output.

It also seems to me that an equivalent to the idea above already already exists as an ofFbo.

1 Like

Or i did not express myself properly :slight_smile: I was making a suggestion in case someone decided to redesign how multiple windows work a bit. As is, i think you need 1 “ofBaseApp” per window, anyway that is how it was setup in my project.

What i am suggesting is to change that system to be able to use one “ofBaseApp” and draw on multiple windows from it. You are correct in saying this would work a bit like FBOs, but instead of drawing in an undisplayed buffer, you would draw in a seperate window. Basically anyone who understand how FBOs work would be able to create multiple windows just as easily.

You would read input from those windows through the “ofWindow” objects. For example, “ofGetMouseX()” could still return the coordinates of the mouse from the main window, and “secondWindow.ofGetMouseX()” would return the coordinate from the 2nd window.

@Thormind it is so weird that with one setup it works and with the other it does not, as internally all these ofRunApp functions end up calling the same function. There might be something in the process that is bothering.

This is not true. you can have any number of windows. and you can create these on demand, not just in main.cpp.

as an example
ofApp.h

#pragma once

#include "ofMain.h"
#include "ofxGui.h"

class ofApp : public ofBaseApp{

	public:
		void setup();
		
		void update();
		void draw();
		void drawGui(ofEventArgs & args);

		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y );
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);


		ofParameterGroup parameters;
		ofParameter<float> radius;
		ofParameter<ofColor> color;
        ofParameter<void>guiOnNewWindow = {"GUI on new window"};
		ofxPanel gui;
    
        void createNewWindow();
        shared_ptr<ofAppBaseWindow> guiWindow = nullptr;
    void onGuiWindowClose(ofEventArgs&);
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
	ofBackground(255);
	ofSetCircleResolution(200);
    parameters.setName("parameters");
    parameters.add(radius.set("radius",50,1,100));
    parameters.add(color.set("color",100,ofColor(0,0),255));
    parameters.add(guiOnNewWindow);
    
    guiOnNewWindow.addListener(this,&ofApp::createNewWindow);
    
    gui.setup(parameters);
    ofSetBackgroundColor(0);
}


//--------------------------------------------------------------
void ofApp::update(){

}
//--------------------------------------------------------------
void ofApp::createNewWindow(){
    if(!guiWindow){
        ofGLFWWindowSettings settings;
        settings.setSize(300, 300);
        settings.setPosition(glm::vec2(0,0));
        settings.resizable = false;
    
    settings.shareContextWith = ofGetCurrentWindow();
        guiWindow = ofCreateWindow(settings);
        guiWindow->setVerticalSync(false);
    
        ofAddListener(guiWindow->events().draw,this,&ofApp::drawGui);
        ofAddListener(guiWindow->events().exit,this,&ofApp::onGuiWindowClose);
    }
}

void ofApp::onGuiWindowClose(ofEventArgs&){
    if(guiWindow){
        ofRemoveListener(guiWindow->events().draw,this,&ofApp::drawGui);
        ofRemoveListener(guiWindow->events().exit,this,&ofApp::onGuiWindowClose);
        guiWindow = nullptr;
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
	ofSetColor(color);
	ofDrawCircle(ofGetWidth()*0.5,ofGetHeight()*0.5,radius);
	ofSetColor(0);
	ofDrawBitmapString(ofGetFrameRate(),20,20);
    if(!guiWindow){
        gui.draw();
    }
}

//--------------------------------------------------------------
void ofApp::drawGui(ofEventArgs & args){
	gui.draw();
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

main.cpp


#include "ofMain.h"
#include "ofApp.h"
int main( ){
    ofSetupOpenGL(1920, 1080, OF_WINDOW);
    ofRunApp(new ofApp());
}

that would be a wrapper for what is already there. you can implement that easily.

that is not how the windowing system works. It could be possible to do such but it would be convoluted and probably not very elegant or performant. just attaching a listener function to draw in the second window, as it currently works, to me at least is a lot easier and clearer.

@roymacdonald inside createWindow function it creates a ofBaseApp here

		windowsApps[window] = std::shared_ptr<ofBaseApp>();

so I think @thormind observation is true.

I’m just trying to give positive feedback from a new user perspective. I’m a university student and a teacher is using it for one of our class. I’ve been working with it almost on a daily basis for close to 2 months now. Based on the comments from my classmates and from reading multiple forum threads during that period, i think the framework could use a bit of lovin to make it more user friendly.

I would work on it but 1) as a student, i dont think i’m qualified enough yet 2) as a student, i do not have the time yet. Imo there are 3 areas that could be improved without too much effort:

  1. Facade/wrapper/better user interfaces. As is, some functionality are extremely complex to use for new users. The multiple windows is a good example, but imo the worst one is the listener system. Some students in my group stopped trying to use it because they thought it was broken.

  2. Documentation. For many components, the doc only list constructor and method signatures. The amount of extra research needed every time you try to use a new feature is often quite high. Most of the time you end up proceeding by trial and error, without knowing if what you are doing could eventually brake something else in your code. For example yesterday i started using “ofMaterial”. I had 1 question that seemed quite basic to me but i was not able to find the answer: what range of value should i give to the method "setShininess? Is it normalized? If not, what are the min/max value? It seems to accept negative float, is it a good idea to give one? 2-3 lines of doc would have saved me hours of work…

  3. Base principles of oop. I understand it’s an open source project, but imo there needs to be some form of code review before a core feature is added. Some examples:

  • A method/class should have only 1 responsibility (also incoherence) : with “ofEasyCam”, you set the viewport by passing an “ofRectangle” to the method “begin”.

  • Encapsulation: there are multiple examples, but one that comes to mind: in “of3dPrimitive primitive”, primitive.getMesh() = …

  • Encapsulation: i do not remember exactly where it was, but one “draw” method was not set to “const”. It forced me to rework a big chuck of my code and broke my own encapsulation.

@dimitre h. I didn’t notice that. But I think that what @Thormind meat was that you needed to explicitly pass an ofBaseApp inheriting class to it in order to make a new window.
I think that the ofBaseApp that is created inside createWindow function is just a dummy, which gets replaced later (when calling ofRunApp), but it actually does nothing by itself as no listeners are registered to it. I wonder if that could be streamlined.

@Thormind don’t get me wrong. Your feedback is greatly appreciated. I do think that a lot of love is put into this project, but as an open source project nobody is really getting paid, so there are lots of parts that need work but the active devs have not been able to work on such.
Check this roadmap for the next release.

You are very welcome to work on these things, it does not matter that you are a student, and honestly I don’t know how is qualified or not. :upside_down_face: If you can help with whatever is useful that makes you qualified, I guess.
a better place for helping is in the github issues. And you are very welcome to make a Pull Request with fixes or improvements. Here is how to do such.

As for the points you make here. I don’t think that these are that simple.

It is true, the windowing system could have a bit more of friendly functions. You are very welcome to help. Just by pointing out which these could be is a good starting point.
The events subsystem is really the backbone of OF. I find it to be really elegant with a very streamlined interface considering how complex it is in the background. Because of its templated nature it can be a bit annoying if you dont pass the correct stuff to it. If it was broken nothing would really work. What it does lack is proper documentation. I wrote a chapter for the OFBook long ago about it, now it is a bit outdated, mostly because it does not really address using the ofEventListener and lambda functions. These are just barely mentioned. Yet, it is still a good place to understand how it works. https://github.com/roymacdonald/ofBook/blob/events/chapters/events/chapter.md Why it was never added to the ofBook I can’t even recall. Yet still I think it should be added

This is so true. I have been involved in this project for more than 10 years and I have never relied on the documentation. What I do is to read the header files. Most of the online documentation comes from there, it is a lot easier to access from the IDE, and from checking the implementation you can figure out several things. Again, if you found out the answer to the problem you had, and it was not in the docs, please make a PR with that improvement.

Of course there is, but I don’t think that oop correctness is really a main concern, as long as something works and it is useful. Throughout the years correctness has become a more important issue and it has been addressed in lots of places, although still needs more work. Most of the times, the problem falls into, not breaking what already was there, as doing so can be worse than what you are trying to fix.

True, although that method belongs to ofCamera, yet, setting the viewport is the only thing that it does. When you call that function with no arguments it gets the full window as the rectangle, rather than you having to pass it. Calling that function begin rather than set viewport makes sense as it is easier to remember that you have to call the end function once done. can you see the logic of such?

What do you mean by this? not following

If you can find out where that was it would be great if you open an issue about it.

Best.

1 Like

Hey @Thormind , I think its interesting that other students with multiple windows in their projects may have had similar problems. Maybe you guys will have a chance to compare experiences on using oF in your projects, and some of the other students will post their ideas in the forum, or make PRs, etc. I feel like the more specific the content, the more helpful and actionable it will be.

Also, I like what roymacdonald said about the listeners. I love using them, particularly by passing a lambda to an ofEventListener. There is a blog post about it here; just scroll down till you find it. And I think Roy’s ofBook chapter describes it too.

Also, while the “documentation” is maybe atypical in the modern age, oF has a fantastic suite of example projects that illustrate all kinds of stuff. In particular, these examples can very quickly be modified to test out ideas, and they also often capture effective design patterns for using oF.

Ill try to answer @roymacdonald and @TimChi at the same time.

  • @roymacdonald my constrains to work on the framework are not just competence related, unfortunately, a university student with a part time job does not have much time to spare. My plan is to become a teacher once i finish. I should have a bit more time to get involved then :slight_smile:

  • It’s hard for me to say if the problems come from the fact our teacher told us to use a tool we are not ready to use yet (it wouldnt be a first). Or if he should have taught us a bit how to use it properly. But from a new user perspective:

  • When available, the doc is all over the place: you have a bit of info in the code, a bit in ofBook, a bit on openframeworks.cc, a bit in the examples, and some of it is on the blog. Despite all those sources, some basic info still seem unavailable (my example with ofMaterial, the info is not in the header file either).

  • For the listeners, you have XYZ.addListener, ofAddListener, ofEventListener, ofEventListeners. “ofAddListener” alone has 16 different signatures. All of them seem to require some very specific setup to work. Like i said, maybe i’m not advanced enough to use this framework, but with all the previous ones i’ve used, i could do the exact same thing by only extending 1 class for the events, and 1 class for the listeners. I do understand the multiple event/listener system in OF probably comes from the framework evolving over the years and keeping the old stuff for compatibility. But the proper use of interfaces (or facades) would make all of this invisible to the end user.

  • “I don’t think that oop correctness is really a main concern, as long as something works and it is useful”. On this we really don’t share the same opinion. If we were talking about a small standalone executable project, or if the language wasnt C++, i would be a bit more inclined to agree. But a framework is meant to be used by other programmers. It’s meant to be evolving, modified, extended, etc. This is exactly where you want the strongest adhesion to oop basics. The user experience with a framework should be as important as the feature it offers. For example, one might say “OF offers a very powerful and flexible event handling system”, but a user would reply “it might, but i was never able to make it work in my project”, or “it does, but it took me a full day of work to figure out how to make something as simple as a custom event”. Maybe if i explain the examples i gave a bit further it would help to illustrate my pov

  • “ofCamera.begin()” @roymacdonald said “setting the viewport is the only thing that it does”. The thing is, from a user perspective, when you see “begin” / “end”, you think “ok, this is to delimit what’s going to be drawn with this camera”. Or this is to say “now every draw command i write within this begin/end is going to be affected by the camera”. The same way “ofMaterial.begin()” / “ofMaterial.end” (or shader.begin/end) is delimiting the drawn elements affected by the material/shader. To me, there is no logical relation between “begin” and “viewport”. It also doesnt seem to be coherent with how other “begin/end” methods works in the framework.

  • “primitive.getMesh() = …” the framework is allowing an object to be modified through a “getter”. This should normally be a big “no”. It’s worst in C++ because it means your getter is not “const”. With many other common languages, braking encapsulation like that would ( or could) not be as big of a deal for the end user. They would most likely not realize it’s there unless they have to look at the code. But in C++, if my code is properly encapsulated, it means i can’t use this getter in any of my “const” method.