JOGL Questions - Guidance on creating Scene class

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

JOGL Questions - Guidance on creating Scene class

glangho
Hello,

I'm trying to build more complicated games, i.e., games with multiple "scenes". I ran into a few situations where I can't quite figure out the best way to proceed with JOGL. Was hoping someone would be kind enough to provide some advice. Also looking for answers to a few general questions I had.

1. I'm trying to create a Scene class. A new Scene would be created when rendering/updating certain objects becomes obsolete. For example, the game would start off in a TitleScene, transition into the WorldMapScene, then have the option of either a BattleScene or a MenuScene based on game logic / user input. Assuming I have the necessary management classes to save and recall Scenes on the heap, what's the best/cleanest way to handle this as far as JOGL goes?

As of now, I have a Scene abstract class which implements GLEventListener. In my Main class I initiate GLCanvas, Animator, and attach TitleScene using GLCanvas.addGLEventListener. Then in TitleScene I have a transition to add WorldMapScene under display() using drawable.addGLEventListener and TitleScene is removed as a listener. This effectively sets up TitleScene to be removed from the heap. Each Scene is responsible for initializing any OpenGL, shader, and textures needed, as well as displaying, reshaping, and disposing.

Is this the correct way to go? The other idea I had was to have my Main class implement GLEventListener, store the active scene on the heap and then pass down the GL object through the stack, i.e., calling scene.display(GL gl) under Main.display(GLAutoDrawable drawable). I had reservations of this approach. I'm unsure whether or not Scene would need it's own init, display, reshape, and dispose or only a draw / update methods and let the Main class worry about init, display, reshape, and dispose.

2. If I have a program that uses multiple textures and shaders, what's the best way to initialize them? In my current approach for my Scene class (see question #1) each Scene has it's own init() method so I'm initializing all of my OpenGL configurations, textures, and shaders in each Scene - even if the scene before is using the exact same code. This seems wasteful, but I was reading the JOGL User Guide (not sure how old this is) and it says JOGL may call init() more than once so maybe it's a moot point. This makes me think maybe using the second approach I mention in #1 is more feasible. I could then initialize everything once in Main that I would need (i.e., all shaders and textures) and pass them down either in the stack or as part of the Scene constructor.

3. I have an Event system with a type of event called RemoveVBO. I use it when an object is destroyed/killed. The RenderSystem has a method called removeVBO() which is executed when the RemoveVBO event is received. I use GLContext.getCurrent().getGL() and pass the GL object to GLArrayDataServer.destroy(GL gl). Is it safe to call GLContext.getCurrent().getGL() using either approach from question #1 in this use case? Is there any time calling GLContext to obtain the GL object not a good idea / practice?

4. Does the Texture class support Texture Arrays? I saw the CubeMap example and tried to adopt the same logic for GL_2D_TEXTURE_ARRAY but I got an invalid operation error from the GL_TEXTURE_2D_ARRAY. I was able to fit my textures in one 1024x1024 texture so I ended up not needing it, but wanted to know if it's supported.

            Texture texture = TextureIO.newTexture(GL2.GL_TEXTURE_2D_ARRAY);            
	    File file1 = new File("src/main/resources/images/spritesheet.png");
            File file2 = new File("src/main/resources/images/spritesheet_complete.png");
            TextureData data1 = TextureIO.newTextureData(GLContext.getCurrentGL().getGLProfile(), file1, false, IOUtil.getFileSuffix(file1));
            TextureData data2 = TextureIO.newTextureData(GLContext.getCurrentGL().getGLProfile(), file2, false, IOUtil.getFileSuffix(file2));
            texture.updateImage(gl, data1, 0);
            texture.updateImage(gl, data2, 1);
Thanks!
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

elect
1) see reddit

2) int arrays controlled by enums. You can retrieve an int by an enum with ordinal(). If you use same code for multiple scenes, create additional subscene in between and extend from it. Read that guide with a grain of salt, it is quite old.

3) GLContext.getCurrent().getGL() should be fine. Anyway I always tend to grab the GL object from the display() in order to minimize any potential bug, since OpenGL is a state machine this makes easier to monitor/debug the order of the gl commands.

4) I'd say not, but I didn't spend so much time getting it work since I saw it is quite old. I am working to update it. If you need it now I have a self-made lib for the meanwhile.

my 2 cents
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

gouessej
Administrator
In reply to this post by glangho
The less outdated user's guide is rather here:
http://jogamp.org/jogl/doc/userguide/
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

gouessej
Administrator
In reply to this post by glangho
GLArrayDataServer.destroy() deletes the OpenGL identifiers but it doesn't release the native memory of the direct NIO buffers.

There is no need of creating several GLEventListener instances, just create a single one and call your drawing code in it.

You can use a state machine to structure your game. I can show you a schema if you don't see what I mean.

You should look at how the current scenegraph APIs manage all those notions even though you don't plan to use any of them, just as sources of inspiration instead of reinventing the wheel.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

gouessej
Administrator
In reply to this post by elect
elect wrote
3) GLContext.getCurrent().getGL() should be fine. Anyway I always tend to grab the GL object from the display() in order to minimize any potential bug, since OpenGL is a state machine this makes easier to monitor/debug the order of the gl commands.
Storing the GL instance into a field or a non-local variable is a bad practice. I already explained why numerous times here. You might accidentally use an invalidated instance and it is really hard to detect. I advise you and others to use GLContext.getCurrent().getGL() as much as possible. You can store a GL instance in a local variable but you should avoid passing it to another method or storing it into a field.
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

glangho
In reply to this post by elect
elect wrote
1) see reddit
Ok, you win :) Was hoping not to have to change my code again XD
elect wrote

2) int arrays controlled by enums. You can retrieve an int by an enum with ordinal(). If you use same code for multiple scenes, create additional subscene in between and extend from it. Read that guide with a grain of salt, it is quite old.
I want to make sure I understand your approach for initialization. You're saying I should initialize all my shaders and textures in an enum and store their glGetShaderi/glGenTexture values in an int array assigned as a value of the enum? I'm not sure where ordinal() fits in since we're talking about int arrays.
<quote author="elect">
elect wrote
4) I'd say not, but I didn't spend so much time getting it work since I saw it is quite old. I am working to update it. If you need it now I have a self-made lib for the meanwhile.
I think I'm good for now. If I end up needing something I can probably just use the native OpenGL commands.
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

glangho
In reply to this post by gouessej
gouessej wrote
GLArrayDataServer.destroy() deletes the OpenGL identifiers but it doesn't release the native memory of the direct NIO buffers.
The memory won't be freed until dispose() is called, correct?
gouessej wrote
There is no need of creating several GLEventListener instances, just create a single one and call your drawing code in it. You can use a state machine to structure your game. I can show you a schema if you don't see what I mean. You should look at how the current scenegraph APIs manage all those notions even though you don't plan to use any of them, just as sources of inspiration instead of reinventing the wheel.
I'm all for using what's available. I find all the JOGL convenience classes to be really helpful. Would you be able to post the example schema you mentioned?
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

gouessej
Administrator
Yes except if the last Java object referring to a direct NIO buffer gets garbage collected. However, it's quite common in a Java game to create a single huge direct NIO buffer once and slice it later (done in some commercial games) or create smaller direct NIO buffers and destroying them manually when you know you don't need them (what I do).

The Fettle API for state machines is used by Candy Crush. The schema is somewhere on my blog, I'm looking for it...
Julien Gouesse | Personal blog | Website
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

elect
In reply to this post by glangho
glangho wrote
Ok, you win :)  Was hoping not to have to change my code again XD
it's just my opinion, if you want you can leave as it it :)

glangho wrote
I want to make sure I understand your approach for initialization.  You're saying I should initialize all my shaders and textures in an enum and store their glGetShaderi/glGenTexture values in an int array assigned as a value of the enum?  I'm not sure where ordinal() fits in since we're talking about int arrays.
For the shaders, I suggest you to use the jogl utility class, it makes things much easier and less verbose.
For textures, save the int id into an integer array and use an enum to handle it. Something like here
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

gouessej
Administrator
In reply to this post by glangho
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

glangho
elect wrote
For the shaders, I suggest you to use the jogl utility class, it makes things much easier and less verbose. For textures, save the int id into an integer array and use an enum to handle it. Something like here
This is quite clever. I like it.

My initial thought was to create a bunch of Scenes for each state and each having their own systems for input handling, rendering, ai, phyics, etc (I'm using an Entity Component System design so I have many systems). If I'm understanding things correctly, the more professional approach is to have one set of systems with logic to handle different states. So for example, my input system would have one set of controls for BattleState while another set of controls for MenuState all in the same object. It looks like the concept of a SceneNode (part of a SceneGraph) could be used to organize which entities need processing based on the state as well.

Am I close to understanding or completely off?

One last questionon GLAutoDrawable. You mentioned to only the GL object locally, not pass it or the GLAutoDrawable down on the stack, and instead use GLContext.getCurrent().getGL(). If you want to offload rendering to a different class from your GLEventListener implementation like this:

    ...
    public void display(GLAutoDrawable drawable){
        renderSystem.draw();
    }
    ....
The RenderSystem draw() method would then look like this, correct?

    ...
    public void draw(){
        GLContext.getCurrent().getGL();
        // draw stuff
    }
The more I think about this post, the less I feel a need to have a separate class for rendering. Maybe it just makes more sense to do everything in the GLEventListener display method using GLAutoDrawable. It certainly sounds safer. Is this "professionally / commercially" acceptable?

I wanted to thank you both for your time. This has really been enlightening for me.
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

elect
glangho wrote
The more I think about this post, the less I feel a need to have a separate class for rendering.  Maybe it just makes more sense to do everything in the GLEventListener display method using GLAutoDrawable.  It certainly sounds safer.  Is this "professionally / commercially" acceptable?  
If your rendering is quite simple, yes, but if you will end up with 1/2k+ lines, then I'd say something may be easily improved. Incapsulate code and divide it by a logic point of view, it makes it more readable, that means in turn less bugs, easier to debug, easier to re-use (if multiple scene needs to execute the same steps), easier to implement new features, etc..

Lines are not a reliable measurement, but they can be a signal/symptom.
Reply | Threaded
Open this post in threaded view
|

Re: JOGL Questions - Guidance on creating Scene class

gouessej
Administrator
In reply to this post by glangho
Each state can still have its own separate input handler, root node, ... I don't use any entity component system as it is overkill in my case. When you move from one state to another one, you can clean your mess and install the state-specific objects. I would rather separate the concerns by putting the different sets of controls into different classes if I were you.

Yes, it's better because if you pass a GL object to a method, it's not obvious that the receiver is executed on a thread on which the OpenGL context is current and you're not sure that the GL instance is still valid. If you follow my advise, in the worst case you'll get a clear GL exception telling you that there is no current OpenGL context on this thread.

You can still use different root nodes and different rendering methods for your states.
Julien Gouesse | Personal blog | Website