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)