[Q] Multi window (again)

Hi,

I was looking for help in previous posts about multi window, but with no success.
What I want to achieve:

  • grab web cam image (done)
  • draw its image into window (done)
  • dynamically create more separate windows with the same image from web cam (?)

thanks for any help
Paum

Hello,
That reminds me of Raven Kwok’s Matryoshka piece done in processing.
It should totally be doable in openFrameworks, did you check the multiwindow examples ?
I think you’ll need to set a window flag on child windows to share the GL context, so that the childs have access to the OpenGL textures.

2 Likes

Thanks Daan, that works. Next problem is how to make seperate windows in object instances, not in main.cpp. This matryoska is very inspiring. Thank you.

I haven’t done this before, but I remembered a recent-ish thread that might be of some help. @dimitre posted some code but I can’t tell if its in main.cpp or not:

1 Like

Thanks for this. I somehow cannot grasp the problem how to make it workng.

  • I have ofApp with working videoGrabber (webcam)
  • I have my custom object, which creates its own window with inherited ofGLFWWindowSettings with sharedContext from main window of ofApp. Object has its own ofPixels storage which I am filling with videoGrabber.getPixels() function in ofApp::update().
  • I am drawing my objects in for loop in ofApp::draw() and this point seems wrong. All the contents which should be in separate windows is drawn into main ofApp window.
    … still looking for solution.

The multiWindowOneApp example (and also dimitre’s code) uses a listener to draw into the secondary window(s). So could something like that work?

There is also the multiWindowExample, where ofApp has a shared_ptr to another class with its own window (made in main.cpp). It might be interesting to see if this pointer (and window and everything else) could be made in ofApp instead.

Some of the OF examples are very very simple, but contain the basic design pattern for how to do a particular thing in the context of how OF works.

1 Like

just a quick note that I’ve done something like this in the past w. openframeworks !

https://www.instagram.com/p/BFA8AJapNve/

I don’t have the code in front of me, but it’s definitely possible and I’m happy to find the code to see if there any tips.

1 Like

Guys I got it! It took me few hours to fiddle with it but, finally success. @TimChi yes that is true. The examples are simple but showing important design.
thank you all for guiding me.
best

1 Like

very nice :slight_smile:

my code is here. If I could go further, I have a question whether it is possible to get rid of the main ofApp window…

int main() {

  ofGLFWWindowSettings settings;
  settings.setSize(300, 300);
  settings.setPosition(glm::vec2(300, 0));
  settings.resizable = false;
  auto mainWindow = ofCreateWindow(settings);

  settings.shareContextWith = mainWindow;

  auto mainApp = make_shared<ofApp>(settings);

  ofRunApp(mainWindow, mainApp);
  ofRunMainLoop();
}

class ofApp : public ofBaseApp {

public:
  ofApp(ofGLFWWindowSettings &);
 
  ofVideoGrabber vidGrabber;
  vector<shared_ptr<Sq>> square;
  ofGLFWWindowSettings settings;

 // and the obvious stuff
};

ofApp::ofApp(ofGLFWWindowSettings &s) { settings = s; }

//--------------------------------------------------------------
void ofApp::setup() {
  
  // here is vidGrabber code, the same as in webcam OF example 
  // ...

  ofSetEscapeQuitsApp(true);

 // create 5 objects...
  for (int i = 0; i < 5; i++) {
    settings.setPosition(glm::vec2(ofRandom(0, 1000), ofRandom(0, 1000)));
    shared_ptr<Sq> sqApp(new Sq);
    this->square.push_back(sqApp);
    this->square.back()->setup(settings);
    ofRunApp(this->square.back()->win, sqApp);
  }
}

//--------------------------------------------------------------
void ofApp::update() {
  vidGrabber.update(); 
  for (int i = 0; i < 5; i++) {
    this->square[i]->update(vidGrabber.getPixels());
  }
}

… and my object

#ifndef SQ_H_
#define SQ_H_

#include "ofMain.h"
class Sq : public ofBaseApp {
public:
  void setup(ofGLFWWindowSettings &);
  void update(ofPixels &);
  void draw();
  ofPixels p;
  ofTexture t;
  std::shared_ptr<ofAppBaseWindow> win;
};

#endif // SQ_H_

#include "Sq.h"

void Sq::setup(ofGLFWWindowSettings &s) {
  p.allocate(300, 300, OF_PIXELS_RGB);
  t.allocate(p);
  s.setSize(300, 300);
  s.resizable = false;
  win = ofCreateWindow(s);
}

void Sq::update(ofPixels &p) { t.loadData(p); }

void Sq::draw() { t.draw(0, 0, 300, 300); }

3 Likes

Hey glad you have it working and also thanks for posting some code! You might be able to do this ā€œheadlessā€, so that ofApp does not have a window (or anything openGL related), but where the instances of class Sq do have windows. There is the noWindowExample as a guide.

I think if the current app still works after calling ofVideoGrabber::setUseTexture(false), then a headless ofApp would be interesting to try.

Also @zach I LOVE this video in the link! So fun to see you in so many many windows!

I just tried, guided by the noWindowExample, but there are some glitches in the other windows. I think problem could be, that I need to call settings.shareContextWith = mainWindow; to share openGL .
I am not sure, wheter VideoGrabber initialized in ofApp without window could work with noWindow and share properly its context…

Why do you think VideoGrabber::setUseTexture(false) could be interesting? I have read the docs, but I did’t get it :slight_smile:

Hey I was thinking that if the video grabber didn’t need a texture, then ofApp could become windowless (and without any openGL context). A call to ofVideoGrabber::setUseTexture() would tell the grabber to just use the pixels. I’m thinking that no openGL context means nothing with an ofTexture can be used.

You could try eliminating settings.shareContextWith = mainWindow; in main.cpp; I’m not sure it’s needed and it seems like it may not be doing anything since the main window has been created and no other windows are created in main().

Each instance of class Sq is kind of like its own ofApp in that it derives from ofBaseApp (like ofApp does), has its own window, and a call to ofRunApp() in ofApp::setup(). So each Sq should have its own openGL context anyway. But maybe try it and see how it goes. The design pattern seems similar to the multiWindow example, except that the things that happen in main.cpp (in the example) are happening in ofApp instead.

Also, I’ll bet zach would be more than happy to find some code if it would help.

From my knowledge (which is limited at this time), this code cannnot by eliminated, because there is one global web cam videoGrabber which feeds the ā€˜kids’ - instances of other object through ofPixels, so if I understand it correctly GL space sould be shared.

Ah yes I see now how settings.shareContextWith = mainWindow; is needed in main for the custom constructor of ofApp.

So my question is, whether it is possible to build a of app which has no mainWindow, but web cam videoGrabber is running in it, and when receiving a signal from outside ( e.g. OSC) opens instance of ā€˜child’ window with image in it. I don’t know if I am clear enough. I just don’t want main app win. Such an application but with a mainwin works.

I took a look, and it looks like I’m sharing the context with the sub windows. this is how I set it up (also a note this code is from 2016)

int main( ){
    
    
    ofGLFWWindowSettings settings;
    settings.width = 600;
    settings.height = 480;
    settings.setPosition(ofVec2f(300,0));
    settings.resizable = true;
    shared_ptr<ofAppBaseWindow> mainWindow = ofCreateWindow(settings);
    shared_ptr<ofApp> mainApp(new ofApp);
    shared_ptr<ofAppBaseWindow> gui2Window [68];
    
    for (int i = 0; i < 68; i++){
        
        settings.width = 100;
        settings.height = 100;
        settings.setPosition(ofVec2f(0,0));
        settings.resizable = false;
        settings.shareContextWith = mainWindow;
        gui2Window[i] = ofCreateWindow(settings);
        gui2Window[i]->setVerticalSync(false);
        
        ofAddListener(gui2Window[i]->events().draw,mainApp.get(),&ofApp::drawSmall, 200 + i);
        
        mainApp->gui2Window2[i] = gui2Window[i];

    }
    

    ofRunApp(mainWindow, mainApp);
    ofRunMainLoop();
}

it seems like I store the windows in the ofApp – to move them

shared_ptr<ofAppBaseWindow> gui2Window2 [68];

and in ofApp::drawSmall , it seems like I check which window I am since it was not clear

void ofApp::drawSmall(const void * sender, ofEventArgs & args){
   
    for (int i = 0; i < 68; i++){
        if (gui2Window2[i].get()  == ofGetWindowPtr()){

I hope this is helpful !

1 Like

Yes you should be able to do this. Here is basic code to get started. Just add anything else as needed to either ofApp or Sq. With a windowless setup, ofApp will only be able to do things that don’t require rendering or openGL stuff (as I understand it).

Also you can use listeners for the instances of Sq; zach’s code above has an example of this, as do some of the projects in /examples/windowing/.

main.cpp: (this comes from noWindowExample)

#include "ofMain.h"
#include "ofApp.h"
#include "ofAppNoWindow.h"

int main() {
    auto window = make_shared<ofAppNoWindow>();

    //to have a normal windowed app comment the line above and uncomment the lines below
    //ofGLWindowSettings settings;
    //auto window = ofCreateWindow(settings);
    
    ofRunApp(window, make_shared<ofApp>());
    ofRunMainLoop();
}

ofApp.h

#pragma once
#include "ofMain.h"
#include "Sq.h"

class ofApp : public ofBaseApp{
public:
    void setup();
    void update();
    void draw();
    
    void makeSq();
        
    std::vector<std::shared_ptr<Sq>> sqs;
    ofVideoGrabber grabber;
};

ofApp.cpp

#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
    ofSetFrameRate(60);
    grabber.setUseTexture(false);
    grabber.setDesiredFrameRate(60);
    grabber.initGrabber(640, 480);
    makeSq();
}
//--------------------------------------------------------------
void ofApp::update(){
    grabber.update();
    
    ofPixels& pixels = grabber.getPixels();
    for(size_t i{0}; i < sqs.size(); ++i){
        sqs.at(i)->setPixels(pixels);
    }
    
    if(ofGetFrameNum() % 300 == 0){
//         uncomment to make more windows
//        makeSq();
    }
}
//--------------------------------------------------------------
void ofApp::draw(){

}
//--------------------------------------------------------------
void ofApp::makeSq(){
    ofGLWindowSettings settings;
    settings.setSize(300, 300);
    settings.setPosition(glm::vec2(300,0));
    auto window = ofCreateWindow(settings);
    auto app = std::make_shared<Sq>();
    ofRunApp(window, app);
    sqs.push_back(app);
}

Sq.h

#ifndef Sq_h
#define Sq_h

#include "ofMain.h"
class Sq : public ofBaseApp {
public:
    Sq();
    void setup();
    void update();
    void draw();
    
    inline void setPixels(ofPixels& pix) {pixels = pix;}
    ofTexture& getTexture() {return texture;}
    
protected:
    ofPixels pixels;
    ofTexture texture;
};
#endif /* Sq-h */

Sq.cpp

#include "Sq.h"

Sq::Sq(){
    texture.allocate(300, 300, GL_RGBA);
}
void Sq::setup(){
    ofSetFrameRate(30);
}
void Sq::update(){
    texture.loadData(pixels);
}
void Sq::draw(){
    texture.draw(0, 0);
    ofDrawBitmapStringHighlight("frameNum: " + ofToString(ofGetFrameNum()), 20.0, 20.0);
}

OK well adding a video player worked sometimes. I’ll work on it a bit more and see if I can figure it out.

Edit: I’m revising my previous post with some new code that got a bit closer. The windows get created and the pixels from the video grabber make it to a few of them (or so). Maybe they need to come into focus when their pixels are updated and then draw themselves.

Final Edit: I got it working! The code in the previous post should work OK now. The trick was to let each Sq have an ofPixels, and then update its own ofTexture.

1 Like

Thank you @TimChi , you’ve helped me a lot. I’ve learned some new stuff & all my struggles are in peace now. hi @zach thanks you too :slight_smile:

case resolved.

1 Like