[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [E-devel] Putting GL-textures into Etk, Evas, bla bla... How to choke a horse with words.



And again, it worked.

Took a little time before I figured out why the cube was flashing every time I lead the mouse over the button (which triggers an animation).
Got it though: Put this into the _flush_cb function:

   glDrawBuffer (GL_BACK);
   glReadBuffer (GL_BACK);


..Guess we are tricked by Evas' compositing routines.

Regards Rene







Simon TRENY wrote:
On Tue, 14 Nov 2006 10:42:55 +0100,
Rene Jensen <centipede@takhis.net> wrote :

Simon, thanks, it worked!

Next I'll see how well one can get away with mixing Etk with this.
With a little luck it is just a matter of calling glViewport and
glScissor before painting (and resetting them again afterwards of
course).

Here is a really simple code that shows how to make it work in
Etk. It's still heavy experimentation, really dirty and it has some
problems.
To make it work, you still need the Evas patch (the flush callback
thing) and the latest Etk to have the GL-X11 engine of Etk (I've just
committed it).

To compile it:
gcc -o etk_gl_proto etk_gl_proto.c `etk-config --libs --cflags` -Wall
-lGL -lGLU

and to execute it, you have to say to the app that you'd like to use
the GL-X11 engine, so the command is:
./etk_gl_proto --etk-engine=ecore_evas_gl_x11

It should work but it's a bit buggy (probably because I don't reset all
the GL states before drawing the cube, so sometimes blending is
enabled, sometimes scissor-test is enabled, ... it would be useful to
have a command to reset all the GL-states to their default value...)

I know this is a hack, but experimentation first, then we can start thinking about a clean solution that is OK with the lead developers.

A way to make it cleaner and more generic would be to do as Jose said:
render the OpenGL scene in a buffer, and render the buffer content in
an Evas image. This should even be compatible with all the engines, but
it may be a lot slower (I don't know, I never did something like that).

Simon


Regards
Rene



Simon TRENY wrote:
Here is a fixed version of the test code. It fixes the back-face
culling and some glPush/Pop*() bugs.

Simon


On Sun, 12 Nov 2006 18:21:09 +0100,
Simon TRENY <simon.treny@free.fr> wrote :

Hi Rene,

The main problem we have here is that what you'd like to do is
definitely engine-specific whereas Evas tends to abstract totally
the engine layer. Anyway, with some Evas engines it should be
quite easy to use OpenGL to render 3D stuff. Here is how to do it
with the GL-X11 engine of Evas (which is obviously the most
appropriate engine to do that since it already provides the GL
context).

So, with the GL-X11 engine of Evas, all you have to do basically
is to call all your OpenGL functions before glXSwapBuffers() gets
called. glXSwapBuffers() is called by the output_flush() method of
the GL-X11 engine, which is called by
evas_render_updates_internal(). So the problem is that we need to
be somehow notified before evas_render_updates_internal() calls
the output_flush() method, and, for now, there is no way to do
that. I have written a small patch for Evas to add a callback
called each time before output_flush() is called. The function to
set this callback is evas_flush_callback_set(). The Evas patch is
joined with this email (this patch should not be applied to the
CVS repository of Evas, it's a quick'n'dirty hack!!).

Now, all you have to do is to make the calls to the OpenGL
functions in the "flush-callback". I have attached a a small code
that shows how to do it. It's totally dirty and hacky but it works
quite well :) There is just a small problem with the Zbuffer of
with the backbface culling I guess (the cube looks weird...), but
it shouldn't be hard to fix. To compile this code, you have to
apply the patch to Evas, and to compile it with:
gcc -o evas_gl_proto evas_gl_proto.c `ecore-config --cflags --libs`
-lGL -lGLU -Wall


Note that the GL engine of Evas has some serious problems though.
Here is a list of the know problems written by raster some times
ago:
1. a font with glyphs > 256x256 pixels will just break.
2. images that are bigger than max texture size simply won't show
(need to build a mesh)
3. cards that don't support rectangular textures will end up with
nasty scaling results when scaling "stretched" or "squashed"
mostly in 1 dimension (mipmap issues basically when used for 2d)
4. scaling down even with smooth scaling is ugly (need to use
anisotropic filtering  if possible)
5. doesn't support destination alpha (easy-ish to fix)
6. need pbuffer support implemented for the future (we need to be
able to render to an intermediate buffer)
7. if the window is fullscreen glx may choose to do page flipping
not copy from back to front buffer. in this case evas's assumption
that the buffer was exactly as it left it when it last rendered is
wrong - as it actually is as it was rendered 2 frames ago, not the
last frame.
There is also a problem when you have several windows using the GL
engine in your program, but I think it's probably a problem with
Ecore_Evas_GL. The Glitz engine that raster seems to be writting
should fix most of these problems :)


Regards :)
Simon




On Sun, 12 Nov 2006 12:40:37 +0100,
Rene Jensen <centipede@takhis.net> wrote :

WARNING: LONG POST AHEAD...

(Sigh) After having posted on both IRC and the webforum, I get
the distinct feeling that this community has been worn down by
daft requests for getting fancy window drawing using OpenGL.

This is a serious shame because now even the smallest utterance of
that API makes it's author look guilty. The developers forget that
end-users asking for something XGL'ish is a far cry from
application developers asking for their favorite API for
graphics/GLGPU programming, which they need to make video/3D
applications using Ewl/Etk.

However, as an application developer, I feel that:

1) Etk/Edje is perfect for the job. The efficiency is the reason I
am choosing E if I can get away with it in the first place. I
need a strong and themable GUI. I *LOVE* Efl's well-organized
style!

2) The window manager and graphics lib should NOT squander my
precious GPU cycles on needless features. If I could make a 3D in
a console window, I'd do that.

3) An extra GL layer is actually another source of errors! Once I
made a small 3D paint-program that used GL for the painting
procedures, http://artcamilla.dk/centipede. I spend a LOT of time
tracking down a bug in Windows where I use Nvidias desktop
switcher. All that was drawn on my screen was a frame delayed.
Turned out that when I switched off the desktop switcher or even
turned off screen rotation (I have a tablet-pc), the bug
dissappeared. Wasn't in my program after all. Incidently, the
above mentioned program used my own OpenGL based mini-GUI. Guess
why I am looking for another GUI with a strong support of
themes ;-)

4) It will be hard to write any program at all in the video/3D
industry without having support for OpenGL.

--------------------------------

Ok, enough ranting. This is a verbatim copy of the forum post I
made few days ago. By the end of this mail, I have already
answered some of it:

--------------------------------

I've learned that OpenGL requests falls in two camps this place:
Those for a way to put Evas into OpenGL (for kicks), and those
for a way to put OpenGL into Evas/Etk (for making any kind of
serious multimedia program). This post belongs in the latter camp.

Without in any way claiming to be a proficient E programmer, I've
taken the liberty to scout for the possibility of drawing 3D in
ETK, since it appears to be a non-existing feature right now.
Hope I won't make too big a fool of myself ;-)

Basically all that is needed to be able to draw using opengl in X
is the creation of a GL context using GLX. The only thing
required is that you have a handle to an X drawable (Window or
Pixmap). I can see that in E getting this handle is less obvious,
since E is unaware of a specific render-backend. Opposite GTK
which has an X Window for each widget, ETK renders each element
directly to a buffer, and only this grand superbuffer has an
associated Window - if using some kind of X11 backend of course.
If I read the code correctly, the Evas software-X11 backend
actually renders to a pixbuf in shared-memory.

So far so good. I traced this Drawable to a function called
'eng_setup', which is handed an X Drawable (amongst other things).
Though I can't see where this Drawable is created since I don't
know who CALLS eng_setup, I guess it would still be possible to
get the super-drawable somehow.

So I guess that in order to get OpenGL support in ETK, three
things are needed:

1) Access the Drawable for the root Evas (if using X11/GLX) or a
Window (if using GDI/WGL on windows)

2) Knowledge about where in the root-evas the widget which on
which OpenGL drawing should occur is placed.

3) Cooperation from ETK: It shouldn't paint anything to update
that widget and it should make it possible to emit a "paint"
signal so the user can emit the proper set of OpenGL commands.

Getting 2. to work should be easy. Accessing the Drawable in 1 is
not at all impossible either, I guess. (save the Drawable in a
global var in some of the E libraries and get it through a
function. As to 3., it is more of a mystery exactly how, but
perhaps somebody sees the light more clearly.

Before I go on, I'd like your sentiments on this
request/suggestion. Presumable somebody will cry: "No way!  E has
nothing to do with OpenGL period! This would violate the API which
is totally architecture agnostic, you ignorant philistine", which
is a perfectly adult way of opening this interesting discussion.
Other people may ask why I am not simply using this ...XXX...
GL-widget. I certainly hope that happens ;-)


--------------------------------

And a few more thoughts (and solutions) I have since made:

1) I can see a problem if Evas uses some form of buffering. Then
you need to pump out the data from the GL context. This may not be
as slow as it sounds on modern architectures and if the updating
of the cache doesn't happen every time a single bit has been
changed.

2) A spin on the above. In case Evas buffers use that cache, it is
going to be hard to emit some kind of "paint" signal which will be
needed to draw opengl into the canvas. However I studied Evas'
code and I can see that the engine-module simply get a

    eng_image_draw (in modules/engines/gl_x11/evas_engine.c)

which converts into a

    evas_gl_common_image_draw (in
modules/engines/gl_common/evas_gl_image.c)

In the last function, the is evident that the Image being painted
is actually a GL texture. So a quick solution could be to use GL's
recent Render-To-Texture mechanism (a.k.a Framebuffers) to make
the Image being juggled with an actual 3D image rendered at users
will (as long as (s)he remembers to tell Evas that the "image" has
changed). Not the fastest solution, but not bad either. The
programmer is unburdened with "paint" calls, and Evas is free to
repaint and buffer as it sees fit.


Responding to my own statements earlier:

1) We already have access to the Drawable from the program's main function, which sets up the environment. I missed that because it
seems etk_init handles it. Thus etk_init needs to open up a bit.

2) If you use Render-To-Texture, you don't need anything from Etk
except access/modification possibility of it's GLuint that holds
the texture name

3) - ditto -


Caveat's:

1) You still need the GL CONTEXT that get's created. For some
reason this isn't dont in the main function, e.g.

    bin/evas_gl_x11_main.c

2) You need to be able to get the GLuint of an image.

3) This is inherently ENGINE SPECIFIC. No sense in asking for a texture-id from a cairo backend.


--------------------------------

And something completely different:

I have good experience with OpenGL and a small GDI/GL + X/GL cross-platform GUI behind me. I couldn't help noticing that your Win32/GDI engine files are almost empty. Just ask if you need any
tips. I might be able to help. I also have a somewhat deep
understanding of OpenGL Framebuffer Objects and all that "weird"
OpenGL stuff.

Oh, and don't use the GDI way of transferring buffered graphics
data to a window (Device Independent Bitmap). Slow like hell.
Either use a GL context (why not?) or DirectX. I don't know
anything about GDI+ or the new stuff from MS.



Ahem, I should stop talking.


Regards Centipede (Rene Jensen)


-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services,
security? Get stuff done quickly with pre-integrated technology to
make your job easier Download IBM WebSphere Application Server
v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
enlightenment-devel mailing list
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

------------------------------------------------------------------------

#include <stdlib.h>
#include <stdio.h>
#include <Evas.h>
#include <Ecore.h>
#include <Ecore_Evas.h>
#include <GL/gl.h>
#include <GL/glu.h>

#define WIDTH 800
#define HEIGHT 600

static int _animator_cb(void *data);
static void _flush_cb(Evas *e);

int main(int argc, char *argv[])
{
   Evas *e;
   Ecore_Evas *ee;
   Evas_Object *bg;
   Evas_Object *rect;
evas_init();
   ecore_init();
   ecore_evas_init();
ee = ecore_evas_gl_x11_new(NULL, 0, 0, 0, WIDTH, HEIGHT);
   ecore_evas_size_min_set(ee, WIDTH, HEIGHT);
   ecore_evas_size_max_set(ee, WIDTH, HEIGHT);
   e = ecore_evas_get(ee);
   evas_flush_callback_set(e, _flush_cb);
   ecore_evas_show(ee);
/* We create a black background rect because Evas doesn't
clear the color buffer */ bg = evas_object_rectangle_add(e);
   evas_object_color_set(bg, 0, 0, 0, 255);
   evas_object_move(bg, 0, 0);
   evas_object_resize(bg, WIDTH, HEIGHT);
   evas_object_show(bg);
/* The rectangle is here to show that Evas can render object
while OpenGL is also rendering */ rect =
evas_object_rectangle_add(e); evas_object_color_set(rect, 0, 200,
180, 255); evas_object_move(rect, 10, 10);
   evas_object_resize(rect, 30, 30);
   evas_object_show(rect);
/* By default, an animator is 30 times per second */
   ecore_animator_frametime_set(1.0 / 60.0);
   ecore_animator_add(_animator_cb, e);
   ecore_main_loop_begin();
ecore_evas_shutdown();
   ecore_shutdown();
   evas_shutdown();
return 0;
}

/* Forces Evas to redraw */
static int _animator_cb(void *data)
{
   Evas *e;
if (!(e = data))
      return 1;

   evas_damage_rectangle_add(e, 0, 0, WIDTH, HEIGHT);
   return 1;
}

/* This is called before glXSwapBuffers() gets called.
 * The calls to the GL API have to be placed here */
static void _flush_cb(Evas *e)
{
   static float rot = 0.0;
glPushAttrib(GL_ALL_ATTRIB_BITS); glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_LEQUAL);
   /* We have to disable the scissor test or sometimes it's not
rendered. Not sure why... */ glDisable(GL_SCISSOR_TEST);
   glEnable(GL_CULL_FACE);
   glCullFace(GL_BACK);
glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   glLoadIdentity();
   gluPerspective(45.0, (float)WIDTH / (float)HEIGHT, 0.1, 100.0);

   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
   glLoadIdentity();
glTranslatef(0.0, 0.0, -5.0);
   glRotatef(rot, 0.3, 0.6, 0.0);
   glBegin(GL_QUADS);
      /* Top of cube */
      glColor3f(0.0f, 1.0f, 0.0f);
      glVertex3f(1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(1.0f, 1.0f, 1.0f);
      /* Bottom of cube */
      glColor3f(1.0f, 0.5f, 0.0f);
      glVertex3f(1.0f, -1.0f, 1.0f);
      glVertex3f(-1.0f, -1.0f, 1.0f);
      glVertex3f(-1.0f, -1.0f, -1.0f);
      glVertex3f(1.0f, -1.0f, -1.0f);
      /* Front of cube */
      glColor3f(1.0f, 0.0f, 0.0f);
      glVertex3f(1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, -1.0f, 1.0f);
      glVertex3f(1.0f, -1.0f, 1.0f);
      /* Back of cube */
      glColor3f(1.0f, 1.0f, 0.0f);
      glVertex3f(-1.0f, 1.0f, -1.0f);
      glVertex3f(1.0f, 1.0f, -1.0f);
      glVertex3f(1.0f, -1.0f, -1.0f);
      glVertex3f(-1.0f, -1.0f, -1.0f);
      /* Right side of cube */
      glColor3f(1.0f, 0.0f, 1.0f);
      glVertex3f(1.0f, 1.0f, -1.0f);
      glVertex3f(1.0f, 1.0f, 1.0f);
      glVertex3f(1.0f, -1.0f, 1.0f);
      glVertex3f(1.0f, -1.0f, -1.0f);
      /* Left side of cube */
      glColor3f(0.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, -1.0f, -1.0f);
      glVertex3f(-1.0f, -1.0f, 1.0f);
   glEnd();
/* Restore the previous transform matrices */
   glPopAttrib();
   glMatrixMode(GL_PROJECTION);
   glPopMatrix();
   glMatrixMode(GL_MODELVIEW);
   glPopMatrix();
rot += 1.0;
}
------------------------------------------------------------------------

#include <Etk.h>
#include <GL/gl.h>
#include <GL/glu.h>

static void _opengl_view_realize_cb(Etk_Object *object, void *data);
static void _rotation_toggled_cb(Etk_Object *object, void *data);
static int _animator_cb(void *data);
static void _flush_cb(Evas *e);

static Etk_Widget *_opengl_view;
static Etk_Bool _rotate = ETK_TRUE;

int main(int argc, char *argv[])
{
   Etk_Widget *window;
   Etk_Widget *vbox;
   Etk_Widget *frame;
   Etk_Widget *button;
etk_init(&argc, &argv); window = etk_window_new();
   vbox = etk_vbox_new(ETK_FALSE, 0);
   etk_container_add(ETK_CONTAINER(window), vbox);
frame = etk_frame_new("OpenGL View");
   etk_box_append(ETK_BOX(vbox), frame, ETK_BOX_START, ETK_BOX_EXPAND_FILL, 0);
_opengl_view = etk_widget_new(ETK_WIDGET_TYPE, NULL);
   etk_widget_size_request_set(_opengl_view, 240, 320);
   etk_signal_connect("realize", ETK_OBJECT(_opengl_view), _opengl_view_realize_cb, NULL);
   etk_container_add(ETK_CONTAINER(frame), _opengl_view);
etk_box_append(ETK_BOX(vbox), etk_hseparator_new(), ETK_BOX_START, ETK_BOX_NONE, 0);
   button = etk_toggle_button_new_with_label("Stop Rotation");
   etk_signal_connect("toggled", ETK_OBJECT(button), _rotation_toggled_cb, NULL);
   etk_box_append(ETK_BOX(vbox), button, ETK_BOX_START, ETK_BOX_NONE, 0);
ecore_animator_frametime_set(1.0 / 60.0);
   ecore_animator_add(_animator_cb, NULL);
etk_widget_show_all(window);
   etk_main();
return 0;
}

/* Called when the OpenGL view is realized: we set the "flush callback" to the Evas */
static void _opengl_view_realize_cb(Etk_Object *object, void *data)
{
   Evas *e;
if (!(e = etk_widget_toplevel_evas_get(ETK_WIDGET(object))))
      return;
   evas_flush_callback_set(e, _flush_cb);
}

/* Called when the "Stop Rotation" button is toggled */
static void _rotation_toggled_cb(Etk_Object *object, void *data)
{
   _rotate = !etk_toggle_button_active_get(ETK_TOGGLE_BUTTON(object));
}

/* Forces Evas to redraw */
static int _animator_cb(void *data)
{
   Evas *e;
if (!(e = etk_widget_toplevel_evas_get(_opengl_view)))
      return 1;

   evas_damage_rectangle_add(e, 0, 0, 1000, 1000);
   return 1;
}

/* This is called before glXSwapBuffers() gets called.
 * The calls to the GL API have to be placed here */
static void _flush_cb(Evas *e)
{
   Etk_Geometry geometry;
   static float rot = 0.0;
etk_widget_geometry_get(_opengl_view, &geometry.x, &geometry.y, &geometry.w, &geometry.h); glPushAttrib(GL_ALL_ATTRIB_BITS); glViewport(geometry.x, geometry.y, geometry.w, geometry.h); glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_LEQUAL);
   /* We have to disable the scissor test or sometimes it's not rendered. Not sure why... */
   glDisable(GL_BLEND);
   glDisable(GL_SCISSOR_TEST);
   glEnable(GL_CULL_FACE);
   glCullFace(GL_BACK);
glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   glLoadIdentity();
   gluPerspective(45.0, (float)geometry.w / (float)geometry.h, 0.1, 100.0);

   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
   glLoadIdentity();
glTranslatef(0.0, 0.0, -5.0);
   glRotatef(rot, 0.3, 0.6, 0.0);
   glBegin(GL_QUADS);
      /* Top of cube */
      glColor3f(0.0f, 1.0f, 0.0f);
      glVertex3f(1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(1.0f, 1.0f, 1.0f);
      /* Bottom of cube */
      glColor3f(1.0f, 0.5f, 0.0f);
      glVertex3f(1.0f, -1.0f, 1.0f);
      glVertex3f(-1.0f, -1.0f, 1.0f);
      glVertex3f(-1.0f, -1.0f, -1.0f);
      glVertex3f(1.0f, -1.0f, -1.0f);
      /* Front of cube */
      glColor3f(1.0f, 0.0f, 0.0f);
      glVertex3f(1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, -1.0f, 1.0f);
      glVertex3f(1.0f, -1.0f, 1.0f);
      /* Back of cube */
      glColor3f(1.0f, 1.0f, 0.0f);
      glVertex3f(-1.0f, 1.0f, -1.0f);
      glVertex3f(1.0f, 1.0f, -1.0f);
      glVertex3f(1.0f, -1.0f, -1.0f);
      glVertex3f(-1.0f, -1.0f, -1.0f);
      /* Right side of cube */
      glColor3f(1.0f, 0.0f, 1.0f);
      glVertex3f(1.0f, 1.0f, -1.0f);
      glVertex3f(1.0f, 1.0f, 1.0f);
      glVertex3f(1.0f, -1.0f, 1.0f);
      glVertex3f(1.0f, -1.0f, -1.0f);
      /* Left side of cube */
      glColor3f(0.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, 1.0f, 1.0f);
      glVertex3f(-1.0f, 1.0f, -1.0f);
      glVertex3f(-1.0f, -1.0f, -1.0f);
      glVertex3f(-1.0f, -1.0f, 1.0f);
   glEnd();
/* Restore the previous transform matrices */
   glPopAttrib();
   glMatrixMode(GL_PROJECTION);
   glPopMatrix();
   glMatrixMode(GL_MODELVIEW);
   glPopMatrix();
if (_rotate)
      rot += 1.0;
}