MoSync example using OpenGL ES

10 posts / 0 new
Last post
Chris Hughes
qlmhuge's picture
Offline
Mobile Archmage
Joined: 11 Jan 2010
Posts:
MoSync example using OpenGL ES

A simple beginners app showing how to use OpenGL for embedded systems (GLES) in your MoSync application.

The application is shown here running in the Android emulator and should work fine on all Android devices.

A full OpenGL library is under development. For now, openGL is reached through IOCTL calls. You will need to download and install the latest MoSync nightly build to work with it. OpenGL is also not yet implemented in MoRE (the MoSync emulator) but will be Real Soon Now.

The complete HelloOpenGLES project is attached at the bottom of this post as zip file. Just unzip it, import it into your workspace, and add MAUtil.lib to the list of libraries for the project (Properties > MoSync Project > Build Settings > Paths and Files > Additional Libraries).

helloopengles.cpp

/* Copyright (C) 2011 MoSync AB

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2, as published by
the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/

/** @file helloopengles.cpp
*
* This application provides a very basic example of how to use OpenGL for
* Embedded Systems (GLES) in your MoSync application. It creates a spinning
* box on the screen with 3D shading. The code is very well commented
* o that you can see exactly what's happening at each step.
* The application makes use of MoSync's Moblet framework to handle events.
*
* @author Mattias Frånberg and Chris Hughes
*/

//Include the header files for MoSync Moblets and OpenGL so that we can
//access their libraries from our application.
#include <MAUtil/Moblet.h>
#include <GLES/gl.h>

//Include NativeUI so that we can create an OpenGL view widget.
#include <IX_WIDGET.h>

//Include MoSync syscall collection.
#include <maapi.h>

//Include our project's widget utilities. These utilities simplify
//getting and setting widget properties.
#include "widgetutil.h"

//Include our resources (in this case a texture to be used as the
//surface of our box).
#include "MAHeaders.h"

//Standard OpenGL code for setting up a perspective projection matrix.
void gluPerspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
{
 GLfloat xmin, xmax, ymin, ymax;

 const float M_PI = 3.14159;

   ymax = zNear * tan(fovy * M_PI / 360.0);
   ymin = -ymax;
   xmin = ymin * aspect;
   xmax = ymax * aspect;

   glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar);
}

//Standard OpenGL code for initialization.
void initGL(GLvoid)
{
    /* Enable Texture Mapping */
    glEnable(GL_TEXTURE_2D);

    /* Enable smooth shading */
    glShadeModel(GL_SMOOTH);

    /* Set the background black */
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);

    /* Depth buffer setup */
    glClearDepthf(1.0f);

    /* Enables Depth Testing */
    glEnable(GL_DEPTH_TEST);

    /* The Type Of Depth Test To Do */
    glDepthFunc(GL_LEQUAL);

    /* Really Nice Perspective Calculations */
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}

//Standard OpenGL code for setting camera angle.
void resizeWindow(int width, int height)
{
    /* Height / width ration */
    GLfloat ratio;

    /* Protect against a divide by zero */
    if (height==0)
    {
        height = 1;
    }

    ratio=(GLfloat)width/(GLfloat)height;

    /* Setup our view port. */
    glViewport(0, 0, (GLint)width, (GLint)height);

    /*
     * Change to the projection matrix and set
     * our viewing volume.
     */
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    /* Set our perspective */
    gluPerspective(45.0f, ratio, 0.1f, 100.0f);

    /* Make sure we're changing the model view and not the projection */
    glMatrixMode(GL_MODELVIEW);

    /* Reset The View */
    glLoadIdentity();
}

//Create the wrapper for the entire application. It is here that we will manage
//the application and handle events.
class MyMoblet : public MAUtil::Moblet, MAUtil::TimerListener {

//Define our new class's public methods.
public:

	//First, the constructor.
	MyMoblet()
	: mGlViewInitialized( false ),
	  mXRotation( 0.0f ),
	  mYRotation( 0.0f ),
	  mZRotation( 0.0f )
	{
		//Create a screen using NativeUI. Create an OpenGL widget within that
		//screen. Set the widget's width and height: -1 means the whole
		//screen. Make the widget a child of the screen, then show it.
		int screen = maWidgetCreate( "Screen" );
		mGLView = maWidgetCreate( "GLView" );
		maWidgetSetPropertyInt( mGLView, "width", -1 );
		maWidgetSetPropertyInt( mGLView, "height", -1 );
		maWidgetAddChild( screen, mGLView );
		maWidgetScreenShow( screen );

		//Make the moblet listen to custom events, so that we can know when our
		//GLView widget is ready to be drawn.
		MAUtil::Environment::getEnvironment( ).addCustomEventListener( this );

		//Make the moblet draw every 50 milliseconds.
		MAUtil::Environment::getEnvironment( ).addTimer( this, 50, -1 );
	}

	//Next, a method for detecting key presses. This is a method we have
	//inherited from the Moblet base class, and here we will override that
	//method with some processing of our own.
	void keyPressEvent(int keyCode, int nativeCode) {
		//Close the application if key 0 or the back key is pressed.
		if(keyCode == MAK_0 || keyCode == MAK_BACK)
		{
			close();
		}
	}

	//Now we implement the custom event listener interface.
	void customEvent(const MAEvent& event)
	{
		//Ignore events that do not come from widgets.
		if( event.type != EVENT_TYPE_WIDGET )
		{
			return;
		}

		// Check if the GLView is ready to be drawn.
		MAWidgetEventData *eventData = (MAWidgetEventData *) event.data;
		if( eventData->eventType == WIDGET_EVENT_GL_VIEW_READY )
		{
			//Make the OpenGL context associated with the GLView active.
			maWidgetSetProperty(mGLView, "bind", "");

			//Create an OpenGL 2D texture from the R_BOX resource.
			glEnable(GL_TEXTURE_2D);
			glGenTextures(1, &mBoxTextureHandle);
			glBindTexture(GL_TEXTURE_2D, mBoxTextureHandle);
			maOpenGLTexImage2D(R_BOX);

			//Set texture parameters.
			glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

			//Get width and height of the GLView widget, and resize.
			int glViewWidth = maWidgetGetPropertyInt(mGLView, "width");
			int glViewHeight = maWidgetGetPropertyInt(mGLView, "height");
			resizeWindow(glViewWidth, glViewHeight);

			//Initialize OpenGL.
			initGL();

			//Commit the changes to GLView ("invalidate" tells the screen
			//that it is out-of-date and needs to be redrawn).
			maWidgetSetProperty(mGLView, "invalidate", "");

			//Record that the GLView has been initialized.
			mGlViewInitialized = true;
		}
	}

	void runTimerEvent()
	{
		// Only draw when the GLView is initialized
		if( !mGlViewInitialized )
		{
			return;
		}

	    //Calculate the frames per second.
	    GLfloat texcoords[4][2];
	    GLfloat vertices[4][3];
	    GLubyte indices[4]={0, 1, 3, 2}; /* QUAD to TRIANGLE_STRIP conversion; */

	    //Clear the screen and the depth buffer.
	    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	    //Move into the screen 5 units.
	    glLoadIdentity();
	    glTranslatef(0.0f, 0.0f, -5.0f);

	    glRotatef(mXRotation, 1.0f, 0.0f, 0.0f); /* Rotate On The X Axis */
	    glRotatef(mYRotation, 0.0f, 1.0f, 0.0f); /* Rotate On The Y Axis */
	    glRotatef(mZRotation, 0.0f, 0.0f, 1.0f); /* Rotate On The Z Axis */

	    //Select our texture.
	    glBindTexture(GL_TEXTURE_2D, mBoxTextureHandle);

	    //Set pointers to vertices and texcoords.
	    glVertexPointer(3, GL_FLOAT, 0, vertices);
	    glTexCoordPointer(2, GL_FLOAT, 0, texcoords);

	    //Enable vertices and texcoords arrays.
	    glEnableClientState(GL_VERTEX_ARRAY);
	    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	    //Front face.
	    texcoords[0][0]=1.0f; texcoords[0][1]=0.0f;
	    vertices[0][0]=-1.0f; vertices[0][1]=-1.0f; vertices[0][2]=1.0f;
	    texcoords[1][0]=0.0f; texcoords[1][1]=0.0f;
	    vertices[1][0]=1.0f;  vertices[1][1]=-1.0f; vertices[1][2]=1.0f;
	    texcoords[2][0]=0.0f; texcoords[2][1]=1.0f;
	    vertices[2][0]=1.0f;  vertices[2][1]=1.0f; vertices[2][2]=1.0f;
	    texcoords[3][0]=1.0f; texcoords[3][1]=1.0f;
	    vertices[3][0]=-1.0f; vertices[3][1]=1.0f; vertices[3][2]=1.0f;

	    //Draw one textured plane using two stripped triangles.
	    glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);

	    //Back face.
	    //Normal pointing away from viewer.
	    texcoords[0][0]=0.0f; texcoords[0][1]=0.0f;
	    vertices[0][0]=-1.0f; vertices[0][1]=-1.0f; vertices[0][2]=-1.0f;
	    texcoords[1][0]=0.0f; texcoords[1][1]=1.0f;
	    vertices[1][0]=-1.0f; vertices[1][1]=1.0f; vertices[1][2]=-1.0f;
	    texcoords[2][0]=1.0f; texcoords[2][1]=1.0f;
	    vertices[2][0]=1.0f;  vertices[2][1]=1.0f; vertices[2][2]=-1.0f;
	    texcoords[3][0]=1.0f; texcoords[3][1]=0.0f;
	    vertices[3][0]=1.0f; vertices[3][1]=-1.0f; vertices[3][2]=-1.0f;

	    //Draw one textured plane using two stripped triangles.
	    glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);

	    //Top Face.
	    texcoords[0][0]=1.0f; texcoords[0][1]=1.0f;
	    vertices[0][0]=-1.0f; vertices[0][1]=1.0f; vertices[0][2]=-1.0f;
	    texcoords[1][0]=1.0f; texcoords[1][1]=0.0f;
	    vertices[1][0]=-1.0f; vertices[1][1]=1.0f; vertices[1][2]=1.0f;
	    texcoords[2][0]=0.0f; texcoords[2][1]=0.0f;
	    vertices[2][0]=1.0f;  vertices[2][1]=1.0f; vertices[2][2]=1.0f;
	    texcoords[3][0]=0.0f; texcoords[3][1]=1.0f;
	    vertices[3][0]=1.0f;  vertices[3][1]=1.0f; vertices[3][2]=-1.0f;

	    //Draw one textured plane using two stripped triangles.
	    glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);

	    //Bottom Face.
	    texcoords[0][0]=0.0f; texcoords[0][1]=1.0f;
	    vertices[0][0]=-1.0f; vertices[0][1]=-1.0f; vertices[0][2]=-1.0f;
	    texcoords[1][0]=1.0f; texcoords[1][1]=1.0f;
	    vertices[1][0]=1.0f;  vertices[1][1]=-1.0f; vertices[1][2]=-1.0f;
	    texcoords[2][0]=1.0f; texcoords[2][1]=0.0f;
	    vertices[2][0]=1.0f;  vertices[2][1]=-1.0f; vertices[2][2]=1.0f;
	    texcoords[3][0]=0.0f; texcoords[3][1]=0.0f;
	    vertices[3][0]=-1.0f; vertices[3][1]=-1.0f; vertices[3][2]=1.0f;

	    //Draw one textured plane using two stripped triangles.
	    glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);

	    //Right face.
	    texcoords[0][0]=0.0f; texcoords[0][1]=0.0f;
	    vertices[0][0]=1.0f;  vertices[0][1]=-1.0f; vertices[0][2]=-1.0f;
	    texcoords[1][0]=0.0f; texcoords[1][1]=1.0f;
	    vertices[1][0]=1.0f;  vertices[1][1]=1.0f; vertices[1][2]=-1.0f;
	    texcoords[2][0]=1.0f; texcoords[2][1]=1.0f;
	    vertices[2][0]=1.0f;  vertices[2][1]=1.0f; vertices[2][2]=1.0f;
	    texcoords[3][0]=1.0f; texcoords[3][1]=0.0f;
	    vertices[3][0]=1.0f;  vertices[3][1]=-1.0f; vertices[3][2]=1.0f;

	    //Draw one textured plane using two stripped triangles.
	    glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);

	    //Left Face.
	    texcoords[0][0]=1.0f; texcoords[0][1]=0.0f;
	    vertices[0][0]=-1.0f; vertices[0][1]=-1.0f; vertices[0][2]=-1.0f;
	    texcoords[1][0]=0.0f; texcoords[1][1]=0.0f;
	    vertices[1][0]=-1.0f; vertices[1][1]=-1.0f; vertices[1][2]=1.0f;
	    texcoords[2][0]=0.0f; texcoords[2][1]=1.0f;
	    vertices[2][0]=-1.0f; vertices[2][1]=1.0f; vertices[2][2]=1.0f;
	    texcoords[3][0]=1.0f; texcoords[3][1]=1.0f;
	    vertices[3][0]=-1.0f; vertices[3][1]=1.0f; vertices[3][2]=-1.0f;

	    //Draw one textured plane using two stripped triangles.
	    glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);

	    //Disable texcoords and vertices arrays.
	    glDisableClientState(GL_NORMAL_ARRAY);
	    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	    glDisableClientState(GL_VERTEX_ARRAY);

	    //Flush all drawings.
	    glFinish();

	    mXRotation+=1.0f; /* X Axis Rotation */
	    mYRotation+=0.8f; /* Y Axis Rotation */
	    mZRotation+=0.6f; /* Z Axis Rotation */

		//Commit the changes to GLView.
	    maWidgetSetProperty(mGLView, "invalidate", "");
	}

private:
	//Handle to the GLView widget.
	MAHandle mGLView;

	//GLView state (true = initialized and ready to be drawn).
	bool mGlViewInitialized;

	//Handle for the box texture.
	GLuint mBoxTextureHandle;

	//Rotation angles.
	GLfloat mXRotation;
	GLfloat mYRotation;
	GLfloat mZRotation;
};

//The entry point for the application - the place where processing starts.
extern "C" int MAMain() {

	//Create the instance of MyMoblet
	MyMoblet myMoblet;

	// Run the Moblet to start the application.
	MAUtil::Moblet::run( &myMoblet );

	//MyMoblet will run until it is closed by the user pressing key 0. When
	//it's closed we end our program in a well-behaved way by returning zero.
	return 0;
};

AttachmentSize
HelloOpenGLES.zip159.22 KB

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Olivier Berthelot
oberthelot's picture
Offline
Joined: 22 Mar 2011
Posts:

Project is not compiling....Cannot find MAHeaders.h

- Where in your code is the gl context created ? Via the screen widget ?
- When I try your code in my app. It doesn't create the screen widget correctly as if it does not exist (I have the lastest nightly build of MoSync).
Function declaration specifies this :
/**
* maWidgetCreate: Create a new widget of the specified type.
*
* Currently the following widgets are supported:
* - ListBox
* - HorizontalLayout
* - VerticalLayout
* - Button
* - Label
*
* \param widgetType A String representing the type of the widget to create.
*
* \returns A handle to the widget, or WIDGET_ERROR if the widget
* could not be created.
*/

Sam Pickard
rival's picture
Offline
Mobile Archmage
Joined: 19 Mar 2009
Posts:

Hi, thanks for this.

MAHeaders.h is generated when the code is built. You can't see it your project, but the source code will have access to it at build time.

Screen isn't a widget, rather it manages the program flow and displays widgets it contains. Are you building from the zip file or have you copy and pasted the source code into a new project?

Thanks

Olivier Berthelot
oberthelot's picture
Offline
Joined: 22 Mar 2011
Posts:

I'm using the zip file....But I just got it working....

I've had to add MAUtil.lib in the project additionnal librairies includes (under : Project properties / Build Settings )

Thanks for the reply ;)

Olivier Berthelot
oberthelot's picture
Offline
Joined: 22 Mar 2011
Posts:

I'm trying to use this in conjunction with other widget to create some kind of layout with buttons and the OpenGl View at the same time.

Any ideas on how I can merge that all together....I was thinking about using the glView created by maCreateWidget and put it in some layout on a Screen, but it only returns a maHandle. I think my main question would be : How to use the Handle system with the already existing MAUI Widget, Screen system ???

Niklas Nummelin
niklas's picture
Offline
Mobile Wizard
Joined: 18 Dec 2007
Posts:

MAUI and Native UI are two different api:s (maWidgetCreate is a part of the Native UI api). MAUI is a custom UI system that works the same on all platforms, while Native UI is an api that that makes it possible to use the native UI system of the platform (currently running on Android and iPhone).

The GLView is a widget that can be created using the Native UI api. Today you can't combine Native UI and regular MoSync drawing (which MAUI relies on). In the latest nightly builds you can however switch between the Native UI screens and the MoSync canvas screen. Switch back to the MoSync canvas (after switching to a Native UI screen) by using maWidgetScreenShow(MAW_CONSTANT_MOSYNC_SCREEN_HANDLE).

Documentation about how to use the Native UI system will be published asap. For now you can use the offline reference documentation (/docs/html/index.html), and find out some stuff by expanding the "Modules" item in the left frame.

The goal in the future is to do a layer that makes a common interface between Native UI and MAUI so you don't have to care about this. But as it is now you have to chose one of them, or make two different UI:s (if you want to target all platforms) - one with MAUI and one with Native UI.

// Niklas

Olivier Berthelot
oberthelot's picture
Offline
Joined: 22 Mar 2011
Posts:

Thanks for the reply !
I had figured that much going through the source code for the past few days ! But thanks for confirming my presumptions !
Looks like MoSync won't be the solution for me here, maybe in further release . . .

Thanks for the help !

sasij
sasij's picture
Offline
Mobile Conjurer
Joined: 2 Apr 2011
Posts:

Just want to point out to anyone downloading and trying this with the latest nightly build. You'll need to change WIDGET_EVENT_GL_VIEW_READY to MAW_EVENT_GL_VIEW_READY. That's it, just figured I'd throw that out there.

Samuel Kupka
bwpow's picture
Offline
Joined: 6 Feb 2011
Posts:

HelloOpenGLES, when run on android, can cause some minor problems. You can see them in this video.

When the app is closed using back button, everything is ok. This event is caught with keyPressEvent method and close() is called. But when you try to close app using home button, or your device goes to sleep mode, app is still running in the background and tries to draw the cube and you can see parts of the OpenGL viewport popping to front (for example in sms creator shown in the video). The solution is catching focusLost event.

For this, you need to change a few lines. First, add MAUtil::FocusListener to MyMoblet:

class MyMoblet : public MAUtil::Moblet, MAUtil::TimerListener, MAUtil::FocusListener {

Next, to have to attach listener to this class. The following line goes into constructor, just after attaching CustomEventListener and before adding the timer.

MAUtil::Environment::getEnvironment().addFocusListener(this);

The last thing you need to do is to implement focusLost() and focusGained() methods:

void focusLost()
{
	close();
}

void focusGained()
{
}

Method focusLost() is called whenever your application losts its focus and that happens when you press home button, you receive a call or some other application forces itself to foreground. Of course, you don't have to close your application. You can save current state and close, or just stop the timer until focusGained() method is called. But, the point is, once you lost focus, stop invalidating your OpenGL widget, so it won't interfere with other applications.

powerlogy
powerlogy's picture
Offline
Joined: 19 Jul 2011
Posts:

i done every single step , to run this project.But it gives lots of errors , really cant understand whats the problem.I added  MAUtil.lib.

i attached , error file to the post.Thanks for helping me

AttachmentSize
error.txt 11.5 KB