Author Topic: Switching between multiple Worlds  (Read 3948 times)

Offline K24A3

  • long
  • ***
  • Posts: 231
    • View Profile
Switching between multiple Worlds
« on: September 19, 2011, 04:09:06 pm »
I'm trying to utilize two separate worlds, one world is for the menu, the other is for the game.

My design is this:

-start the app
-load the menu world
-when 'start' is pressed, load another world
-go back to the original world when done

I created a class for the menu world, within that menu world I load another world on the fly, and redirect the main GLSurfaceView.Renderer OnFrame call to the game world.

I've ran into all kinds of problems. It's crashing when loading the game world, it always crashed on world.draw() for some reason. If I remove world.draw, the skymap renders ok but nothing else. If I remove all the object3D creations, draw.world works fine.
I tried using the same World both both the menu and game, but still have problems.
The confusing thing is, if I load the game world first, it works ok.


But anyway, ignore all that... Time to start fresh!

1. My main questions is, how do you handle two separate worlds correctly? Logically?

2. Do you need to dispose the first world then reload the second?  Or can both worlds coexists side by side, redirecting the onFrame method to the current world?

3. Rather calling/loading a world from within a world (in terms of class files), would it be better to create a World Handler that disposes the current world before creating the new world, having each world in it's own class?


Any help is appreciated, thanks.
« Last Edit: September 19, 2011, 04:10:56 pm by K24A3 »

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Switching between multiple Worlds
« Reply #1 on: September 19, 2011, 05:40:52 pm »
There's no problem with rendering different worlds into one frame buffer. No need to manage them somehow. You just pick the world that you need and render it. If that doesn't work, then its a bug and i would like to see the exception or a test case.

Offline K24A3

  • long
  • ***
  • Posts: 231
    • View Profile
Re: Switching between multiple Worlds
« Reply #2 on: September 20, 2011, 01:31:51 am »
Ok I'll persist further with the current setup and double check all the logic.

The two worlds are sharing the singular TextureManager and framebuffer, everything else is separate.

Thanks for the help, I'll post the exception if I get stuck.

Offline K24A3

  • long
  • ***
  • Posts: 231
    • View Profile
Re: Switching between multiple Worlds
« Reply #3 on: September 20, 2011, 10:41:45 am »
Ok I found the main problem. For those who run into the same problems, here's the story...

The GL renderer was calling onFrame while I was loading objects into the 2nd world, causing Object3D pointers to crash since they were null. Obviously the renderer is running on it's own thread.

What confused me is that the null objects were not generating an exception in the renderer thread (onFrame function). Instead it was just jumping/return out of the onFrame function half way through the function by itself, before it was reaching the world.draw() call, then crashing the app around 10 seconds later.

Anyway I started fresh and simply used the same World class for both worlds, using world.removeAllObjects() when switching worlds, using a boolean flag in onFrame to return straight away to avoid rendering while loading objects, and pre-loading all the textures at the start of the app rather than load them on the fly when needed. It's much less complicated that way in my opinion. The menu textures are small anyway.

Offline K24A3

  • long
  • ***
  • Posts: 231
    • View Profile
Re: Switching between multiple Worlds
« Reply #4 on: September 20, 2011, 02:40:39 pm »
I'm having headaches trying to load 3D objects during onDrawFrame(GL10 gl) from the renderer thread. I think there is a sync problem with the renderer thread and the touch input thread

If I do this:

onDrawFrame()
{
    if(bLoad3DObjects == true)
   {
      LoadWorldObjects();
                bLoad3DObjects = false; // all 3d objects are loaded, should be ready to draw
      return;
   }
   
   world.draw()...
   etc..
}

and..

onTouchEvent(MotionEvent event)
{
   if(ACTION_DOWN == true)
   {
      bLoad3DObjects = true;
   }
}


Next time it calls onDrawFrame, it crashes saying:


Thread [<9> GLThread 10] (Suspended (exception OutOfMemoryError))   
   GLSurfaceView$GLThread.run() line: 1184   




It doesn't crash when I do this:

onDrawFrame()
{
    if(bDontRender == true)
   {
      return;
   }
   
   world.draw()...
   etc..
}

and..

onTouchEvent(MotionEvent event)
{
   if(ACTION_DOWN == true)
   {
      bDontRender = true;
      LoadWorldObjects();
      bDontRender = false;
   }
}

But that introduces a crash when returning back to the menu. I'm guessing it's a threading problem. Should I be using Synhronize() somewhere?

Does onDrawFrame() expect something to be rendered?

Is it safe to exit from the start of onDrawFrame if I load or unload from a Touch event?
« Last Edit: September 20, 2011, 03:01:03 pm by K24A3 »

Offline K24A3

  • long
  • ***
  • Posts: 231
    • View Profile
Re: Switching between multiple Worlds
« Reply #5 on: September 20, 2011, 02:57:25 pm »
Maybe I should use queueEvent()?

http://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer.html

Threading
The renderer will be called on a separate thread, so that rendering performance is decoupled from the UI thread. Clients typically need to communicate with the renderer from the UI thread, because that's where input events are received. Clients can communicate using any of the standard Java techniques for cross-thread communication, or they can use the queueEvent(Runnable) convenience method.

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Switching between multiple Worlds
« Reply #6 on: September 20, 2011, 03:03:26 pm »
queueEvent is one options. Or you could do something like:

Code: [Select]
onDrawFrame()
{
   synchronize(this) {
    world.draw()...
    etc..
   }
}

and..

onTouchEvent(MotionEvent event)
{
   if(ACTION_DOWN == true)
   {
      synchronize(renderer) {
      LoadWorldObjects();
      }
   }
}

Never use simple boolean (or other) flags for synchronization, because you might still fall into a trap where both paths will be executed in parallel.

However, i don't think that this really solves the actual problem. Loading your objects in the render thread should actually work fine (it's not nice, because you are blocking the screen updates with that, but it should work...). If you are getting an OOM-error, maybe your problem is rather that your resources are a bit too large? The Wiki has some information about this: http://www.jpct.net/wiki/index.php/Reducing_memory_usage

Still, i don't see why you are dealing with two worlds in such a complicated manner. Just initialize both at startup and render them on demand once they have been fully loaded. As said: If this doesn't work (for any other reason than out of memory...), it's a bug. There should be no need to switch a world's content on the fly unless you are going to replace it anyway.

Offline K24A3

  • long
  • ***
  • Posts: 231
    • View Profile
Re: Switching between multiple Worlds
« Reply #7 on: September 20, 2011, 03:40:03 pm »
I tried adding Synchronized() to those two areas and it helped with one problem, but it still crashes when exiting to the menu and it crashes at random when loading the app. It's got to be a threading/timing issue. The Resources are not large.

I'll redesign the system to use two Worlds as you recommended, and perhaps use RENDER_WHEN_DIRTY instead since I don't see the value of having a dedicated rendering thread if it complicates everything. Keeping the flow in my own loop seems cleaner and more logical to me.

Appreciate your help, thanks.


Offline K24A3

  • long
  • ***
  • Posts: 231
    • View Profile
Re: Switching between multiple Worlds
« Reply #8 on: September 21, 2011, 03:42:39 pm »
I've got it all working now.

I ended up using two preloaded worlds, placing each world in it's own class file, and using a main class to handle both worlds. I ended up using queueEvent in the TouchEvent to handle the multiple threads.

Now I can swap between the two worlds 60 times a second via TouchEvent, causing the two worlds to appear blended together. It's solid as a rock now :)

Thanks again Egon.