www.jpct.net

jPCT-AE - a 3d engine for Android => Support => Topic started by: Darai on January 27, 2016, 02:39:41 pm

Title: More FrameBuffers in one program
Post by: Darai on January 27, 2016, 02:39:41 pm
Hello,

I am trying to test bliting to an existing texture. I know, there is a warning sign don't do it, but I wanted to test it and I found a strange behaviour:

I have the renderer's function onSurfaceChange, creating the world and it's objects. In this function, I create 2 frame buffers, one for the standard rendering and one for the rendering into the texture. BUT once I make a second FrameBuffer with the same GL10 the screen shrinks to the size of the new frame buffer no matter what. So, I should maybe call different GL10? Or how can I create more independent FrameBuffers?

Code: [Select]
FrameBuffer fb;
FrameBuffer toTex;

public void onSurfaceChanged(GL10 gl, int w, int h) {
fb = new FrameBuffer(gl, w, h);
toTex = new FrameBuffer(gl, 256, 256);
...
}

public void onDrawFrame(GL10 gl) {
...
fb.display;
}

And to the larger problem, I would like to blit a text to a texture. I see here 4 ways:
1) blit to screen every frame: Too slow and can't skew the text.
2) create an object with 2 Triangle Sprite for every letter: Fine, but creates a complex Object3D
3) blit to bitmap using Canvas and replace texture in TextureManager: Unrecomended since texture replace(== upload a new texture to GPU), Canvas and Bitmap object is (i expect) much slower than Texture and FrameBuffer operations.
4) blit directly to texture using FrameBuffer.setRenderTarget(): This is unrecomended in the documentation by Egon but I don't know why.

So my question to the larger problem is: Why is the 4th approach not recomended since from this point of view it is probably the fastest one. (For a text which will not change every frame) And what would you pick and why?

Thanks for all advices.
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on January 27, 2016, 06:21:51 pm
You can't use multiple FrameBuffer instances on the same GL context. The FrameBuffer contains a GLRenderer which itself is bound to the context and manages the GL states. If you have two buffers, you have two renderers and because one doesn't know the managed states of the other, you will end up with everything being screwed up. With that out of the way, on to your actual question...

...4) is the way to go. The docs state that it's not recommended and if you've already tried it, you should know the reason: It doesn't work... ;) Except that it actually does...for my RPG's quest book, I'm doing exactly the same thing (Old screen shot: http://jpct.de/jpctnet/img/rpg_and57.png (http://jpct.de/jpctnet/img/rpg_and57.png)). But here comes the catch: You have to blit with modified coordinates and jPCT doesn't do this modification for you...because of...reasons...that I can't remember...anyway...what you have to do is this:

A blit like this:
Code: [Select]
buffer.blit(texture, left, top, destX, destY, width, height, destWidth, destHeight, transparency, additive, color);

has to use modified coordinates like this:

Code: [Select]
destX = convertX(buffer, destX);
destY = convertY(buffer, cm.getHeight() - destY);
destWidth = convertX(buffer, destWidth);
destHeight = -convertY(buffer, destHeight);

buffer.blit(texture, left, top, destX, destY, width, height, destWidth, destHeight, transparency, additive, color);

with convertX(), convertY() and getHeigth() being (with target being the texture that is used as a render target):

Code: [Select]
public int convertX(FrameBuffer buffer, int x) {
return (int) ((float) x * ((float) buffer.getWidth() / (float) target.getWidth()));
}

public int convertY(FrameBuffer buffer, int y) {
return (int) ((float) y * ((float) buffer.getHeight() / (float) target.getHeight()));
}

public int getHeight() {
return target.getHeight();
}

And you might want to add a call to

Code: [Select]
buffer.sync();

before starting the actual render to texture process, because it makes it more compatible with some strange devices.
Title: Re: More FrameBuffers in one program
Post by: Darai on January 28, 2016, 10:38:53 am
Hello Egon,

Thanks for the quick answer, I love the screenshot you posted.

To summarize this topic:

More FrameBuffers is definitley nogo, since there is only one GLcontext. That means that I have to use the one FrameBuffer I have (?) which means to use the process described in the thread http://www.jpct.net/forum2/index.php/topic,3716.0.html (http://www.jpct.net/forum2/index.php/topic,3716.0.html).

The meaning is to temporary (just for the proces of remaking the texture) use the setRenderTarget() and then end with removeRenderTarget() to redirect the main Buffer.

AND:
You suggest to scale the blit according the texture size viz the code snippet you wrote (and reverse its Y axis) and SYNC the Buffer before (?) the manipulation with the texture.

If I misunderstood you, just say so please. :-)
Thanks once again, I am going to try it out.
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on January 28, 2016, 11:19:49 am
Yes, that's basically it.
Title: Re: More FrameBuffers in one program
Post by: Darai on January 28, 2016, 02:01:27 pm
Hmm,

ok, I am still doing something wrong... I know, that now I moved to the wider topic (blitting to a texture) but I hope you don't mind to finish it.

I rewrote the problem into the following simple example. It is all inside the onSurfaceChanged() method. I have 2 squares, the left one with the original texture and the right one, where I would expect a BLUE texture, or copy of the right-square texture. But no, I have still the dull black empty texture, so what am I missing? And what is more, during the fb.display() it anounces a warning: "<core_glCopyTexSubImage2D:318>: GL_INVALID_OPERATION"

Thanks again for your help.

Code: [Select]
public void onSurfaceChanged(GL10 gl, int w, int h) {
if(master != null){return;}
master = HelloWorld.this;

if (fb != null) {
fb.dispose();
}

fb = new FrameBuffer(gl, w, h);
world = new World();
world.setAmbientLight(150, 150, 150);
sun = new Light(world);
sun.setIntensity(100, 100, 100);
SimpleVector sv = new SimpleVector(0, -50, -80);
sun.setPosition(sv);
try {
tx1 = new Texture(HelloWorld.getApp().getAssets().open("Texture.png"), true);
TextureManager.getInstance().addTexture("tx1", tx1);
} catch (IOException e) {
e.printStackTrace();
}
tx2 = new Texture(256,256);
TextureManager.getInstance().addTexture("tx2", tx2);
int tx1ID = TextureManager.getInstance().getTextureID("tx1");
int tx2ID = TextureManager.getInstance().getTextureID("tx2");

int destX = convertX(fb, 0);
int destY = convertY(fb, tx2.getHeight() - 0);
int destWidth = convertX(fb, 256);
int destHeight = -convertY(fb, 256);

fb.setRenderTarget(tx2);
fb.sync();
fb.clear(RGBColor.BLUE);
fb.blit(tx1, 0, 0, destX, destY, 512, 512, destWidth, destHeight, 255, false);
fb.display();
fb.removeRenderTarget();

camera = world.getCamera();
camera.setPosition(0, 0, -50);
camera.lookAt(SimpleVector.ORIGIN);

try {
obj1 = new Object3D(2);
obj1.addTriangle(new SimpleVector(-1f,-1f,0), 0, 0, new SimpleVector(-1f,1f,0), 0, 1f, new SimpleVector(1f,1f,0), 1f, 1f, tx1ID);
obj1.addTriangle(new SimpleVector(-1f,-1f,0), 0, 0, new SimpleVector(1f,1f,0), 1f, 1f, new SimpleVector(1f,-1f,0), 1f, 0, tx1ID);
obj1.scale(10f);
world.addObject(obj1);

obj1.translate(-15f, 0f, 0f);

obj2 = new Object3D(2);
obj2.addTriangle(new SimpleVector(-1f,-1f,0), 0, 0, new SimpleVector(-1f,1f,0), 0, 1f, new SimpleVector(1f,1f,0), 1f, 1f, tx2ID);
obj2.addTriangle(new SimpleVector(-1f,-1f,0), 0, 0, new SimpleVector(1f,1f,0), 1f, 1f, new SimpleVector(1f,-1f,0), 1f, 0, tx2ID);
obj2.scale(10f);
world.addObject(obj2);

obj2.translate(15f, 0f, 0f);
} catch (Exception e) {
e.printStackTrace();
}
MemoryHelper.compact();
}
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on January 28, 2016, 02:17:56 pm
You can't do that in onSurfaceChanged(), you have to do it in onDraw(), because only onDraw() actually...draws... ;)
Title: Re: More FrameBuffers in one program
Post by: Darai on January 28, 2016, 02:47:08 pm
Still no success,

The renderer requires the onDrawFrame(GL10 gl) method, so I am using this for the normal onDraw stuff. I tried to add the texture alternating code to the beginning of this onDrawFrame() and also to the isolated onDraw() method, but without success. So again, I expect that I am something missing, but what? obj2.build(false)? obj2.touch()? I tried those with the same result.

Code: [Select]
public void onDrawFrame(GL10 gl) {
if(!textureChanged){
textureChanged = true;

int destX = convertX(fb, 0);
int destY = convertY(fb, tx2.getHeight() - 0);
int destWidth = convertX(fb, 256);
int destHeight = -convertY(fb, 256);

fb.setRenderTarget(tx2);
fb.sync();
fb.clear(RGBColor.BLUE);
fb.blit(tx1, 0, 0, destX, destY, 512, 512, destWidth, destHeight, 255, false);
fb.display();
fb.removeRenderTarget();
}
fb.clear(RGBColor.BLUE);
world.renderScene(fb);
world.draw(fb);
fb.display();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on January 28, 2016, 02:56:19 pm
onDrawFrame() is correct, my fault...

Make sure that you are using OpenGL ES 2.0 (http://www.jpct.net/wiki/index.php?title=OpenGL_ES_2.0_support (http://www.jpct.net/wiki/index.php?title=OpenGL_ES_2.0_support)). Render to texture on OpenGL ES 1.x usually doesn't work too well...
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on January 28, 2016, 02:58:43 pm
Also, change this

Code: [Select]
fb.blit(tx1, 0, 0, destX, destY, 512, 512, destWidth, destHeight, 255, false);
fb.display();

to this

Code: [Select]
fb.blit(tx1, 0, 0, destX, destY, 512, 512, destWidth, destHeight, 255, false);
emptyBlitWorld.renderScene(fb);
emptyBlitWorld.draw(fb);
fb.display();

with emptyBlitWorld being an additional world instance with no further content.
Title: Re: More FrameBuffers in one program
Post by: Darai on January 28, 2016, 03:29:51 pm
Finally, it is alive!!  :)

Thank you. So, to summarize it (how to blit into a texture):
And that is that... thanks again for all your help.
Title: Re: More FrameBuffers in one program
Post by: Darai on January 28, 2016, 04:32:25 pm
One small addition, sorry :)

possibly a bug report, but first evaluate this by yourself, maybe you allready fixed this in the new beta version:
The scaling of the blitting to the texture is wrong. Even the convert function is not entirely helping. The image on the new texture was suspiciously scaled in a simmilar ratio as the screen (== the buffer) was, so I was experimenting and it turned out that if you want to blit one texture to another one using FrameBuffer fb, the variables you need are not:
Code: [Select]
int destX = 0;
int destY = -fb.getHeight();
int destWidth = fb.getWidth();
int destHeight = fb.getHeight();
as you suggested but actually:
Code: [Select]
int destX = 0;
int destY = (int)((float)(fb.getWidth()+fb.getHeight())/2f);
int destWidth = fb.getWidth();
int destHeight = -fb.getWidth();
Including the strange int destHeight = fb.getWidth();, that is not an error in my transcription I checked it and rechecked it it really works this way, check it please too if it is only my bug, and I should download your beta or if it is really still there.
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on January 28, 2016, 05:19:31 pm
I'm not sure what the issue is here...I'm doing it exactly the way that I posted and it works just fine. I even created a quick and dirty test case for it using the same approach and it works just fine as well.
Here it is for reference:

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

import java.lang.reflect.Field;

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

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Logger;
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.BitmapHelper;
import com.threed.jpct.util.MemoryHelper;

/**
 * @author EgonOlsen
 *
 */
public class HelloWorld extends Activity {

// Used to handle pause and resume...
private static HelloWorld master = null;

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);

private float touchTurn = 0;
private float touchTurnUp = 0;

private float xpos = -1;
private float ypos = -1;

private Object3D cube = null;
private int fps = 0;
private boolean gl2 = true;

private Light sun = null;

private Texture target = null;
private Texture texture = null;
private World dummyWorld = new World();

protected void onCreate(Bundle savedInstanceState) {

Logger.log("onCreate");

if (master != null) {
copy(master);
}

super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

if (gl2) {
mGLView.setEGLContextClientVersion(2);
} else {
mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});

}

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();
}

private void copy(Object src) {
try {
Logger.log("Copying data from master Activity!");
Field[] fs = src.getClass().getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
f.set(this, f.get(src));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}

if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;

xpos = me.getX();
ypos = me.getY();

touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}

try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}

return super.onTouchEvent(me);
}

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) {
if (fb != null) {
fb.dispose();
}

if (gl2) {
fb = new FrameBuffer(w, h); // OpenGL ES 2.0 constructor
} else {
fb = new FrameBuffer(gl, w, h); // OpenGL ES 1.x constructor
}

Config.viewportOffsetY = -0.15f;

if (master == null) {

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

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

// Create a texture out of the icon...:-)
texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(getResources().getDrawable(R.drawable.icon)), 64, 64));
TextureManager.getInstance().addTexture("texture", texture);

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

target = new Texture(256, 256);

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();

if (master == null) {
Logger.log("Saving master Activity!");
master = HelloWorld.this;
}
}
}

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

public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
cube.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
cube.rotateX(touchTurnUp);
touchTurnUp = 0;
}

// Snip
fb.setRenderTarget(target);
fb.sync();
int x = convertX(fb, target, 0);
int y = convertY(fb, target, target.getHeight() - 0);
int width = convertX(fb, target, target.getWidth());
int height = -convertY(fb, target, target.getHeight());
fb.blit(texture, 0, 0, x, y, 64, 64, width, height, -1, false, null);
dummyWorld.renderScene(fb);
dummyWorld.draw(fb);
fb.display();
fb.removeRenderTarget();
// Snap

fb.clear(back);
world.renderScene(fb);
world.draw(fb);
world.getCamera().moveCamera(Camera.CAMERA_MOVEOUT, 0.1f);
world.getCamera().rotateCameraZ(0.01f);

// And blit the result
fb.blit(target, 0, 0, 10, 10, 256, 256, 400, 400, -1, false, null);

fb.display();

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

public int convertX(FrameBuffer buffer, Texture target, int x) {
return (int) ((float) x * ((float) buffer.getWidth() / (float) target.getWidth()));
}

public int convertY(FrameBuffer buffer, Texture target, int y) {
return (int) ((float) y * ((float) buffer.getHeight() / (float) target.getHeight()));
}

public int getHeight(Texture target) {
return target.getHeight();
}
}
}


Make sure that these dummy world rendering calls come between the blit()- and the display()-call. I know that this sucks and I could explain the reasons...but I'm too lazy... ;)
Title: Re: More FrameBuffers in one program
Post by: Darai on January 29, 2016, 09:58:20 am
I have it,

I was damaging your code up until it did the same thing I was experiencing in my code. The important difference is, that I was doing the new texture rendering only on the first onSurfaceDraw() using this code (I added the firstRun if condition). So that is the problem. On the first onDrawFrame() run, the blit is acting differently.  ;D
Code: [Select]
public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
cube.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
cube.rotateX(touchTurnUp);
touchTurnUp = 0;
}
if(firstRun){
// Snip
fb.setRenderTarget(target);
fb.sync();
int x = convertX(fb, target, 0);
int y = convertY(fb, target, target.getHeight() - 0);
int width = convertX(fb, target, target.getWidth());
int height = -convertY(fb, target, target.getHeight());
fb.blit(texture, 0, 0, x, y, texture.getWidth(), texture.getHeight(), width, height, -1, false, null);
dummyWorld.renderScene(fb);
dummyWorld.draw(fb);
fb.display();
fb.removeRenderTarget();
firstRun = false;
}
// Snap

fb.clear(back);
world.renderScene(fb);
world.draw(fb);
world.getCamera().moveCamera(Camera.CAMERA_MOVEOUT, 0.1f);
world.getCamera().rotateCameraZ(0.01f);

// And blit the result
fb.blit(target, 0, 0, 10, 10, 256, 256, 400, 400, -1, false, null);

fb.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
}
Title: Now for two textures
Post by: Darai on February 04, 2016, 01:49:35 pm
Hello, Egon,

I am sorry for pulling this out so many times, but I really need to solve this to know if I can use the feature or abandon this approach and do something else...

What I did:
And the result is strange, twisted again. I am printing here the example code... it is really only that your code with added second texture to plot to. Can you give me a hint what I did wrong? Thanks.
Code: [Select]
package com.threed.jpct.example;

import java.lang.reflect.Field;

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

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Logger;
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.BitmapHelper;
import com.threed.jpct.util.MemoryHelper;

/**
 * @author EgonOlsen
 *
 */
public class HelloWorld extends Activity {

// Used to handle pause and resume...
private static HelloWorld master = null;

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);

private float touchTurn = 0;
private float touchTurnUp = 0;

private float xpos = -1;
private float ypos = -1;

private Object3D cube = null;
private int fps = 0;
private boolean gl2 = true;
private boolean first = true;

private Light sun = null;

private Texture target = null;
private Texture target2 = null;
private Texture texture = null;
private World dummyWorld = new World();

private boolean firstRun = true;

protected void onCreate(Bundle savedInstanceState) {

Logger.log("onCreate");

if (master != null) {
copy(master);
}

super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

if (gl2) {
mGLView.setEGLContextClientVersion(2);
} else {
mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});

}

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();
System.exit(0);
}

private void copy(Object src) {
try {
Logger.log("Copying data from master Activity!");
Field[] fs = src.getClass().getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
f.set(this, f.get(src));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}

if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;

xpos = me.getX();
ypos = me.getY();

touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}

try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}

return super.onTouchEvent(me);
}

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) {
if (fb != null) {
fb.dispose();
}

if (gl2) {
fb = new FrameBuffer(w, h); // OpenGL ES 2.0 constructor
} else {
fb = new FrameBuffer(gl, w, h); // OpenGL ES 1.x constructor
}

Config.viewportOffsetY = -0.15f;

if (master == null) {

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

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

// Create a texture out of the icon...:-)
texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(getResources().getDrawable(R.drawable.icon)), 64, 64));
TextureManager.getInstance().addTexture("texture", texture);

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

target = new Texture(256, 256);
TextureManager.getInstance().addTexture("target", target);

target2 = new Texture(256, 256);
TextureManager.getInstance().addTexture("target2", target2);

world.addObject(cube);
cube.translate(15f, 0f, 0f);

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();

if (master == null) {
Logger.log("Saving master Activity!");
master = HelloWorld.this;
}
}
}

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

public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
cube.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
cube.rotateX(touchTurnUp);
touchTurnUp = 0;
}
if(firstRun){
if(first){
first = false;
}else{
// Snip on first
fb.setRenderTarget(target);
fb.sync();
int x = convertX(fb, target, 0);
int y = convertY(fb, target, target.getHeight() - 0);
int width = convertX(fb, target, target.getWidth());
int height = -convertY(fb, target, target.getHeight());
fb.blit(texture, 0, 0, x, y, texture.getWidth(), texture.getHeight(), width, height, -1, false, null);
dummyWorld.renderScene(fb);
dummyWorld.draw(fb);
fb.display();
fb.removeRenderTarget();
// Snip on second
fb.setRenderTarget(target2);
fb.sync();
x = convertX(fb, target2, 0);
y = convertY(fb, target2, target2.getHeight() - 0);
width = convertX(fb, target2, target2.getWidth());
height = -convertY(fb, target2, target2.getHeight());
fb.blit(texture, 0, 0, x, y, texture.getWidth(), texture.getHeight(), width, height, -1, false, null);
dummyWorld.renderScene(fb);
dummyWorld.draw(fb);
fb.display();
fb.removeRenderTarget();
firstRun = false;
}
}
// Snap

fb.clear(back);
world.renderScene(fb);
world.draw(fb);
world.getCamera().moveCamera(Camera.CAMERA_MOVEOUT, 0.1f);
world.getCamera().rotateCameraZ(0.01f);

// And blit the result
fb.blit(target, 0, 0, 10, 10, 256, 256, 400, 400, -1, false, null);
fb.blit(target2, 0, 0, 10, 510, 256, 256, 400, 400, -1, false, null);

fb.display();

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

public int convertX(FrameBuffer buffer, Texture target, int x) {
return (int) ((float) x * ((float) buffer.getWidth() / (float) target.getWidth()));
}

public int convertY(FrameBuffer buffer, Texture target, int y) {
return (int) ((float) y * ((float) buffer.getHeight() / (float) target.getHeight()));
}
}
}
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on February 04, 2016, 01:59:11 pm
Should actually work and looks fine to me code wise, at least at first glance. What exactly does "twisted again" mean in this context? Screen shot?
Title: Re: More FrameBuffers in one program
Post by: Darai on February 04, 2016, 02:07:54 pm
Sure, here is the screenshot (attachment):
If attachment is a bad approach, let me know and I will upload it to ImageShack
I admit, twisted is not a good description... so let's say cutted and zoomed.
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on February 04, 2016, 02:11:29 pm
But obviously only the second one? I'll look into it.
Title: Re: More FrameBuffers in one program
Post by: Darai on February 04, 2016, 02:19:49 pm
And third and fourth... All but the first one are wrong...

This examle has only two objects because I wanted it to be simple, but I foud this problem on a more complex  implementation and I backtracked it here, so I can tell you also this one additional information.  :)

Interrestingly also in every onSurfaceChanged() the first texture you will blit-in will be done succesfully no matter how many times you will blit into it. But once you will try to blit to one and than switch and blit to another one... the second e.t.c. will be wrong. And no only in the first frame or second, but in every frame you will try it in.
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on February 04, 2016, 02:47:33 pm
I'm actually doing the same thing and it works fine. I'll look into it...
Title: Re: More FrameBuffers in one program
Post by: EgonOlsen on February 04, 2016, 04:54:33 pm
Ok, I think I got it now. It always confuses me as well...sorry about that. The trick is to make the blitting operations operate on the actual framebuffer size (even when using a render target) and NOT on the ones of the texture. One solution is this dummyWorld-approach, but it wasn't the right place where I putted it. It has to be done BEFORE setting the render target and it's sufficient to do it once. But a better solution is to tell the framebuffer which values to use directly. There's a method for this: http://www.jpct.net/jpct-ae/doc/com/threed/jpct/FrameBuffer.html#setVirtualDimensions(int, int) (http://www.jpct.net/jpct-ae/doc/com/threed/jpct/FrameBuffer.html#setVirtualDimensions(int, int)).

So here's the modified example. If you don't want to use this method, you can still use the dummyWorld-approach: Just replace the call to setVirtualDimensions() with the renderScene()/draw()-sequence.

Hope this helps.

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

import java.lang.reflect.Field;

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

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

import com.threed.jpct.Camera;
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Logger;
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.BitmapHelper;
import com.threed.jpct.util.MemoryHelper;

public class HelloWorld extends Activity {

private static HelloWorld master = null;

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private RGBColor back = new RGBColor(50, 50, 100);

private float touchTurn = 0;
private float touchTurnUp = 0;

private float xpos = -1;
private float ypos = -1;

private Object3D cube = null;
private int fps = 0;
private boolean gl2 = true;

private Light sun = null;

private Texture target = null;
private Texture target2 = null;
private Texture texture = null;
private boolean firstRun = true;

protected void onCreate(Bundle savedInstanceState) {

Logger.log("onCreate");

if (master != null) {
copy(master);
}

super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

if (gl2) {
mGLView.setEGLContextClientVersion(2);
} else {
mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});

}

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();
}

private void copy(Object src) {
try {
Logger.log("Copying data from master Activity!");
Field[] fs = src.getClass().getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
f.set(this, f.get(src));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public boolean onTouchEvent(MotionEvent me) {

if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
return true;
}

if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
return true;
}

if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;

xpos = me.getX();
ypos = me.getY();

touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
return true;
}

try {
Thread.sleep(15);
} catch (Exception e) {
// No need for this...
}

return super.onTouchEvent(me);
}

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) {
if (fb != null) {
fb.dispose();
}

if (gl2) {
fb = new FrameBuffer(w, h); // OpenGL ES 2.0 constructor
} else {
fb = new FrameBuffer(gl, w, h); // OpenGL ES 1.x constructor
}

Config.viewportOffsetY = -0.15f;

if (master == null) {

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

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

// Create a texture out of the icon...:-)
texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(getResources().getDrawable(R.drawable.icon)), 64, 64));
TextureManager.getInstance().addTexture("texture", texture);

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

target = new Texture(256, 256);
target2 = new Texture(256, 256);

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();

if (master == null) {
Logger.log("Saving master Activity!");
master = HelloWorld.this;
}
}
}

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

public void onDrawFrame(GL10 gl) {
if (touchTurn != 0) {
cube.rotateY(touchTurn);
touchTurn = 0;
}

if (touchTurnUp != 0) {
cube.rotateX(touchTurnUp);
touchTurnUp = 0;
}

// Snip
// First

if (firstRun) {
fb.setVirtualDimensions(fb.getWidth(), fb.getHeight());

fb.sync();
fb.setRenderTarget(target);
int x = convertX(fb, target, 0);
int y = convertY(fb, target, target.getHeight() - 0);
int width = convertX(fb, target, target.getWidth());
int height = -convertY(fb, target, target.getHeight());
fb.blit(texture, 0, 0, x, y, 64, 64, width, height, -1, false, null);
fb.display();
fb.removeRenderTarget();

// second
fb.setRenderTarget(target2);
x = convertX(fb, target2, 0);
y = convertY(fb, target2, target2.getHeight() - 0);
width = convertX(fb, target2, target2.getWidth());
height = -convertY(fb, target2, target2.getHeight());
fb.blit(texture, 0, 0, x, y, 64, 64, width, height, -1, false, null);
fb.display();
fb.removeRenderTarget();

firstRun=false;
}
// Snap

fb.clear(back);
world.renderScene(fb);
world.draw(fb);
world.getCamera().moveCamera(Camera.CAMERA_MOVEOUT, 0.1f);
world.getCamera().rotateCameraZ(0.01f);

// And blit the result
fb.blit(target, 0, 0, 10, 10, 256, 256, 400, 400, -1, false, null);
fb.blit(target2, 0, 0, 10, 410, 256, 256, 400, 400, -1, false, null);

fb.display();

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

public int convertX(FrameBuffer buffer, Texture target, int x) {
return (int) ((float) x * ((float) buffer.getWidth() / (float) target.getWidth()));
}

public int convertY(FrameBuffer buffer, Texture target, int y) {
return (int) ((float) y * ((float) buffer.getHeight() / (float) target.getHeight()));
}

public int getHeight(Texture target) {
return target.getHeight();
}
}
}

This whole issue is caused by a design flaw in the way in which jPCT as well as jPCT-AE calculates the actual blitting coordinates. It needs the current camera to do this, but neither FrameBuffer itself nor the blit-method have it, so it does some crude things internally to deal with it and when using a render target, this goes wrong if you don't offer it some support. I should really revise this somehow...sometime...