Hatena::ブログ(Diary)

麗ちゃんとママの実験ノート

2011-01-14

[][][][] OpenGLによる3D描画とBulletによる物理演算の縫合メモ 07:06

OpenGL立方体描画、自然落下をBulletで物理演算処理するサンプル。(一部省略&略式コード)

物理演算処理は、当然描画系メインスレッドとは別のスレッドで。下記コードではAsyncTestTaskでバックグラウンド処理として行っているが、コメントアウトしているHandlerを使っても同じ動作になる。

いろんなポリゴンテクスチャ貼って組み合わせて、衝突や拘束(振り子)なんかの演算も使えば、OpenGL描画系とBullet物理演算系による3Dゲームなど面白いものも作成できる。

1.ネイティブをコールするAndroid側コード(Java)

・TestActivity.java

package chiaki.test.cube;

import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;

public class TestActivity extends Activity {

	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mGLSurfaceView = new TestSurfaceView(this);
        setContentView(mGLSurfaceView);
        mGLSurfaceView.requestFocus();
        mGLSurfaceView.setFocusableInTouchMode(true);
    }
	
    @Override
    protected void onResume() {
        super.onResume();
        mGLSurfaceView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLSurfaceView.onPause();
    }

    private GLSurfaceView mGLSurfaceView;
    
	protected void onStop() {
		super.onStop();
	}

}

class TestSurfaceView extends GLSurfaceView {

    public TestSurfaceView(Context context) {
        super(context);
        mRenderer = new CubeRenderer();
        setRenderer(mRenderer);
    }

    private class CubeRenderer implements GLSurfaceView.Renderer {

        public CubeRenderer() {
            mCube = new Cube();
        }

        public void onDrawFrame(GL10 gl) {

            gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

            gl.glMatrixMode(GL10.GL_MODELVIEW);
            gl.glLoadIdentity();
            gl.glTranslatef(0, 0, -3.0f);

            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
            
            mCube.draw(gl);
        }

        public int[] getConfigSpec() {
            int[] configSpec = {
                    EGL10.EGL_DEPTH_SIZE,   16,
                    EGL10.EGL_NONE
            };
            return configSpec;
        }

        public void onSurfaceChanged(GL10 gl, int width, int height) {
             gl.glViewport(0, 0, width, height);

             float ratio = (float) width / height;
             gl.glMatrixMode(GL10.GL_PROJECTION);
             gl.glLoadIdentity();
             gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
        }

        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            gl.glDisable(GL10.GL_DITHER);

             gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
                     GL10.GL_FASTEST);


             gl.glClearColor(1,1,1,1);
             gl.glEnable(GL10.GL_CULL_FACE);
             gl.glShadeModel(GL10.GL_SMOOTH);
             gl.glEnable(GL10.GL_DEPTH_TEST);
        }
        public Cube mCube;
        
    }

    private CubeRenderer mRenderer;

}

Cube.java(描画&ネイティブを呼び出す部分の実装)

package chiaki.test.cube;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.concurrent.ScheduledExecutorService;

import javax.microedition.khronos.opengles.GL10;

import android.os.AsyncTask;
import android.os.Handler;

class Cube
{
	  static{
			System.loadLibrary("Cube");
		  }
  	Handler handler = new Handler();
	private ScheduledExecutorService srv;

    public Cube()
    {
        int one = 0x10000;
        int vertices[] = {
                -one/4, 6*one/4, -one/4,
                one/4, 6*one/4, -one/4,
                one/4,  8*one/4, -one/4,
                -one/4,  8*one/4, -one/4,
                -one/4, 6*one/4,  one/4,
                one/4, 6*one/4,  one/4,
                one/4,  8*one/4,  one/4,
                -one/4,  8*one/4,  one/4,
        };
        int colors[] = {
                0,    0,    0,  one,
                one,    0,    0,  one,
                one,  one,    0,  one,
                0,  one,    0,  one,
                0,    0,  one,  one,
                one,    0,  one,  one,
                one,  one,  one,  one,
                0,  one,  one,  one,
        };

        byte indices[] = {
                0, 4, 5,    0, 5, 1,
                1, 5, 6,    1, 6, 2,
                2, 6, 7,    2, 7, 3,
                3, 7, 4,    3, 4, 0,
                4, 7, 6,    4, 6, 5,
                3, 0, 1,    3, 1, 2
        };

        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        mVertexBuffer = vbb.asIntBuffer();
        mVertexBuffer.put(vertices);
        mVertexBuffer.position(0);

        ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
        cbb.order(ByteOrder.nativeOrder());
        mColorBuffer = cbb.asIntBuffer();
        mColorBuffer.put(colors);
        mColorBuffer.position(0);

        mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
        mIndexBuffer.put(indices);
        mIndexBuffer.position(0);
        
/*       
        new Thread(new Runnable() {
          public void run() {
		      simulate();
              handler.post(new Runnable() {
                public void run() {
                }
              });
            }
          }).start();
*/
        AsyncTestTask task = new AsyncTestTask();  
        task.execute();  
    }
    public class AsyncTestTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected void onPreExecute() {
        }

		@Override
		protected Void doInBackground(Void... params) {
			// TODO Auto-generated method stub
        	simulate();
			return null;
		}
     }
    public void simulate(){
    	simulate(this);
    }
	public native void simulate(Cube cube);            

    public void draw(GL10 gl)
    {
        gl.glFrontFace(gl.GL_CW);
        gl.glVertexPointer(3, gl.GL_FIXED, 0, mVertexBuffer);
        gl.glColorPointer(4, gl.GL_FIXED, 0, mColorBuffer);
        gl.glDrawElements(gl.GL_TRIANGLES, 36, gl.GL_UNSIGNED_BYTE, mIndexBuffer);

        setvertex();
    }

    private IntBuffer   mVertexBuffer;
    private IntBuffer   mColorBuffer;
    private ByteBuffer  mIndexBuffer;

    public void setvertex()
    {
        int one = 0x10000;
        int y = (int)float_y;
        
        int vertices[] = {
                -one/4, 6*one/4+y, -one/4,
                one/4, 6*one/4+y, -one/4,
                one/4,  8*one/4+y, -one/4,
                -one/4,  8*one/4+y, -one/4,
                -one/4, 6*one/4+y,  one/4,
                one/4, 6*one/4+y,  one/4,
                one/4,  8*one/4+y,  one/4,
                -one/4,  8*one/4+y,  one/4,
        };
        
        int colors[] = {
                0,    0,    0,  one,
                one,    0,    0,  one,
                one,  one,    0,  one,
                0,  one,    0,  one,
                0,    0,  one,  one,
                one,    0,  one,  one,
                one,  one,  one,  one,
                0,  one,  one,  one,
        };

        byte indices[] = {
                0, 4, 5,    0, 5, 1,
                1, 5, 6,    1, 6, 2,
                2, 6, 7,    2, 7, 3,
                3, 7, 4,    3, 4, 0,
                4, 7, 6,    4, 6, 5,
                3, 0, 1,    3, 1, 2
        };

        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());
        mVertexBuffer = vbb.asIntBuffer();
        mVertexBuffer.put(vertices);
        mVertexBuffer.position(0);

        ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
        cbb.order(ByteOrder.nativeOrder());
        mColorBuffer = cbb.asIntBuffer();
        mColorBuffer.put(colors);
        mColorBuffer.position(0);

        mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
        mIndexBuffer.put(indices);
        mIndexBuffer.position(0);
    }
    public float float_y;    
}

2.Bullet側のネイティブコード(C++)

・chiaki_test_cube_Cube.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class chiaki_test_cube_Cube */

#ifndef _Included_chiaki_test_cube_Cube
#define _Included_chiaki_test_cube_Cube
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     chiaki_test_cube_Cube
* Method:    simulate
* Signature: 
*/
JNIEXPORT void JNICALL Java_chiaki_test_cube_Cube_simulate
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

Cube.cpp

#include "chiaki_test_cube_Cube.h"
#include "btBulletDynamicsCommon.h"

JNIEXPORT void JNICALL Java_chiaki_test_cube_Cube_simulate
(JNIEnv *env, jobject cube){

	int i;

	btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration();

	btCollisionDispatcher* dispatcher = new	btCollisionDispatcher(collisionConfiguration);

	btBroadphaseInterface* overlappingPairCache = new btDbvtBroadphase();

	btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;

	btDiscreteDynamicsWorld* dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher,overlappingPairCache,solver,collisionConfiguration);

	dynamicsWorld->setGravity(btVector3(0,-10,0));

	btCollisionShape* groundShape = new btBoxShape(btVector3(btScalar(50.),btScalar(50.),btScalar(50.)));

	btAlignedObjectArray<btCollisionShape*> collisionShapes;

	collisionShapes.push_back(groundShape);

	btTransform groundTransform;
	groundTransform.setIdentity();
	groundTransform.setOrigin(btVector3(0,-56,0));

	{
		btScalar mass(0.);

		bool isDynamic = (mass != 0.f);

		btVector3 localInertia(0,0,0);
		if (isDynamic)
			groundShape->calculateLocalInertia(mass,localInertia);

		btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform);
		btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,groundShape,localInertia);
		btRigidBody* body = new btRigidBody(rbInfo);

		dynamicsWorld->addRigidBody(body);
	}


	{

		btCollisionShape* colShape = new btSphereShape(btScalar(1.));
		collisionShapes.push_back(colShape);

		btTransform startTransform;
		startTransform.setIdentity();

		btScalar	mass(1.f);

		bool isDynamic = (mass != 0.f);

		btVector3 localInertia(0,0,0);
		if (isDynamic)
			colShape->calculateLocalInertia(mass,localInertia);

			startTransform.setOrigin(btVector3(2,10,0));
		
			btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
			btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,colShape,localInertia);
			btRigidBody* body = new btRigidBody(rbInfo);

			dynamicsWorld->addRigidBody(body);
	}




	jclass clazz = env->GetObjectClass(cube);
	jfieldID fid = env->GetFieldID(clazz, "float_y", "F");

	float k = 0.0;
	while(1)
	{
		dynamicsWorld->stepSimulation(1.f/60.f,10);
		
		for (int j=dynamicsWorld->getNumCollisionObjects()-1; j>=0 ;j--)
		{
			btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[j];
			btRigidBody* body = btRigidBody::upcast(obj);
			if (body && body->getMotionState())
			{
				btTransform trans;
				body->getMotionState()->getWorldTransform(trans);


				float f = float(trans.getOrigin().getY());
				k=k+f;
				env->SetFloatField(cube, fid, k);
			}
		}
	}

	for (i=dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
	{
		btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
		btRigidBody* body = btRigidBody::upcast(obj);
		if (body && body->getMotionState())
		{
			delete body->getMotionState();
		}
		dynamicsWorld->removeCollisionObject( obj );
		delete obj;
	}

	for (int j=0;j<collisionShapes.size();j++)
	{
		btCollisionShape* shape = collisionShapes[j];
		collisionShapes[j] = 0;
		delete shape;
	}

	delete dynamicsWorld;

	delete solver;

	delete overlappingPairCache;

	delete dispatcher;

	delete collisionConfiguration;

	collisionShapes.clear();

}

3.AndroidMakeファイル

# 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,
# 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.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

CFLAGS := -Werror

LOCAL_MODULE    := libCube

LOCAL_C_INCLUDES := \
        $(LOCAL_PATH)/ \
        $(LOCAL_PATH)/BulletCollision/BroadphaseCollision \
        $(LOCAL_PATH)/BulletCollision/CollisionDispatch \
        $(LOCAL_PATH)/BulletCollision/CollisionShapes \
        $(LOCAL_PATH)/BulletCollision/NarrowPhaseCollision \
        $(LOCAL_PATH)/BulletDynamics/ConstraintSolver \
        $(LOCAL_PATH)/BulletDynamics/Dynamics \
        $(LOCAL_PATH)/BulletDynamics/Vehicle \
        $(LOCAL_PATH)/LinearMath

LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%)
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -Lbullet/libs -ldl -lm -llog

LOCAL_SRC_FILES := \
        BulletCollision/BroadphaseCollision/btAxisSweep3.cpp \
        BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp \
        BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp \
        BulletCollision/BroadphaseCollision/btDbvt.cpp \
        BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp \
        BulletCollision/BroadphaseCollision/btDispatcher.cpp \
        BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp \
        BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp \
        BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp \
        BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp \
        BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp \
        BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp \
        BulletCollision/CollisionDispatch/btCollisionObject.cpp \
        BulletCollision/CollisionDispatch/btCollisionWorld.cpp \
        BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp \
        BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btGhostObject.cpp \
        BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp \
        BulletCollision/CollisionDispatch/btManifoldResult.cpp \
        BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp \
        BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp \
        BulletCollision/CollisionDispatch/btUnionFind.cpp \
        BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp \
        BulletCollision/CollisionShapes/btBox2dShape.cpp \
        BulletCollision/CollisionShapes/btBoxShape.cpp \
        BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp \
        BulletCollision/CollisionShapes/btCapsuleShape.cpp \
        BulletCollision/CollisionShapes/btCollisionShape.cpp \
        BulletCollision/CollisionShapes/btCompoundShape.cpp \
        BulletCollision/CollisionShapes/btConcaveShape.cpp \
        BulletCollision/CollisionShapes/btConeShape.cpp \
        BulletCollision/CollisionShapes/btConvex2dShape.cpp \
        BulletCollision/CollisionShapes/btConvexHullShape.cpp \
        BulletCollision/CollisionShapes/btConvexInternalShape.cpp \
        BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp \
        BulletCollision/CollisionShapes/btConvexShape.cpp \
        BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp \
        BulletCollision/CollisionShapes/btCylinderShape.cpp \
        BulletCollision/CollisionShapes/btEmptyShape.cpp \
        BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp \
        BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp \
        BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp \
        BulletCollision/CollisionShapes/btMultiSphereShape.cpp \
        BulletCollision/CollisionShapes/btOptimizedBvh.cpp \
        BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp \
        BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp \
        BulletCollision/CollisionShapes/btShapeHull.cpp \
        BulletCollision/CollisionShapes/btSphereShape.cpp \
        BulletCollision/CollisionShapes/btStaticPlaneShape.cpp \
        BulletCollision/CollisionShapes/btStridingMeshInterface.cpp \
        BulletCollision/CollisionShapes/btTetrahedronShape.cpp \
        BulletCollision/CollisionShapes/btTriangleBuffer.cpp \
        BulletCollision/CollisionShapes/btTriangleCallback.cpp \
        BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp \
        BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp \
        BulletCollision/CollisionShapes/btTriangleMesh.cpp \
        BulletCollision/CollisionShapes/btTriangleMeshShape.cpp \
        BulletCollision/CollisionShapes/btUniformScalingShape.cpp \
        BulletCollision/Gimpact/btContactProcessing.cpp \
        BulletCollision/Gimpact/btGenericPoolAllocator.cpp \
        BulletCollision/Gimpact/btGImpactBvh.cpp \
        BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp \
        BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp \
        BulletCollision/Gimpact/btGImpactShape.cpp \
        BulletCollision/Gimpact/btTriangleShapeEx.cpp \
        BulletCollision/Gimpact/gim_box_set.cpp \
        BulletCollision/Gimpact/gim_contact.cpp \
        BulletCollision/Gimpact/gim_memory.cpp \
        BulletCollision/Gimpact/gim_tri_collision.cpp \
        BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp \
        BulletCollision/NarrowPhaseCollision/btConvexCast.cpp \
        BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp \
	BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp \
        BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp \
        BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp \
        BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp \
        BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp \
        BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp \
        BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp \
        BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp \
        BulletDynamics/Character/btKinematicCharacterController.cpp \
        BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp \
        BulletDynamics/ConstraintSolver/btContactConstraint.cpp \
        BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp \
        BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp \
        BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp \
        BulletDynamics/ConstraintSolver/btHingeConstraint.cpp \
        BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp \
        BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp \
        BulletDynamics/ConstraintSolver/btSliderConstraint.cpp \
        BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp \
        BulletDynamics/ConstraintSolver/btTypedConstraint.cpp \
        BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp \
        BulletDynamics/Dynamics/btContinuousDynamicsWorld.cpp \
        BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp \
        BulletDynamics/Dynamics/btRigidBody.cpp \
        BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp \
        BulletDynamics/Dynamics/Bullet-C-API.cpp \
        BulletDynamics/Vehicle/btRaycastVehicle.cpp \
        BulletDynamics/Vehicle/btWheelInfo.cpp \
        LinearMath/btAlignedAllocator.cpp \
        LinearMath/btConvexHull.cpp \
        LinearMath/btGeometryUtil.cpp \
        LinearMath/btQuickprof.cpp \
        LinearMath/btSerializer.cpp \
	Cube.cpp

include $(BUILD_SHARED_LIBRARY)

momomonimomomoni 2011/01/15 00:29 すばらしいサンプル!。
コード参考にさせていただきます。
私のXperiaで動かしてみようと思います。

chiakisugimotochiakisugimoto 2011/01/17 13:12 コメントありがとうございます。
サンプルは、もう少し複雑な処理を行う手元の実験コードを、目視で掲載用にシンプルに変更したもので、動作させるには手をくわえる必要がありましたため、そのままでも、掲載のシンプルなイメージで動作するコードに少し修正しました。(OpenGL描画系とBullet処理系の縫合メモが目的なので、細かい処理のフォロー等はしていません)
ビルドで「はまる点」としては、ネイティブのコンパイル時、デバイス側のsystemにあるライブラリを開発環境に持ってきてコンパイルするので、デバイスによっては、「あるデバイス用」にビルドしたネイティブのライブラリを別デバイスで動作させることが出来ない場合もあると思います(そこがNDKを使う場合の移植性の課題です)。その場合デバイス毎に同じソースでビルドする必要があります。