I’m trying to implement offscreen rendering on Android. On desktop I simply call start(JmeContext.Type.OffscreenSurface) method from LegacyApplication and engine starts to initialize itself. On android it seems that nothing happens after I call start(JmeContext.Type.OffscreenSurface). simpleInitApp() does not get called and I do not see any activity in the logs.
Is it possible to use OffscreenSurface mode on Android at all?
I want to generate an image and save it locally without displaying anything on screen. I just press a button, the app renders an image in a memory and save it on a device.
Well, i haven’t tried that before, so you have multiple solutions :
Use gl.glReadPixels() in GLSurfaceView#onDrawFrame(), but you will have to access the internals of jme android OGLESContext#onDrawFrame() to inject an action into the onDrawFrame().
But still there is a question how to initialize an engine?
LegacyApplication.start(JmeContext.Type.OffscreenSurface) seems does not initialize a rendering pipeline at all. Maybe I’m doing something wrong.
Does anybody know is offscreen rendering is implemented on Android?
Or I should create a View and set it as render target for jme, but will it render on it if it will not be added to the interface?
A JmeSurfaceView is an android RelativeLayout that holds a GlSurfaceView upon which a GlSurfaceView.Renderer can initialize and update a jme application, this is an example code :
public class MainActivity extends AppCompatActivity implements OnRendererCompleted, OnExceptionThrown {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
/*define the android view with it's id from xml*/
JmeSurfaceView jmeSurfaceView = new JmeSurfaceView(this);
/*set the jme game*/
jmeSurfaceView.setLegacyApplication(new MyGame());
jmeSurfaceView.setOnExceptionThrown(this);
jmeSurfaceView.setOnRendererCompleted(this);
/*start the game*/
jmeSurfaceView.startRenderer(JmeSurfaceView.NO_DELAY);
/*set your view as activity content */
setContentView(jmeSurfaceView);
}
}
More at :
EDIT :
you can skip the setContentView(jmeSurfaceView); part which displays the game on your screen , from jmeSurafecView you can getLegacyApplication() and then convert the frame buffer into byte buffer and output the result to a Bitmap image using Screenshots appstate.
LegacyApplication#start() on android starts a blank jMonkeyEngine context, so you will need to bind jme application lifecycle to glSurfaceView renderer, JmeSurfaceView and OGLESContext already does this, so you will only need to use JmeSurfaceView,
Just tried this approach. If I skip setContentView(jmeSurfaceView); then simpleInitApp() is not get called either. It seems that rendering initializes only when view is attached to a layout…
Tried, no success. (setContentView() replaces current layout so I use addView() but I think it makes no difference)
When I use view.setVisibility(View.Gone) nothing happens. Without it after view is attached to a layout - jme starts to initialize rendering
Use view.setVisibility(View.INVISIBLE) and it differs from GONE that it applies something like an alpha map that helps to keep the view out of color but the view isn’t gone.
Use OnRendererCompleted listener to listen for renderer status and set the view as gone when rendering is completed :
/**
* Fired when the user delay in ms is up #{@link JmeSurfaceView#startRenderer(int)}.
* @param application the current jme game instance
* @param appSettings the current game settings
*/
@Override
public void onRenderCompletion(LegacyApplication application, AppSettings appSettings) {
findViewById(R.id.jmeSurfaceView).setVisibility(View.GONE);
}
i think better make it invisible so to be still visible to the renderer lifecycle…
So I answered for my first question. Offscreen rendering is not implemented on Android. When calling LegacyApplication.start(JmeContext.Type.OffscreenSurface) it calls JmeAndroidSystem.newContext(AppSettings settings, Type contextType) and contextType is ignored there. It always creates context with Display rendering type.
So now the question is there is possible to create GLSurfaceView (or JmeSurfaceView) and set rendering to it without adding it to the layoyt.
/**
* Fired when the user delay in ms is up #{@link JmeSurfaceView#startRenderer(int)}.
* @param application the current jme game instance
* @param appSettings the current game settings
*/
@Override
public void onRenderCompletion(LegacyApplication application, AppSettings appSettings) {
jmeSurfaceView.setLayoutParams(new FrameLayout.LayoutParams(0, 0));
}
In my understanding it will create surface with zero size so I will not be able to render anything on it. But I need to render scene on the surface and then get the image data from it and store it as image file.
Hmm, when I’m set INVISIBLE for JmeSurfaceView it still became visible for me. And when I’m trying to make INVISIBLE for GLSurfaceView then rendering does not started. Need to look further…