I'm having trouble to change my rendered mesh in my GLSurfaceView, and more precisely in the Renderer, I guess there is something I didn't understood in the workflow of the Renderer class.
So, let's consider we have this :
public class MyRenderer extends GLSurfaceView.Renderer{
private Mesh aMesh;
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config){
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
aMesh = new Mesh("TestMesh1");
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
...Creating viewport...
}
#Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(gl.GL_COLOR_BUFFER_BIT);
...all Maths to create mvpMatrix...
aMesh.draw(mvpMatrix);
}
}
So this is perfectly working, aMesh is display on the screen with all the good settings. Now, I want to change this mesh when the user is pressing a button, and to refresh my renderer I created a specific method in the renderer, as follow :
public class MyRenderer extends GLSurfaceView.Renderer{
private Mesh aMesh;
public void changeMesh(String newMeshName){
GLES20.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);
....
aMesh=new Mesh(newMeshName);
}
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config){
....
}
#Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
...
}
#Override
public void onDrawFrame(GL10 gl) {
aMesh.draw(mvpMatrix);
}
}
So in my mind, calling changeMesh would make onDrawFrame method to redraw aMesh with the new model, right?
My problem is that the result is an empty renderer. It's not even crashing or so. When I call changeMesh(), the previously well displayed mesh disappear (as expected), but the new one is not draw.
So I'm wondering, does it requires to create a new Renderer(which seems a little bit heavy)? Is there a way to ask a pass in OnSurfaceCreated manually? I'm a bit lost on this bug because it's kinda unexpected.
Part of the problem was the call to new, I couldn't explain exactly the reasons, but it seems it's somehow breaking links between OpenGL Objects and my buffers. I solved the problem with a kind of "setter" which just change all datas in an instance of Mesh to suit the datas of an other Mesh, this way I could save the memory address of the first instance.
It's a bit strange but well this solved my problem, maybe it can help someone else.
Related
I have a standard GLSurfaceView class:
public class TestSurfaceView extends GLSurfaceView {
public MainRenderer mRenderer;
public GStreamerSurfaceView(Context context) {
super(context);
setEGLContextClientVersion(2);
mRenderer = new MainRenderer(context);
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
I have a Renderer class that implements GLSurfaceView.Renderer:
public class MainRenderer implements GLSurfaceView.Renderer {
private int[] hTex;
private SurfaceTexture mSTexture;
private Context context;
MainRenderer(Context c) {
context = c;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
}
public void onDrawFrame(GL10 unused) {
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
}
public void onSurfaceCreated(GL10 arg0, javax.microedition.khronos.egl.EGLConfig arg1) {
}
}
In a separate JNI thread, I have some video data (YUV format) uploaded to an OpenGLES texture. I receive in Java the notification that a new texture is available and I have the corresponding texture id.
How can I display the content of this texture in the Renderer class with minimum performance impact?
For cases where the frames are coming from Camera or MediaCodec there are some very efficient solutions. It sounds like you're generating or decoding the video in software, though, which puts a mild spin on things (though you do have a SurfaceTexture declared in your code, which is odd). The trick is the "OpenGL ES texture" part, because the texture is associated with the EGL context, and the EGL context can only be active in one thread at a time.
Because you're using GLSurfaceView, rather than plain SurfaceView, you don't have control over the EGL context in the GLSurfaceView's renderer thread. The easiest way to work around this is to jump through some hoops to create a second EGL context that is shared with the first. Once you've done that, the texture created in the separate JNI thread will be available to the GLSurfaceView renderer thread.
You can find an example of this in Grafika's "show + capture camera" activity. If you look at the onDrawFrame() method in CameraCaptureActivity.java you can see it calling updateSharedContext(), which passes a message to the thread running TextureMovieEncoder to cause it to run handleUpdateSharedContext(), which (re-)creates the surface used to feed a video encoder.
If you use plain SurfaceView instead, and do your own EGL and thread management, the code will be less twisty. You can create both contexts at once, then just pass one to the thread that's producing images. You could also create a single context and use eglMakeCurrent() to shift it between threads, but that could be expensive on some platforms.
Update: the Grafika "show + capture camera" implementation has a race condition; see this bug report for details on the problem and solution. You have to perform some extra steps when creating a texture in one thread and using it in another. It's usually better to do everything in one context on one thread. The other activities in Grafika use a plain SurfaceView and do their own EGL context and thread management.
I searched the whole day and can still not figure out, what I am missing. All Examples I found are either incomplete (only not connected snippets) or overcomplete(cannt see what is really part if the principle)
I have an Activity that has a View that extends SurfaceView that should be filled using a native method. It is currently implemented by a memset(..,0,..) but my View is white although all calls seem fine.
MyView:
public class MyView extends SurfaceView implements SurfaceHolder.Callback
{
public MyView(Context context)
{
super(context);
SurfaceHolder sh = getHolder();
sh.addCallback(this);
}
// protected void onDraw(Canvas canvas) // works to make the view red
// { canvas.drawColor(Color.RED);}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
nativeRender(holder.getSurface(), width, height);
}
public void surfaceCreated(SurfaceHolder holder)
{}
public void surfaceDestroyed(SurfaceHolder holder)
{}
private native void nativeRender(Object surface, int width, int height);
}
Native method:
void ...nativeRender(JNIEnv* env, jobject myView, jobject surface, jint width, jint height)
{
ANativeWindow* pWindow(ANativeWindow_fromSurface(env, surface));
ANativeWindow_setBuffersGeometry(pWindow, width,height,WINDOW_FORMAT_RGBX_8888);
ANativeWindow_Buffer buffer;
if (ANativeWindow_lock(pWindow, &buffer, NULL) == 0)
{
memset(buffer.bits, 0, buffer.stride*buffer.height*4);
ANativeWindow_unlockAndPost(pWindow);
}
ANativeWindow_release(pWindow);
}
Things I checked:
return values of all ANativeWindow_* are 0.
buffer size/format is as expected
Things I tried:
calling nativeRender() from onDraw()
calling nativeRender() repeatetly (Handler(mainLooper(), postDelayed())
keeping ANativeWindow* as static variable (over multiple calls of the above)
same without the ANativeWindow_release(pWindow);
WINDOW_FORMAT_RGBX_8888 vs WINDOW_FORMAT_RGBA_8888
fill with patterns
So it looks to me that I fill a buffer of the correct dimensions that is never shown. Probably I' missing something obvious because nobody else seems to have problems in that way.
Thanks in Advance
Moritz
Thanks!
fadden's comment was the answer. My code actually called setBackgroundColor() which is obviously not what I wanted...
(Although I would expect that in this context this property would have no meaning or would be what the buffer would be prefilled with...)
I looked all over the net in order to find out if its possible to change the renderer of a GLSurfaceView on the flight. The reason is that I want to change the OpenGl program, and initiate all the attributes and unified params from its vertex and fragment shader and I don't want the any change would require to create a brand new GLSurfaceView with a brand new Renderer.
It seems like reasonable operation that should be doable.
Note: I haven't implemented the following.
GLSurfaceView.Renderer is an interface. Implement it three times. Twice for your different OpenGL renderers, and one time attached to the GLSurfaceView. The latter only dispatches to one of the former, and allows to change the renderer to which it dispatches. The code must hold a reference to this renderer, and eventually must be synchronized to the draw calls (though I don't know).
Be aware that you cannot easily switch OpenGLES context data. It is shared between all renderer instances.
class DispatchingRenderer implements GLSurfaceView.Renderer {
private class Renderer1 implements GLSurfaceView.Renderer {
...
}
private class Renderer2 implements GLSurfaceView.Renderer {
...
}
public DispatchingRenderer() {
this.r1 = new Renderer1();
this.r2 = new Renderer2();
this.currentRenderer = this.r1;
}
public void ToggleRenderer() {
if(this.currentRenderer == this.r1) {
this.currentRenderer = this.r2;
} else if (this.currentRenderer == this.r2) {
this.currentRenderer = this.r1;
}
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// do one-time setup
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
this.currentRenderer.onSurfaceChanged(gl, w, h);
}
public void onDrawFrame(GL10 gl) {
this.currentRenderer.onDrawFrame(gl);
}
}
The Setup : A RelativeLayout with a GLSurfaceView and a Button as shown in the image..
The Problem: Lets say I have other triangle models (The one in the picture being the initial model)... I wish to change the models cyclically on click of the button. Since button is on the UI thread and glSurfaceView runs on a separate thread, I don't exactly know how to pass the info/instruction to it. I know there is this thing called Handler in Android which might be useful in this case... But I need some help here..
Edit: If Handler is the right way, I need to know how to add Looper to that Handler... The documentation says add looper.prepare() at the start of run() method.. But the glSurfaceView creates thread implicitly, resulting in no run() method directly available..
I don't think it is necessary to use handlers to solve this issue but you may need to adjust the way you organise your classes.
Here is an example of an organisational structure that might solve your issue:
Activity Class
public class MainActivity extends Activity {
private int modelNumber = 0;
private ArrayList<Model> models = new ArrayList<Model>();
private YourRendererClass renderer;
#Override
public void onCreate(Bundle savedInstanceState) {
...
// Setup GLSurfaceView
GLSurfaceView surface = new GLSurfaceView(this);
setContentView(surface);
renderer = new YourRendererClass();
surface.setRenderer(renderer);
// Set up models
models.add(new Model(x, y, size etc..));
models.add(new Model(x, y, size etc..));
models.add(new Model(x, y, size etc..));
etc.
// Display first model
renderer.setCurrentModel(models.get(modelNumber));
...
}
// Called by the button press:
// Use android:onClick="onClick"
// in your layout xml file within button
public void onClick(View view){
// Make it loop round
modelNumber++;
if(modelNumber>=models.size()){
modelNumber=0;
}
// Display current model
renderer.setCurrentModel(models.get(modelNumber));
}
}
Renderer Class
public class YourRendererClass implements Renderer {
private Model currentModel;
#Override
public void onDrawFrame(GL10 gl) {
// ** Your existing set-up code **//
// Draw model
if (currentModel!=null){
currentModel.draw(gl);
}
}
public void setCurrentModel(Model model){
currentModel = model;
}
}
Model class
public class Model {
// Holds model information
private int size;
private int x;
private int y;
// etc...
public model(int x, int y, int size etc...){
this.x=x;
this.y=y;
this.size=size;
// etc....
}
public void draw(GL10 gl) {
// ** Draw model based on model information fields above **
}
}
The above code is untested as I don't have access to your drawing code but the structure should work if implemented correctly. I've tried to make it clear where you'll have to insert your own code to make it work. In particular I wasn't sure what defines each of your different models so you'll need to include sufficient local variables within the Model class to define them.
I hope my answer helps, let me know if you have any questions.
Tim
You should look at queueEvent! It's a very convenient way to pass informations from UI Thread to renderer Thread:
queueEvent(new Runnable(){
#Override
public void run() {
mRenderer.method();
}});
[EDIT] Ignore this post, it was a noob's mistake [/EDIT]
I started Android last week (= I'm pretty new to it :) ) and I'm banging my head on a problem:
I try to perfom a raycasting to get the objects under a point of the screen.
I found out that the GLU.gluUnproject method was what I needed, after a decent amount of failures, I found a solution
I've copied the MatrixGrabber, MatrixStack and MatrixTrackingGL classes in my project, they seem fine.
the method I use goes as follow:
static public Vertex unProject( float x, float y, World world )
{
world.mg = new MatrixGrabber();
world.mg.getCurrentState(world.gl);
float[] pos = new float[4];
GLU.gluUnProject( x, y, 0f,
world.mg.mModelView, 0,
world.mg.mProjection, 0,
world.view().get_size(), 0,
pos, 0);
return new Vertex(pos[0], pos[1], pos[2]);
}
Vertex is a dataHolder with x,y,z floats
World extends GLSurfaceView, I do the GLWrapper replacement in the constructor:
world.mg is a MatrixGrabber()
public World( Context context )
{
super(context);
[...]
//allows matrix manipulation
setGLWrapper( new GLWrapper()
{
public GL wrap(GL gl)
{
return new MatrixTrackingGL(gl);
}
});
}
and I make sure that all the variables are instanciated when I do my call
but still I can't get this to work: the app crashes badly on the
world.mg.getCurrentState(world.gl);
call.
it also crashes it on getCurrentModelView(gl); and getCurrentProjection(gl);
I'm using Android 1.5, but tried with other versions up to 3. same thing.
I don't really know which version of OpenGLI'm using ; the GL10 is used everywhere, I don't know if it is important, all I've read concerned the GL10 "Type".
if anyone has a clue, an advice, a workaround or a solution, I'd be happy happy happy
and anyway, thanks for reading :)
private void getMatrix(GL10 gl, int mode, float[] mat) {
MatrixTrackingGL gl2 = new MatrixTrackingGL(gl);
gl2.glMatrixMode(mode);
gl2.getMatrix(mat, 0);
}
replace the casting on the first line of the MatrixGrabber.java
my World class extended GLSurfaceView and was using the MatrixTrackingGL as a wrapper.
the problem was that the Viewport also extended GLSurfaceView and was NOT using the MatrixTrackingGL... stupid.
now the world doesn't extend anything and the Viewport ( extending GLSurfaceView ) implements the Wrapper's change and everything's fine.