Log in or register to post comments

Text Rendering

April 5, 2017 - 2:18am #1

Hello, 



I would like to be able to recognize a target and then to render dynamic text onto this target. 

I found on another topic that these two links provide a way to do this using openGL : 

- http://fractiousg.blogspot.fr/2012/04/rendering-text-in-opengl-on-android.html

- https://github.com/d3alek/Texample2 (using openGL 2.0)

I tried different things with that sample projects in order to adapt it to the Vuforia Rendering class with openGL. Unfortunately, I'm currently not able to find the right solution. 



Could someone please help me on the injection mechanism with the existing Vuforia model ? I think this is an openGL question since it directly interacts with it. I'm developing with Android Studio, based on the sample project provided by Vuforia (ImageTarget, ObjectRecognition, etc...)





EDIT : Here is my ImageTargetRenderer.java file content, I guess there is something to fix and, as I'm an OpenGL beginner, I still don't find the solution. Maybe some experts could find quickly what is wrong.

import android.content.Context;

import android.opengl.GLES20;

import android.opengl.GLSurfaceView;

import android.opengl.Matrix;

import android.util.Log;

import com.soprasteria.arstore.business.TextRendering.GLText;

import com.vuforia.Device;

import com.vuforia.Matrix44F;

import com.vuforia.State;

import com.vuforia.Tool;

import com.vuforia.Trackable;

import com.vuforia.TrackableResult;

import com.vuforia.Vuforia;

import com.soprasteria.arstore.vuforia.AppRenderer;

import com.soprasteria.arstore.vuforia.AppRendererControl;

import com.soprasteria.arstore.vuforia.ApplicationSession;

import com.soprasteria.arstore.vuforia.utils.CubeShaders;

import com.soprasteria.arstore.vuforia.utils.LoadingDialogHandler;

import com.soprasteria.arstore.vuforia.utils.Application3DModel;

import com.soprasteria.arstore.vuforia.utils.Utils;

import com.soprasteria.arstore.vuforia.utils.Teapot;

import com.soprasteria.arstore.vuforia.utils.Texture;

import java.io.IOException;

import java.util.Vector;

import javax.microedition.khronos.egl.EGLConfig;

import javax.microedition.khronos.opengles.GL10;



// The renderer class for the ImageTargets

public class ImageTargetRenderer implements GLSurfaceView.Renderer, AppRendererControl

{

    private static final String LOGTAG = "ImageTargetRenderer";

    private ApplicationSession applicationSession;

    private ImageTargets mActivity;

    private AppRenderer mAppRenderer;

    private Vector<Texture> mTextures;

    private int shaderProgramID;

    private int vertexHandle;

    private int textureCoordHandle;

    private int mvpMatrixHandle;

    private int texSampler2DHandle;

    private Teapot mTeapot;

    //private FlyingSaucer mTeapot;

    private float kBuildingScale = 0.012f;

    private Application3DModel mBuildingsModel;

    private boolean mIsActive = false;

    private boolean mModelIsLoaded = false;

    private static final float OBJECT_SCALE_FLOAT = 0.003f;

    // TODO : Text Rendering test

    private GLText glText;                             // A GLText Instance

    private Context context;                           // Context (from Activity)

    private int width = 100;                           // Updated to the Current Width + Height in onSurfaceChanged()

    private int height = 100;

    private float[] mProjMatrix = new float[16];

    private float[] mVMatrix = new float[16];

    private float[] mVPMatrix = new float[16];

    //TODO fin



    public ImageTargetRenderer(ImageTargets activity, ApplicationSession session)

    {

        mActivity = activity;

        applicationSession = session;

        // AppRenderer used to encapsulate the use of RenderingPrimitives setting

        // the device mode AR/VR and stereo mode

        mAppRenderer = new AppRenderer(this, mActivity, Device.MODE.MODE_AR, false, 0.01f , 5f);

        // TODO

        context = mActivity.getApplicationContext();

    }



    // Called to draw the current frame.

    @Override

    public void onDrawFrame(GL10 gl)

    {

        if (!mIsActive)

            return;

        // Call our function to render content from AppRenderer class

        mAppRenderer.render();

    }



    public void setActive(boolean active)

    {

        mIsActive = active;

        if(mIsActive)

            mAppRenderer.configureVideoBackground();

    }



    // Called when the surface is created or recreated.

    @Override

    public void onSurfaceCreated(GL10 gl, EGLConfig config)

    {

        Log.d(LOGTAG, "GLRenderer.onSurfaceCreated");

        // Call Vuforia function to (re)initialize rendering after first use

        // or after OpenGL ES context was lost (e.g. after onPause/onResume):

        applicationSession.onSurfaceCreated();

        mAppRenderer.onSurfaceCreated();

        // TODO début

        // Create the GLText

        glText = new GLText(context.getAssets());

        // Load the font from file (set size + padding), creates the texture

        // NOTE: after a successful call to this the font is ready for rendering!

        glText.load( "Roboto-Regular.ttf", 14, 2, 2 );  // Create Font (Height: 14 Pixels / X+Y Padding 2 Pixels)

        // enable texture + alpha blending

        GLES20.glEnable(GLES20.GL_BLEND);

        GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        // TODO Fin

    }



    // Called when the surface changed size.

    @Override

    public void onSurfaceChanged(GL10 gl, int width, int height) {

        Log.d(LOGTAG, "GLRenderer.onSurfaceChanged");

        // TODO début

        GLES20.glViewport(0, 0, width, height);

        float ratio = (float) width / height;

        // Take into account device orientation

        if (width > height) {

            Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10);

        }

        else {

            Matrix.frustumM(mProjMatrix, 0, -1, 1, -1/ratio, 1/ratio, 1, 10);

        }

        // Save width and height

        this.width = width;                             // Save Current Width

        this.height = height;                           // Save Current Height

        int useForOrtho = Math.min(width, height);

        //TODO: Is this wrong?

        Matrix.orthoM(mVMatrix, 0,

                -useForOrtho/2,

                useForOrtho/2,

                -useForOrtho/2,

                useForOrtho/2, 0.1f, 100f);

        // TODO FIN

        // Call Vuforia function to handle render surface size changes:

        applicationSession.onSurfaceChanged(width, height);

        // RenderingPrimitives to be updated when some rendering change is done

        mAppRenderer.onConfigurationChanged(mIsActive);

        initRendering();

    }



    // Function for initializing the renderer.

    private void initRendering()

    {

        GLES20.glClearColor(0.0f, 0.0f, 0.0f, Vuforia.requiresAlpha() ? 0.0f

                : 1.0f);

        for (Texture t : mTextures)

        {

            GLES20.glGenTextures(1, t.mTextureID, 0);

            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, t.mTextureID[0]);

            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,

                GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);

            GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,

                GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

            GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,

                t.mWidth, t.mHeight, 0, GLES20.GL_RGBA,

                GLES20.GL_UNSIGNED_BYTE, t.mData);

        }

        shaderProgramID = Utils.createProgramFromShaderSrc(

            CubeShaders.CUBE_MESH_VERTEX_SHADER,

            CubeShaders.CUBE_MESH_FRAGMENT_SHADER);

        vertexHandle = GLES20.glGetAttribLocation(shaderProgramID,

            "vertexPosition");

        textureCoordHandle = GLES20.glGetAttribLocation(shaderProgramID,

            "vertexTexCoord");

        mvpMatrixHandle = GLES20.glGetUniformLocation(shaderProgramID,

            "modelViewProjectionMatrix");

        texSampler2DHandle = GLES20.glGetUniformLocation(shaderProgramID,

            "texSampler2D");

        if(!mModelIsLoaded) {

            mTeapot = new Teapot();

            //mTeapot = new FlyingSaucer();

            try {

                mBuildingsModel = new Application3DModel();

                mBuildingsModel.loadModel(mActivity.getResources().getAssets(),

                        "ImageTargets/Buildings.txt");

                mModelIsLoaded = true;

            } catch (IOException e) {

                Log.e(LOGTAG, "Unable to load buildings");

            }

            // Hide the Loading Dialog

            mActivity.loadingDialogHandler

                    .sendEmptyMessage(LoadingDialogHandler.HIDE_LOADING_DIALOG);

        }

    }

    public void updateConfiguration()

    {

        mAppRenderer.onConfigurationChanged(mIsActive);

    }

    // The render function called from AppRendering by using RenderingPrimitives views.

    // The state is owned by AppRenderer which is controlling it's lifecycle.

    // State should not be cached outside this method.

    public void renderFrame(State state, float[] projectionMatrix)

    {

        // Renders video background replacing Renderer.DrawVideoBackground()

        mAppRenderer.renderVideoBackground();

        GLES20.glEnable(GLES20.GL_DEPTH_TEST);

        // handle face culling, we need to detect if we are using reflection

        // to determine the direction of the culling

        GLES20.glEnable(GLES20.GL_CULL_FACE);

        GLES20.glCullFace(GLES20.GL_BACK);

        // Did we find any trackables this frame?

        for (int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++) {

            TrackableResult result = state.getTrackableResult(tIdx);

            Trackable trackable = result.getTrackable();

            printUserData(trackable);

            Matrix44F modelViewMatrix_Vuforia = Tool

                    .convertPose2GLMatrix(result.getPose());

            float[] modelViewMatrix = modelViewMatrix_Vuforia.getData();

            int textureIndex = trackable.getName().equalsIgnoreCase("stones") ? 0

                    : 1;

            textureIndex = trackable.getName().equalsIgnoreCase("tarmac") ? 2

                    : textureIndex;

            // deal with the modelview and projection matrices

            float[] modelViewProjection = new float[16];

            if (!mActivity.isExtendedTrackingActive()) {

                Matrix.translateM(modelViewMatrix, 0, 0.0f, 0.0f,

                        OBJECT_SCALE_FLOAT);

                Matrix.scaleM(modelViewMatrix, 0, OBJECT_SCALE_FLOAT,

                        OBJECT_SCALE_FLOAT, OBJECT_SCALE_FLOAT);

            } else {

                Matrix.rotateM(modelViewMatrix, 0, 90.0f, 1.0f, 0, 0);

                Matrix.scaleM(modelViewMatrix, 0, kBuildingScale,

                        kBuildingScale, kBuildingScale);

            }

            Matrix.multiplyMM(modelViewProjection, 0, projectionMatrix, 0, modelViewMatrix, 0);

            // TODO début

            // TEST: render the entire font texture

            glText.drawTexture( width/2, height/2, mVPMatrix);            // Draw the Entire Texture

            // TEST: render some strings with the font

            glText.begin( 1.0f, 1.0f, 1.0f, 1.0f, mVPMatrix );         // Begin Text Rendering (Set Color WHITE)

            glText.drawC("Test String 3D!", 0f, 0f, 0f, 0, -30, 0);

//  glText.drawC( "Test String :)", 0, 0, 0 );          // Draw Test String

            glText.draw( "Diagonal 1", 40, 40, 40);                // Draw Test String

            glText.draw( "Column 1", 100, 100, 90);              // Draw Test String

            glText.end();                                   // End Text Rendering

            glText.begin( 0.0f, 0.0f, 1.0f, 1.0f, mVPMatrix );         // Begin Text Rendering (Set Color BLUE)

            glText.draw( "More Lines...", 50, 200 );        // Draw Test String

            glText.draw( "The End.", 50, 200 + glText.getCharHeight(), 180);  // Draw Test String

            glText.end();                                   // End Text Rendering

            // TODO Fin

            // activate the shader program and bind the vertex/normal/tex coords

            GLES20.glUseProgram(shaderProgramID);

            if (!mActivity.isExtendedTrackingActive()) {

                GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT,

                        false, 0, mTeapot.getVertices());

                GLES20.glVertexAttribPointer(textureCoordHandle, 2,

                        GLES20.GL_FLOAT, false, 0, mTeapot.getTexCoords());

                GLES20.glEnableVertexAttribArray(vertexHandle);

                GLES20.glEnableVertexAttribArray(textureCoordHandle);

                // activate texture 0, bind it, and pass to shader

                GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,

                        mTextures.get(textureIndex).mTextureID[0]);

                GLES20.glUniform1i(texSampler2DHandle, 0);

                // pass the model view matrix to the shader

                GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false,

                        modelViewProjection, 0);

                // finally draw the teapot

                GLES20.glDrawElements(GLES20.GL_TRIANGLES,

                        mTeapot.getNumObjectIndex(), GLES20.GL_UNSIGNED_SHORT,

                        mTeapot.getIndices());

                // disable the enabled arrays

                GLES20.glDisableVertexAttribArray(vertexHandle);

                GLES20.glDisableVertexAttribArray(textureCoordHandle);

            } else {

                GLES20.glDisable(GLES20.GL_CULL_FACE);

                GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT,

                        false, 0, mBuildingsModel.getVertices());

                GLES20.glVertexAttribPointer(textureCoordHandle, 2,

                        GLES20.GL_FLOAT, false, 0, mBuildingsModel.getTexCoords());

                GLES20.glEnableVertexAttribArray(vertexHandle);

                GLES20.glEnableVertexAttribArray(textureCoordHandle);

                GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,

                        mTextures.get(3).mTextureID[0]);

                GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false,

                        modelViewProjection, 0);

                GLES20.glUniform1i(texSampler2DHandle, 0);

                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0,

                        mBuildingsModel.getNumObjectVertex());

                Utils.checkGLError("Renderer DrawBuildings");

            }

            Utils.checkGLError("Render Frame");

        }

        GLES20.glDisable(GLES20.GL_DEPTH_TEST);

    }

    private void printUserData(Trackable trackable)

    {

        String userData = (String) trackable.getUserData();

        Log.d(LOGTAG, "UserData:Retreived User Data \"" + userData + "\"");

    }



    public void setTextures(Vector<Texture> textures)

    {

        mTextures = textures;

    }

}

Text Rendering

April 5, 2017 - 6:52am #2

-

Log in or register to post comments