/*==============================================================================
Copyright (c) 2010-2013 QUALCOMM Austria Research Center GmbH.
All Rights Reserved.
@file
FrameMarkers.cpp
@brief
Sample for FrameMarkers
==============================================================================*/
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <QCAR/QCAR.h>
#include <QCAR/CameraDevice.h>
#include <QCAR/Renderer.h>
#include <QCAR/VideoBackgroundConfig.h>
#include <QCAR/Trackable.h>
#include <QCAR/TrackableResult.h>
#include <QCAR/MarkerResult.h>
#include <QCAR/Tool.h>
#include <QCAR/MarkerTracker.h>
#include <QCAR/TrackerManager.h>
#include <QCAR/CameraCalibration.h>
#include <QCAR/Marker.h>
#include "SampleUtils.h"
#include "Texture.h"
#include "CubeShaders.h"
#include "Q_object.h"
#include "C_object.h"
#include "A_object.h"
#include "R_object.h"
#include "assimp_loader.h"
#ifdef __cplusplus
extern "C"
{
#endif
// Textures:
int textureCount = 0;
Texture** textures = 0;
// OpenGL ES 2.0 specific:
unsigned int shaderProgramID = 0;
GLint vertexHandle = 0;
GLint normalHandle = 0;
GLint textureCoordHandle = 0;
GLint mvpMatrixHandle = 0;
GLint texSampler2DHandle = 0;
// Screen dimensions:
unsigned int screenWidth = 0;
unsigned int screenHeight = 0;
// Indicates whether screen is in portrait (true) or landscape (false) mode
bool isActivityInPortraitMode = false;
// The projection matrix used for rendering virtual objects:
QCAR::Matrix44F projectionMatrix;
// Constants:
static const float kLetterScale = 25.0f;
static const float kLetterTranslate = 25.0f;
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_setActivityPortraitMode(JNIEnv *, jobject, jboolean isPortrait)
{
isActivityInPortraitMode = isPortrait;
}
JNIEXPORT int JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_initTracker(JNIEnv *, jobject)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_initTracker");
// Initialize the marker tracker:
QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
QCAR::Tracker* trackerBase = trackerManager.initTracker(QCAR::Tracker::MARKER_TRACKER);
QCAR::MarkerTracker* markerTracker = static_cast<QCAR::MarkerTracker*>(trackerBase);
if (markerTracker == NULL)
{
LOG("Failed to initialize MarkerTracker.");
return 0;
}
if (!markerTracker->createFrameMarker(0, "MarkerQ", QCAR::Vec2F(50,50)))
{
LOG("Failed to create frame marker Q.");
}
if (!markerTracker->createFrameMarker(1, "MarkerC", QCAR::Vec2F(50,50)))
{
LOG("Failed to create frame marker C.");
}
if (!markerTracker->createFrameMarker(2, "MarkerA", QCAR::Vec2F(50,50)))
{
LOG("Failed to create frame marker A.");
}
if (!markerTracker->createFrameMarker(3, "MarkerR", QCAR::Vec2F(50,50)))
{
LOG("Failed to create frame marker R.");
}
LOG("Successfully initialized MarkerTracker.");
return 1;
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_deinitTracker(JNIEnv *, jobject)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_deinitTracker");
// Deinit the marker tracker, this will destroy all created frame markers:
QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
trackerManager.deinitTracker(QCAR::Tracker::MARKER_TRACKER);
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkersRenderer_renderFrame(JNIEnv *, jobject)
{
//LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_GLRenderer_renderFrame");
// Clear color and depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Get the state from QCAR and mark the beginning of a rendering section
QCAR::State state = QCAR::Renderer::getInstance().begin();
// Explicitly render the Video Background
QCAR::Renderer::getInstance().drawVideoBackground();
glEnable(GL_DEPTH_TEST);
// We must detect if background reflection is active and adjust the culling direction.
// If the reflection is active, this means the post matrix has been reflected as well,
// therefore standard counter clockwise face culling will result in "inside out" models.
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
if(QCAR::Renderer::getInstance().getVideoBackgroundConfig().mReflection == QCAR::VIDEO_BACKGROUND_REFLECTION_ON)
glFrontFace(GL_CW); //Front camera
else
glFrontFace(GL_CCW); //Back camera
// Did we find any trackables this frame?
for(int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++)
{
// Get the trackable:
const QCAR::TrackableResult* trackableResult = state.getTrackableResult(tIdx);
QCAR::Matrix44F modelViewMatrix =
QCAR::Tool::convertPose2GLMatrix(trackableResult->getPose());
// Choose the texture based on the target name:
int textureIndex = 0;
// Check the type of the trackable:
assert(trackableResult->getType() == QCAR::TrackableResult::MARKER_RESULT);
const QCAR::MarkerResult* markerResult = static_cast<
const QCAR::MarkerResult*>(trackableResult);
const QCAR::Marker& marker = markerResult->getTrackable();
const aiScene* scene = assimp_getCurrentAiScene();
textureIndex = marker.getMarkerId();
if (scene && textureIndex == 0) {
textureIndex = 4;
}
assert(textureIndex < textureCount);
const Texture* const thisTexture = textures[textureIndex];
// Select which model to draw:
const GLvoid* vertices = 0;
const GLvoid* normals = 0;
const GLvoid* indices = 0;
const GLvoid* texCoords = 0;
int numIndices = 0;
switch (marker.getMarkerId())
{
case 0:
if (scene) {
char logStr[300];
sprintf(logStr, "We have %d vertices, %d normals, %d faces, %d indexes and %d texture coordinates to render",
scene->mMeshes[0]->mNumVertices,
scene->mMeshes[0]->mNumVertices,
scene->mMeshes[0]->mNumFaces,
scene->mMeshes[0]->mFaces->mNumIndices,
scene->mMeshes[0]->mNumVertices
);
LOG(logStr);
vertices = scene->mMeshes[0]->mVertices;
normals = scene->mMeshes[0]->mNormals;
indices = scene->mMeshes[0]->mFaces->mIndices;
texCoords = scene->mMeshes[0]->mTextureCoords;
numIndices = scene->mMeshes[0]->mFaces->mNumIndices;
} else {
vertices = &QobjectVertices[0];
normals = &QobjectNormals[0];
indices = &QobjectIndices[0];
texCoords = &QobjectTexCoords[0];
numIndices = NUM_Q_OBJECT_INDEX;
}
break;
case 1:
vertices = &CobjectVertices[0];
normals = &CobjectNormals[0];
indices = &CobjectIndices[0];
texCoords = &CobjectTexCoords[0];
numIndices = NUM_C_OBJECT_INDEX;
break;
case 2:
vertices = &AobjectVertices[0];
normals = &AobjectNormals[0];
indices = &AobjectIndices[0];
texCoords = &AobjectTexCoords[0];
numIndices = NUM_A_OBJECT_INDEX;
break;
default:
vertices = &RobjectVertices[0];
normals = &RobjectNormals[0];
indices = &RobjectIndices[0];
texCoords = &RobjectTexCoords[0];
numIndices = NUM_R_OBJECT_INDEX;
break;
}
QCAR::Matrix44F modelViewProjection;
SampleUtils::translatePoseMatrix(-kLetterTranslate,
-kLetterTranslate,
0.f,
&modelViewMatrix.data[0]);
SampleUtils::scalePoseMatrix(kLetterScale, kLetterScale, kLetterScale,
&modelViewMatrix.data[0]);
SampleUtils::multiplyMatrix(&projectionMatrix.data[0],
&modelViewMatrix.data[0],
&modelViewProjection.data[0]);
glUseProgram(shaderProgramID);
SampleUtils::checkGlError("[assimp] after glUseProgram");
glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, vertices);
SampleUtils::checkGlError("[assimp] after glVertexAttribPointer vertices");
glVertexAttribPointer(normalHandle, 3, GL_FLOAT, GL_FALSE, 0, normals);
SampleUtils::checkGlError("[assimp] after glVertexAttribPointer normals");
glVertexAttribPointer(textureCoordHandle, 2, GL_FLOAT, GL_FALSE,
0, texCoords);
SampleUtils::checkGlError("[assimp] after glVertexAttribPointer texCoords");
glEnableVertexAttribArray(vertexHandle);
SampleUtils::checkGlError("[assimp] after glEnableVertexAttribArray vertex handle");
glEnableVertexAttribArray(normalHandle);
SampleUtils::checkGlError("[assimp] after glEnableVertexAttribArray normal handle");
glEnableVertexAttribArray(textureCoordHandle);
SampleUtils::checkGlError("[assimp] after glEnableVertexAttribArray textureCoord handle");
glActiveTexture(GL_TEXTURE0);
SampleUtils::checkGlError("[assimp] after glActiveTexture");
glBindTexture(GL_TEXTURE_2D, thisTexture->mTextureID);
SampleUtils::checkGlError("[assimp] after glBindTexture");
glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE,
(GLfloat*)&modelViewProjection.data[0]);
SampleUtils::checkGlError("[assimp] after glUniformMatrix4fv");
glUniform1i(texSampler2DHandle, 0 /*GL_TEXTURE0*/);
SampleUtils::checkGlError("[assimp] after glUniform1i");
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices);
SampleUtils::checkGlError("[assimp] after glDrawElements");
glDisableVertexAttribArray(vertexHandle);
glDisableVertexAttribArray(normalHandle);
glDisableVertexAttribArray(textureCoordHandle);
SampleUtils::checkGlError("FrameMarkers render frame");
}
glDisable(GL_DEPTH_TEST);
QCAR::Renderer::getInstance().end();
}
void
configureVideoBackground()
{
// Get the default video mode:
QCAR::CameraDevice& cameraDevice = QCAR::CameraDevice::getInstance();
QCAR::VideoMode videoMode = cameraDevice.
getVideoMode(QCAR::CameraDevice::MODE_DEFAULT);
// Configure the video background
QCAR::VideoBackgroundConfig config;
config.mEnabled = true;
config.mSynchronous = true;
config.mPosition.data[0] = 0.0f;
config.mPosition.data[1] = 0.0f;
if (isActivityInPortraitMode)
{
//LOG("configureVideoBackground PORTRAIT");
config.mSize.data[0] = videoMode.mHeight
* (screenHeight / (float)videoMode.mWidth);
config.mSize.data[1] = screenHeight;
if(config.mSize.data[0] < screenWidth)
{
LOG("Correcting rendering background size to handle missmatch between screen and video aspect ratios.");
config.mSize.data[0] = screenWidth;
config.mSize.data[1] = screenWidth *
(videoMode.mWidth / (float)videoMode.mHeight);
}
}
else
{
//LOG("configureVideoBackground LANDSCAPE");
config.mSize.data[0] = screenWidth;
config.mSize.data[1] = videoMode.mHeight
* (screenWidth / (float)videoMode.mWidth);
if(config.mSize.data[1] < screenHeight)
{
LOG("Correcting rendering background size to handle missmatch between screen and video aspect ratios.");
config.mSize.data[0] = screenHeight
* (videoMode.mWidth / (float)videoMode.mHeight);
config.mSize.data[1] = screenHeight;
}
}
LOG("Configure Video Background : Video (%d,%d), Screen (%d,%d), mSize (%d,%d)", videoMode.mWidth, videoMode.mHeight, screenWidth, screenHeight, config.mSize.data[0], config.mSize.data[1]);
// Set the config:
QCAR::Renderer::getInstance().setVideoBackgroundConfig(config);
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_initApplicationNative(
JNIEnv* env, jobject obj, jint width, jint height)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_initApplicationNative");
// Store screen dimensions
screenWidth = width;
screenHeight = height;
// Handle to the activity class:
jclass activityClass = env->GetObjectClass(obj);
jmethodID getTextureCountMethodID = env->GetMethodID(activityClass,
"getTextureCount", "()I");
if (getTextureCountMethodID == 0)
{
LOG("Function getTextureCount() not found.");
return;
}
textureCount = env->CallIntMethod(obj, getTextureCountMethodID);
if (!textureCount)
{
LOG("getTextureCount() returned zero.");
return;
}
textures = new Texture*[textureCount];
jmethodID getTextureMethodID = env->GetMethodID(activityClass,
"getTexture", "(I)Lcom/qualcomm/QCARSamples/FrameMarkers/Texture;");
if (getTextureMethodID == 0)
{
LOG("Function getTexture() not found.");
return;
}
// Register the textures
for (int i = 0; i < textureCount; ++i)
{
jobject textureObject = env->CallObjectMethod(obj, getTextureMethodID, i);
if (textureObject == NULL)
{
LOG("GetTexture() returned zero pointer");
return;
}
textures[i] = Texture::create(env, textureObject);
}
bool success = assimp_importAsset("collada/m11_BBJ94_13WishesDollAsst.dae");
if (success) {
const aiScene* scene = assimp_getCurrentAiScene();
if (scene->HasAnimations()) {
LOG("[assimp] scene has animations");
}
if (scene->HasCameras()) {
LOG("[assimp] scene has cameras");
}
if (scene->HasLights()) {
LOG("[assimp] scene has lights");
}
if (scene->HasMaterials()) {
LOG("[assimp] scene has materials");
}
if (scene->HasMeshes()) {
LOG("[assimp] scene has meshes");
}
if (scene->HasTextures()) {
LOG("[assimp] scene has textures");
}
char tmpNum[15];
char logScene[1000];
strcpy(logScene, "[assimp] Scene with ");
sprintf(tmpNum, "%d", scene->mNumAnimations);
strcat(logScene, tmpNum);
strcat(logScene, " animations, ");
sprintf(tmpNum, "%d", scene->mNumCameras);
strcat(logScene, tmpNum);
strcat(logScene, " cameras, ");
sprintf(tmpNum, "%d", scene->mNumLights);
strcat(logScene, tmpNum);
strcat(logScene, " lights, ");
sprintf(tmpNum, "%d", scene->mNumMaterials);
strcat(logScene, tmpNum);
strcat(logScene, " materials, ");
sprintf(tmpNum, "%d", scene->mNumMeshes);
strcat(logScene, tmpNum);
strcat(logScene, " meshes, ");
sprintf(tmpNum, "%d", scene->mNumTextures);
strcat(logScene, tmpNum);
strcat(logScene, " textures");
LOG(logScene);
}
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_deinitApplicationNative(
JNIEnv* env, jobject obj)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_deinitApplicationNative");
// Release texture resources
if (textures != 0)
{
for (int i = 0; i < textureCount; ++i)
{
delete textures[i];
textures[i] = NULL;
}
delete[]textures;
textures = NULL;
textureCount = 0;
}
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_startCamera(JNIEnv *,
jobject)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_startCamera");
// Select the camera to open, set this to QCAR::CameraDevice::CAMERA_FRONT
// to activate the front camera instead.
QCAR::CameraDevice::CAMERA camera = QCAR::CameraDevice::CAMERA_DEFAULT;
// Initialize the camera:
if (!QCAR::CameraDevice::getInstance().init(camera))
return;
// Configure the video background
configureVideoBackground();
// Select the default mode:
if (!QCAR::CameraDevice::getInstance().selectVideoMode(
QCAR::CameraDevice::MODE_DEFAULT))
return;
// Start the camera:
if (!QCAR::CameraDevice::getInstance().start())
return;
// Start the tracker:
QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
QCAR::Tracker* markerTracker = trackerManager.getTracker(QCAR::Tracker::MARKER_TRACKER);
if(markerTracker != 0)
markerTracker->start();
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_stopCamera(JNIEnv *,
jobject)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_stopCamera");
// Stop the tracker:
QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
QCAR::Tracker* markerTracker = trackerManager.getTracker(QCAR::Tracker::MARKER_TRACKER);
if(markerTracker != 0)
markerTracker->stop();
QCAR::CameraDevice::getInstance().stop();
QCAR::CameraDevice::getInstance().deinit();
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_setProjectionMatrix(JNIEnv *, jobject)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_setProjectionMatrix");
// Cache the projection matrix:
const QCAR::CameraCalibration& cameraCalibration =
QCAR::CameraDevice::getInstance().getCameraCalibration();
projectionMatrix = QCAR::Tool::getProjectionGL(cameraCalibration, 2.0f, 2500.0f);
}
JNIEXPORT jboolean JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_autofocus(JNIEnv*, jobject)
{
return QCAR::CameraDevice::getInstance().setFocusMode(QCAR::CameraDevice::FOCUS_MODE_TRIGGERAUTO) ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT jboolean JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkers_setFocusMode(JNIEnv*, jobject, jint mode)
{
int qcarFocusMode;
switch ((int)mode)
{
case 0:
qcarFocusMode = QCAR::CameraDevice::FOCUS_MODE_NORMAL;
break;
case 1:
qcarFocusMode = QCAR::CameraDevice::FOCUS_MODE_CONTINUOUSAUTO;
break;
case 2:
qcarFocusMode = QCAR::CameraDevice::FOCUS_MODE_INFINITY;
break;
case 3:
qcarFocusMode = QCAR::CameraDevice::FOCUS_MODE_MACRO;
break;
default:
return JNI_FALSE;
}
return QCAR::CameraDevice::getInstance().setFocusMode(qcarFocusMode) ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkersRenderer_initRendering(
JNIEnv* env, jobject obj)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkersRenderer_initRendering");
// Define clear color
glClearColor(0.0f, 0.0f, 0.0f, QCAR::requiresAlpha() ? 0.0f : 1.0f);
// Now generate the OpenGL texture objects and add settings
for (int i = 0; i < textureCount; ++i)
{
char logTxt[100];
sprintf(logTxt, "[assimp] Binding Texture %d", i);
LOG(logTxt);
sprintf(logTxt, "[assimp] Texture ID=%d (%d x %d)", textures[i]->mTextureID, textures[i]->mWidth, textures[i]->mHeight);
LOG(logTxt);
glGenTextures(1, &(textures[i]->mTextureID));
LOG("[assimp] glGenTextures()");
glBindTexture(GL_TEXTURE_2D, textures[i]->mTextureID);
LOG("[assimp] glBindTexture()");
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
LOG("[assimp] glTexParameterf()");
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
LOG("[assimp] glTexParameterf()");
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textures[i]->mWidth,
textures[i]->mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
(GLvoid*) textures[i]->mData);
LOG("[assimp] glTexImage2D()");
}
LOG("[assimp] All textures have been binded");
const aiScene* scene = assimp_getCurrentAiScene();
if (scene) {
// TODO ?
}
shaderProgramID = SampleUtils::createProgramFromBuffer(cubeMeshVertexShader,
cubeFragmentShader);
vertexHandle = glGetAttribLocation(shaderProgramID,
"vertexPosition");
normalHandle = glGetAttribLocation(shaderProgramID,
"vertexNormal");
textureCoordHandle = glGetAttribLocation(shaderProgramID,
"vertexTexCoord");
mvpMatrixHandle = glGetUniformLocation(shaderProgramID,
"modelViewProjectionMatrix");
texSampler2DHandle = glGetUniformLocation(shaderProgramID,
"texSampler2D");
}
JNIEXPORT void JNICALL
Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkersRenderer_updateRendering(
JNIEnv* env, jobject obj, jint width, jint height)
{
LOG("Java_com_qualcomm_QCARSamples_FrameMarkers_FrameMarkersRenderer_updateRendering");
// Update screen dimensions
screenWidth = width;
screenHeight = height;
// Reconfigure the video background
configureVideoBackground();
}
#ifdef __cplusplus
}
#endif