OpenGLによる3D描画とBulletによる物理演算の縫合メモ

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)