Android - How can I bring multiple targets into a common space?

August 10, 2012 - 3:12pm #1

The pose matrix defines a local coordinate system for each trackable, with the center of the trackable as the origin (0, 0, 0). This makes it easy to draw objects on top of each trackable independently, but not so easy to draw content that spans multiple trackables (taking their relative orientation into account). The solution is to pick one trackable to act as the world center, and bring all other trackables into its coordinate system. We do that by multiplying the inverse pose of our world center target (A) by the pose of our other targets (B). This creates an offset matrix that can be used to bring points on B into A's coordinate system. Now, we can bind a single modelview matrix tied to our world center and render all the points from the world center's point of view. That should let you draw lines between targets. This is a replacement renderFrame method for the ImageTargets sample. It will render a line connecting the center of the chips target to the center of the stones target. You will need to do a few setup steps first:

  1. Copy SampleMath.cpp and SampleMath.h from the Dominoes/jni folder to the ImageTargets/jni folder.
  2. Add SampleMath.cpp to the LOCAL_SRC_FILES flag in the ImageTargets Android.mk file.
  3. Uncomment the following lines in ImageTargets.cpp:

QCAR::setHint(QCAR::HINT_MAX_SIMULTANEOUS_IMAGE_TARGETS, 2);

QCAR::setHint(QCAR::HINT_IMAGE_TARGET_MULTI_FRAME_ENABLED, 1);

Replace the renderFrame method with the following:

#include "SampleMath.h"

JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargetsRenderer_renderFrame(JNIEnv *, jobject)
{
    // Clear color and depth buffer 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    // Render video background:
    QCAR::State state = QCAR::Renderer::getInstance().begin();
    
#ifdef USE_OPENGL_ES_1_1
    // Set GL11 flags:
    glEnableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisable(GL_LIGHTING);
#endif
    
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    
    QCAR::Matrix44F mainModelViewMatrix;
    QCAR::Vec3F targetCenters[2]; // make this big enough to hold all your targets
    
    // Did we find any trackables this frame?
    for(int tIdx = 0; tIdx < state.getNumActiveTrackables(); tIdx++)
    {
        // Get the trackable:
        const QCAR::Trackable* trackable = state.getActiveTrackable(tIdx);
        QCAR::Matrix44F modelViewMatrix =
        QCAR::Tool::convertPose2GLMatrix(trackable->getPose());        
        
        if (tIdx == 0)
        {
            // Make the first visible target our world center (0, 0, 0)
            // Store its modelViewMatrix and continue looking for other targets
            mainModelViewMatrix = modelViewMatrix;
            targetCenters[0].data[0] = 0.0f;
            targetCenters[0].data[1] = 0.0f;
            targetCenters[0].data[2] = 0.0f;
        }
        else
        {
            // This is another visible target
            // Find its center point in relation to the first target
            // To do this we use the matrix inverse function (SampleMath.h from the Dominoes project)
            QCAR::Matrix44F mainModelViewInverse = SampleMath::Matrix44FInverse(mainModelViewMatrix);
            QCAR::Matrix44F modelViewTranspose = SampleMath::Matrix44FTranspose(modelViewMatrix); // let's work with row-major matrices
            QCAR::Matrix44F offsetMatrix = QCAR::Tool::multiply(mainModelViewInverse, modelViewTranspose);
            
            // Transform a point on the second target by this offset matrix
            // (0, 0, 0) is the local center of the target
            QCAR::Vec4F position(0.0f, 0.0f, 0.0f, 1.0f);
            position = SampleMath::Vec4FTransform(position, offsetMatrix);
            
            // Add this position to our array
            targetCenters[1].data[0] = position.data[0];
            targetCenters[1].data[1] = position.data[1];
            targetCenters[1].data[2] = position.data[2];
        }
    }
    
    if (state.getNumActiveTrackables() > 1)
    {
#ifdef USE_OPENGL_ES_1_1
        // Load projection matrix:
        glMatrixMode(GL_PROJECTION);
        glLoadMatrixf(projectionMatrix.data);
        
        // Load model view matrix:
        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf(mainModelViewMatrix.data);
        
        // Set the color to red:
        glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
        
        // Draw object:
        glVertexPointer(3, GL_FLOAT, 0, (const GLvoid*) &targetCenters[0].data[0]);
        glDrawArrays(GL_LINES, 0, 2);
#else
        
        QCAR::Matrix44F modelViewProjection;
        
        SampleUtils::multiplyMatrix(&projectionMatrix.data[0],
                                    &mainModelViewMatrix.data[0],
                                    &modelViewProjection.data[0]);
        
        glUseProgram(shaderProgramID);
        
        glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0,
                              (const GLvoid*) &targetCenters[0].data[0]);
        
        glEnableVertexAttribArray(vertexHandle);
        
        glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE,
                           (GLfloat*) &modelViewProjection.data[0]);
        glDrawArrays(GL_LINES, 0, 2);
#endif
    }
    
    glDisable(GL_DEPTH_TEST);
    
#ifdef USE_OPENGL_ES_1_1        
    glDisable(GL_TEXTURE_2D);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
#else
    glEnable(GL_TEXTURE_2D);
    glDisableVertexAttribArray(vertexHandle);
    glDisableVertexAttribArray(normalHandle);
    glDisableVertexAttribArray(textureCoordHandle);
#endif
    
    QCAR::Renderer::getInstance().end();
}

Topic locked