Commit c8fd8f3e authored by Ken Martin's avatar Ken Martin

Add android vr example and fixes to support it

This topic adds a volume rendering example to android builds.  It also
fixes a number of issues to make it work with android including on
Samsung devices which have a different OpenGL driver.

VR use of 1D textures have been replaced with 2D textures as
1D textures are not supported on OpenGL ES.

Some minor build inprovements are included as well.
parent 26594e1a
......@@ -17,7 +17,12 @@ set(OPENGL_ES_VERSION "2.0" CACHE STRING "OpenGL ES version (2.0 or 3.0)")
set_property(CACHE OPENGL_ES_VERSION PROPERTY STRINGS 2.0 3.0)
# Android options
set(ANDROID_NDK "/opt/android-ndk" CACHE PATH "Path to the Android NDK")
# Android options
if (DEFINED ENV{ANDROID_NDK})
set(ANDROID_NDK "$ENV{ANDROID_NDK}" CACHE PATH "Path to the Android NDK")
else()
set(ANDROID_NDK "/opt/android-ndk" CACHE PATH "Path to the Android NDK")
endif()
set(ANDROID_NATIVE_API_LEVEL "21" CACHE STRING "Android Native API Level")
set(ANDROID_ARCH_NAME "arm" CACHE STRING "Target Android architecture")
......@@ -129,6 +134,13 @@ set(android_cmake_flags
-DModule_vtkRenderingFreeType:BOOL=ON
)
# add volume rendering for ES 3.0
if (OPENGL_ES_VERSION STREQUAL "3.0")
set(android_cmake_flags ${android_cmake_flags}
-DModule_vtkRenderingVolumeOpenGL2:BOOL=ON
)
endif()
macro(crosscompile target toolchain_file)
ExternalProject_Add(
${target}
......
//VTK::System::Dec
/*=========================================================================
Program: Visualization Toolkit
......@@ -14,10 +16,6 @@
=========================================================================*/
// this shader implements imposters in OpenGL for Spheres
// The following line handle system declarations such a
// default precisions, or defining precisions to null
//VTK::System::Dec
attribute vec4 vertexMC;
attribute vec2 offsetMC;
......
//VTK::System::Dec
/*=========================================================================
Program: Visualization Toolkit
......@@ -14,10 +16,6 @@
=========================================================================*/
// this shader implements imposters in OpenGL for Spheres
// The following line handle system declarations such a
// default precisions, or defining precisions to null
//VTK::System::Dec
attribute vec4 vertexMC;
attribute vec3 orientMC;
attribute vec4 offsetMC;
......
add_subdirectory(NativeVTK)
add_subdirectory(JavaVTK)
\ No newline at end of file
add_subdirectory(JavaVTK)
if (OPENGL_ES_VERSION STREQUAL "3.0")
add_subdirectory(VolumeRender)
endif()
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kitware.VolumeRender"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:glEsVersion="0x00030000" />
<!-- This is the platform API where NativeActivity was introduced. -->
<uses-sdk android:minSdkVersion="12" />
<application android:label="@string/app_name" >
<activity android:name="VolumeRenderActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:configChanges="orientation|keyboardHidden">
<!-- Tell NativeActivity the name of our .so -->
<meta-data android:name="android.app.lib_name"
android:value="VolumeRender" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->
cmake_minimum_required(VERSION 2.8)
project(VolumeRender)
if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
message(FATAL_ERROR "VTK Android does not support in-source builds :) .")
endif ()
find_package(VTK COMPONENTS
vtkInteractionStyle
vtkRenderingOpenGL2
vtkRenderingVolumeOpenGL2
vtkRenderingFreeType
)
include(${VTK_USE_FILE})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_NDK_ABI_NAME})
message(${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
add_subdirectory(jni)
# find android
find_program(ANDROID_EXECUTABLE
NAMES android
DOC "The android command-line tool")
if(NOT ANDROID_EXECUTABLE)
message(FATAL_ERROR "Can not find android command line tool: android")
endif()
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml"
"${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml"
COPYONLY)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/ant.properties.in"
"${CMAKE_CURRENT_BINARY_DIR}/ant.properties"
@ONLY)
add_custom_target(VolumeRender-ant-configure ALL
COMMAND "${ANDROID_EXECUTABLE}"
update project
--name VolumeRender
--path "${CMAKE_CURRENT_SOURCE_DIR}"
--target "android-${ANDROID_NATIVE_API_LEVEL}"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/build.xml"
"${CMAKE_CURRENT_BINARY_DIR}/build.xml"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/local.properties"
"${CMAKE_CURRENT_BINARY_DIR}/local.properties"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/project.properties"
"${CMAKE_CURRENT_BINARY_DIR}/project.properties"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/proguard-project.txt"
"${CMAKE_CURRENT_BINARY_DIR}/proguard-project.txt"
COMMAND "${CMAKE_COMMAND}" -E remove
"${CMAKE_CURRENT_SOURCE_DIR}/build.xml"
"${CMAKE_CURRENT_SOURCE_DIR}/local.properties"
"${CMAKE_CURRENT_SOURCE_DIR}/project.properties"
"${CMAKE_CURRENT_SOURCE_DIR}/proguard-project.txt"
WORKING_DIRECTORY
"${CMAKE_CURRENT_BINARY_DIR}")
add_dependencies(VolumeRender-ant-configure VolumeRender)
#find ant
find_program(ANT_EXECUTABLE
NAMES ant
DOC "The ant build tool")
if(NOT ANT_EXECUTABLE)
message(FATAL_ERROR "Can not find ant build tool: ant")
endif()
add_custom_target(VolumeRender-apk-release ALL
COMMAND ${ANT_EXECUTABLE}
-file "${CMAKE_CURRENT_BINARY_DIR}/build.xml"
release)
add_dependencies(VolumeRender-apk-release
VolumeRender-ant-configure
VolumeRender)
add_custom_target(VolumeRender-apk-debug ALL
COMMAND ${ANT_EXECUTABLE}
-file "${CMAKE_CURRENT_BINARY_DIR}/build.xml"
debug)
add_dependencies(VolumeRender-apk-debug
VolumeRender-apk-release
VolumeRender-ant-configure
VolumeRender)
builddir=@CMAKE_CURRENT_BINARY_DIR@
srcdir=@CMAKE_CURRENT_SOURCE_DIR@
source.dir=${srcdir}/src
gen.dir=${builddir}/gen
out.dir=${builddir}/bin
asset.dir=${builddir}/assets
resource.absolute.dir=${srcdir}/res
jar.libs.dir=${builddir}/libs
external.libs.dir=${builddir}/libs
native.libs.dir=${builddir}/libs
include_directories(
"${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include"
)
set(sources
main.cxx
)
add_library(VolumeRender SHARED ${sources})
target_link_libraries(VolumeRender ${VTK_LIBRARIES}
android
log
)
/*=========================================================================
Program: Visualization Toolkit
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <jni.h>
#include <errno.h>
#include <sstream>
#include "vtkNew.h"
#include "vtkNrrdReader.h"
#include "vtkImageCast.h"
#include "vtkRTAnalyticSource.h"
#include "vtkOpenGLGPUVolumeRayCastMapper.h"
#include "vtkVolumeProperty.h"
#include "vtkColorTransferFunction.h"
#include "vtkPiecewiseFunction.h"
#include "vtkVolume.h"
#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkConeSource.h"
#include "vtkDebugLeaks.h"
#include "vtkGlyph3D.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkSphereSource.h"
#include "vtkTextActor.h"
#include "vtkTextProperty.h"
#include "vtkImageData.h"
#include "vtkPointData.h"
#include "vtkAndroidRenderWindowInteractor.h"
#include "vtkCommand.h"
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "NativeVTK", __VA_ARGS__))
#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "NativeVTK", __VA_ARGS__))
extern "C" {
JNIEXPORT jlong JNICALL Java_com_kitware_VolumeRender_VolumeRenderLib_init(JNIEnv * env, jobject obj, jint width, jint height);
JNIEXPORT void JNICALL Java_com_kitware_VolumeRender_VolumeRenderLib_render(JNIEnv * env, jobject obj, jlong renWinP);
JNIEXPORT void JNICALL Java_com_kitware_VolumeRender_VolumeRenderLib_onKeyEvent(JNIEnv * env, jobject obj, jlong udp,
jboolean down, jint keyCode, jint metaState, jint repeatCount
);
JNIEXPORT void JNICALL Java_com_kitware_VolumeRender_VolumeRenderLib_onMotionEvent(JNIEnv * env, jobject obj, jlong udp,
jint action,
jint eventPointer,
jint numPtrs,
jfloatArray xPos, jfloatArray yPos,
jintArray ids, jint metaState);
};
struct userData
{
vtkRenderWindow *RenderWindow;
vtkRenderer *Renderer;
vtkAndroidRenderWindowInteractor *Interactor;
};
/*
* Here is where you would setup your pipeline and other normal VTK logic
*/
JNIEXPORT jlong JNICALL Java_com_kitware_VolumeRender_VolumeRenderLib_init(JNIEnv * env, jobject obj, jint width, jint height)
{
vtkRenderWindow *renWin = vtkRenderWindow::New();
char jniS[4] = {'j','n','i',0};
renWin->SetWindowInfo(jniS); // tell the system that jni owns the window not us
renWin->SetSize(width,height);
vtkNew<vtkRenderer> renderer;
renWin->AddRenderer(renderer.Get());
vtkNew<vtkAndroidRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
vtkNew<vtkOpenGLGPUVolumeRayCastMapper> volumeMapper;
#if 0
vtkNew<vtkRTAnalyticSource> wavelet;
wavelet->SetWholeExtent(-127, 128,
-127, 128,
-127, 128);
wavelet->SetCenter(0.0, 0.0, 0.0);
vtkNew<vtkImageCast> ic;
ic->SetInputConnection(wavelet->GetOutputPort());
ic->SetOutputScalarTypeToUnsignedChar();
volumeMapper->SetInputConnection(ic->GetOutputPort());
#else
vtkNew<vtkNrrdReader> mi;
mi->SetFileName("/sdcard/CT-chest-quantized.nrrd");
mi->Update();
double range[2];
mi->GetOutput()->GetPointData()->GetScalars()->GetRange(range);
LOGI("Min %f Max %f type %s", range[0], range[1], mi->GetOutput()->GetScalarTypeAsString());
volumeMapper->SetInputConnection(mi->GetOutputPort());
#endif
volumeMapper->SetAutoAdjustSampleDistances(1);
volumeMapper->SetSampleDistance(0.5);
vtkNew<vtkVolumeProperty> volumeProperty;
volumeProperty->SetShade(1);
volumeProperty->SetInterpolationTypeToLinear();
vtkNew<vtkColorTransferFunction> ctf;
// ctf->AddRGBPoint(90, 0.2, 0.29, 1);
// ctf->AddRGBPoint(157.091, 0.87, 0.87, 0.87);
// ctf->AddRGBPoint(250, 0.7, 0.015, 0.15);
ctf->AddRGBPoint(0, 0, 0, 0);
ctf->AddRGBPoint(255*67.0106/3150.0, 0.54902, 0.25098, 0.14902);
ctf->AddRGBPoint(255*251.105/3150.0, 0.882353, 0.603922, 0.290196);
ctf->AddRGBPoint(255*439.291/3150.0, 1, 0.937033, 0.954531);
ctf->AddRGBPoint(255*3071/3150.0, 0.827451, 0.658824, 1);
// vtkNew<vtkPiecewiseFunction> pwf;
// pwf->AddPoint(0, 0.0);
// pwf->AddPoint(7000, 1.0);
double tweak = 80.0;
vtkNew<vtkPiecewiseFunction> pwf;
pwf->AddPoint(0, 0);
pwf->AddPoint(255*(67.0106+tweak)/3150.0, 0);
pwf->AddPoint(255*(251.105+tweak)/3150.0, 0.3);
pwf->AddPoint(255*(439.291+tweak)/3150.0, 0.5);
pwf->AddPoint(255*3071/3150.0, 0.616071);
volumeProperty->SetColor(ctf.GetPointer());
volumeProperty->SetScalarOpacity(pwf.GetPointer());
vtkNew<vtkVolume> volume;
volume->SetMapper(volumeMapper.GetPointer());
volume->SetProperty(volumeProperty.GetPointer());
renderer->SetBackground2(0.2,0.3,0.4);
renderer->SetBackground(0.1,0.1,0.1);
renderer->GradientBackgroundOn();
renderer->AddVolume(volume.GetPointer());
renderer->ResetCamera();
renderer->GetActiveCamera()->Zoom(1.4);
struct userData *foo = new struct userData();
foo->RenderWindow = renWin;
foo->Renderer = renderer.Get();
foo->Interactor = iren.Get();
return (jlong)foo;
}
JNIEXPORT void JNICALL Java_com_kitware_VolumeRender_VolumeRenderLib_render(JNIEnv * env, jobject obj, jlong udp)
{
struct userData *foo = (userData *)(udp);
foo->RenderWindow->SwapBuffersOff(); // android does it
foo->RenderWindow->Render();
foo->RenderWindow->SwapBuffersOn(); // reset
}
JNIEXPORT void JNICALL Java_com_kitware_VolumeRender_VolumeRenderLib_onKeyEvent(JNIEnv * env, jobject obj, jlong udp,
jboolean down, jint keyCode, jint metaState, jint repeatCount)
{
struct userData *foo = (userData *)(udp);
foo->Interactor->HandleKeyEvent(down, keyCode, metaState, repeatCount);
}
JNIEXPORT void JNICALL Java_com_kitware_VolumeRender_VolumeRenderLib_onMotionEvent(JNIEnv * env, jobject obj, jlong udp,
jint action,
jint eventPointer,
jint numPtrs,
jfloatArray xPos, jfloatArray yPos,
jintArray ids, jint metaState)
{
struct userData *foo = (userData *)(udp);
int xPtr[VTKI_MAX_POINTERS];
int yPtr[VTKI_MAX_POINTERS];
int idPtr[VTKI_MAX_POINTERS];
// only allow VTKI_MAX_POINTERS touches right now
if (numPtrs > VTKI_MAX_POINTERS)
{
numPtrs = VTKI_MAX_POINTERS;
}
// fill in the arrays
jfloat *xJPtr = env->GetFloatArrayElements(xPos, 0);
jfloat *yJPtr = env->GetFloatArrayElements(yPos, 0);
jint *idJPtr = env->GetIntArrayElements(ids, 0);
for (int i = 0; i < numPtrs; ++i)
{
xPtr[i] = (int)xJPtr[i];
yPtr[i] = (int)yJPtr[i];
idPtr[i] = idJPtr[i];
}
env->ReleaseIntArrayElements(ids, idJPtr, 0);
env->ReleaseFloatArrayElements(xPos, xJPtr, 0);
env->ReleaseFloatArrayElements(yPos, yJPtr, 0);
foo->Interactor->HandleMotionEvent(action, eventPointer, numPtrs, xPtr, yPtr, idPtr, metaState);
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">VolumeRender</string>
</resources>
/*=========================================================================
Program: Visualization Toolkit
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kitware.VolumeRender;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import java.io.File;
public class VolumeRenderActivity extends Activity
{
VolumeRenderView mView;
@Override protected void onCreate(Bundle icicle)
{
super.onCreate(icicle);
mView = new VolumeRenderView(getApplication());
this.setContentView(mView);
}
@Override protected void onPause()
{
super.onPause();
this.mView.onPause();
}
@Override protected void onResume()
{
super.onResume();
this.mView.onResume();
}
}
/*=========================================================================
Program: Visualization Toolkit
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kitware.VolumeRender;
import android.view.KeyEvent;
// Wrapper for native library
public class VolumeRenderLib
{
static
{
System.loadLibrary("VolumeRender");
}
/**
* @param width the current view width
* @param height the current view height
*/
public static native long init(int width, int height);
public static native void render(long udp);
public static native void onKeyEvent(long udp, boolean down, int keyCode,
int metaState,
int repeatCount);
public static native void onMotionEvent(long udp,
int action,
int eventPointer,
int numPtrs,
float [] xPos, float [] yPos, int [] ids,
int metaState);
}
/*=========================================================================
Program: Visualization Toolkit
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,