Author Topic: Need to render two worlds on two GLSurfaceView  (Read 2271 times)

Offline ginopeloso

  • byte
  • *
  • Posts: 13
    • View Profile
Need to render two worlds on two GLSurfaceView
« on: July 20, 2015, 05:57:08 pm »
I know it is not possible to have two GLSurfaceView on the same layout. Basically I need to render the same scene (the same world) on two separated parts of my screen. I've read it is possible to use TextureViews, but I can't draw my FrameBuffer on a TextureView...
The question is simple: is there a way for drawing the same World on two parts of the same GLSurfaceView (one on the left half of the screen and one on the right half).

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 11776
    • View Profile
    • http://www.jpct.net
Re: Need to render two worlds on two GLSurfaceView
« Reply #1 on: July 20, 2015, 09:32:37 pm »
Yes, for example by rendering it into a Texture (not a TextureView or something) and blit that. You create two textures (most likely NPOTTexture instances), setup your camera for the first render pass, assign one texture as render target, render, remove the render target, setup the camera for the second pass, assign the second texture and repeat the process. Finally, remove the render target and blit both textures into the FrameBuffer.

Offline ginopeloso

  • byte
  • *
  • Posts: 13
    • View Profile
Re: Need to render two worlds on two GLSurfaceView
« Reply #2 on: July 23, 2015, 02:35:37 pm »
Following your explanation I wrote the following code (which does not work as expected):

Code: [Select]
// create a NPOTTexture with same height and half width of the framebuffer
NPOTTexture blitTexture = new NPOTTexture(mFrameBuffer.getWidth()/2, mFrameBuffer.getHeight(), RGBColor.GREEN);

// draw the scene on the texture
mFrameBuffer.setRenderTarget(blitTexture);
mWorld.renderScene(mFrameBuffer);
mWorld.draw(mFrameBuffer);
mFrameBuffer.removeRenderTarget();

// draw the texture on the left half of the framebuffer...
mFrameBuffer.blit(blitTexture, 0, 0, 0, 0, blitTexture.getWidth(), blitTexture.getHeight(), false);
// ...and also on the right half
mFrameBuffer.blit(blitTexture, 0, 0, mFrameBuffer.getWidth()/2, 0, blitTexture.getWidth(), blitTexture.getHeight(), false);

mFrameBuffer has the same size of the screen.
I only see the texture rendered on the left part (I think there is a big misunderstanding on the usage of the "blit" method).
« Last Edit: July 23, 2015, 02:59:35 pm by ginopeloso »

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 11776
    • View Profile
    • http://www.jpct.net
Re: Need to render two worlds on two GLSurfaceView
« Reply #3 on: July 23, 2015, 02:58:54 pm »
You have to add a call to display() once the render to texture is done, i.e. do this

Code: [Select]
mWorld.draw(mFrameBuffer);
mFrameBuffer.display();
mFrameBuffer.removeRenderTarget();


In addition, jPCT-AE don't know how when to blit if you render no actual scene. Try to create another, empty world and add this to the end of your render method:

Code: [Select]
emptyWorld.renderScene(mFrameBuffer);
emptyWorld.draw(mFrameBuffer);
mFrameBuffer.display();

It won't render anything but it will trigger the enqueued blits to be executed.

P.S.: If you want to blit two different scene to the left and to the right, use two textures as target. Don't render into one and blit it twice, because that won't work in all cases.

Offline ginopeloso

  • byte
  • *
  • Posts: 13
    • View Profile
Re: Need to render two worlds on two GLSurfaceView
« Reply #4 on: July 23, 2015, 03:02:02 pm »
I have to display the same scene on the left half and on the right one (when I started the question I had two worlds, now I have just one world and need to display the same scene of that world on two parts of the screen).
« Last Edit: July 23, 2015, 03:04:35 pm by ginopeloso »

Offline ginopeloso

  • byte
  • *
  • Posts: 13
    • View Profile
Re: Need to render two worlds on two GLSurfaceView
« Reply #5 on: July 23, 2015, 05:23:38 pm »
Using the following code:

Code: [Select]
NPOTTexture blitTexture = new NPOTTexture(mFrameBuffer.getWidth()/2, mFrameBuffer.getHeight(), RGBColor.GREEN);

mFrameBuffer.setRenderTarget(blitTexture);
mWorld.renderScene(mFrameBuffer);
mWorld.draw(mFrameBuffer);
mFrameBuffer.display();
mFrameBuffer.removeRenderTarget();

mFrameBuffer.blit(blitTexture, 0, 0, 0, 0, blitTexture.getWidth(), blitTexture.getHeight(), false);
mFrameBuffer.blit(blitTexture, 0, 0, mFrameBuffer.getWidth() / 2, 0, blitTexture.getWidth(), blitTexture.getHeight(), false);
mFrameBuffer.display();

I see two green rectangles (look at the first line). It means that blitTexture has been correctly blitted on the FrameBuffer; the problem is that mFrameBuffer does not render on blitTexture

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 11776
    • View Profile
    • http://www.jpct.net
Re: Need to render two worlds on two GLSurfaceView
« Reply #6 on: July 23, 2015, 07:49:32 pm »
Which renderer are you using? Render targets don't work reliable with some OpenGL ES 1.x drivers. If you haven't already, use OpenGL ES 2.0 instead.

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 11776
    • View Profile
    • http://www.jpct.net
Re: Need to render two worlds on two GLSurfaceView
« Reply #7 on: July 23, 2015, 07:53:20 pm »
Also, add a clear-call after assigning the render target.

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 11776
    • View Profile
    • http://www.jpct.net
Re: Need to render two worlds on two GLSurfaceView
« Reply #8 on: July 23, 2015, 08:58:06 pm »
Here's an example, because this might make it clearer. It also shows how to compensate for the fact that the GL screen's coordinate system starts at the bottom of the screen:

Code: [Select]
package com.threed.jpct.rendertargets;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Logger;
import com.threed.jpct.NPOTTexture;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.RGBColor;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.MemoryHelper;

public class RenderTargets extends Activity {

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private World dummyWorld = null;

private NPOTTexture target1 = null;
private NPOTTexture target2 = null;

private Object3D cube = null;
private int fps = 0;

private Light sun = null;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

mGLView.setEGLContextClientVersion(2);

mGLView.setPreserveEGLContextOnPause(true);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);
}

@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}

@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}

@Override
protected void onStop() {
super.onStop();
}

protected boolean isFullscreenOpaque() {
return true;
}

class MyRenderer implements GLSurfaceView.Renderer {

private long time = System.currentTimeMillis();

public MyRenderer() {
//
}

public void onSurfaceChanged(GL10 gl, int w, int h) {

fb = new FrameBuffer(w, h); // OpenGL ES 2.0 constructor

world = new World();
world.setAmbientLight(20, 20, 20);

sun = new Light(world);
sun.setIntensity(250, 250, 250);

TextureManager.getInstance().addTexture("texture", new Texture(16, 16, RGBColor.GREEN));

cube = Primitives.getCube(10);
cube.calcTextureWrapSpherical();
cube.setTexture("texture");
cube.strip();
cube.build();

world.addObject(cube);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 50);
cam.lookAt(cube.getTransformedCenter());

SimpleVector sv = new SimpleVector();
sv.set(cube.getTransformedCenter());
sv.y -= 100;
sv.z -= 100;
sun.setPosition(sv);
MemoryHelper.compact();

target1 = new NPOTTexture(w / 2, h, RGBColor.BLUE);
target2 = new NPOTTexture(w / 2, h, RGBColor.RED);

dummyWorld = new World();
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}

public void onDrawFrame(GL10 gl) {
cube.rotateY(0.01f);
cube.rotateX(0.01f);

// Render into first target
fb.setRenderTarget(target1);
fb.clear(RGBColor.BLUE);
world.renderScene(fb);
world.draw(fb);
fb.display();
fb.removeRenderTarget();

// Render into second target
fb.setRenderTarget(target2);
fb.clear(RGBColor.RED);
world.renderScene(fb);
world.draw(fb);
fb.display();
fb.removeRenderTarget();

// jPCT-AE needs this to setup some stuff for correct blitting
dummyWorld.renderScene(fb);
dummyWorld.draw(fb);

// Blit both textures. Please note that the blitting is upside down
// (starting at the bottom with negative destination height, because
// OpenGL's coordinate system starts at the bottom. This compensates
// for that.
fb.blit(target1, 0, 0, 0, fb.getHeight(), target1.getWidth(), target1.getHeight(), fb.getWidth() / 2, -fb.getHeight(), -1, false);
fb.blit(target2, 0, 0, fb.getWidth() / 2, fb.getHeight(), target1.getWidth(), target1.getHeight(), fb.getWidth() / 2, -fb.getHeight(), -1, false);

fb.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
}
}

}


Offline ginopeloso

  • byte
  • *
  • Posts: 13
    • View Profile
Re: Need to render two worlds on two GLSurfaceView
« Reply #9 on: July 24, 2015, 10:04:39 am »
It worked perfectly!  :D :D :D
Also the fact that the OpenGL's zero is on the bottom...

There was another error: I was using the FrameBuffer's constructor with the GL10 parameter (the one for OpenGL ES 1.0/1.1)...