提要
3d模型的导入是游戏开发中比较基础的部分了,这里通过jpct这个游戏引擎来加载的,实现起来比较简单。
jpct简介
jPCT是一款基于OpenGL技术开发的3D图形引擎(PC环境为标准OpenGL,Android为OpenGL ES), 以Java语言为基础的,拥有功能强大的Java 3D解决方案。该引擎与LGame(此为2D游戏引擎)相类似,目前拥有PC(J2SE)以及Android两个开发版本。
jPCT的最大优势之一,就在于它惊人的向下兼容性。在PC环境中,jPCT甚至可以运行在JVM1.1环境之中,因为jPCT内部提供的图形渲染接口完 全符合所有的Java 1.1规范(就连已经消失的Microsoft VM乃至更古老的Netscape 4 VM也不例外)。
最低运行环境要求为Android 1.5。
去官网 - http://www.jpct.net/可以下载到打包好的jar文件,还有自带的两个demo。
使用jPCT之前最好把两个demo都跑一下,还有常去查看jpct的doc http://www.jpct.net/jpct-ae/doc/
模型的加载都是基于HelloWorld来修改的。
主要用到的对象及方法
Object3D
表示的是3维物体对象,在渲染的时候,需要添加进World对象。一个Object3D只能被一个world加载依次,调用的方法是 World.addObject() 或者继承式的绑定。
其中有一个 animate(float index) 方法用于md2的关键帧动画。index取值在(0,1),0为第一帧。
Loader
Loader类提供了很多用于加载文件的静态方法,用于加载模型,动作,纹理或者文本。主要用的的方法有:
loadMD2 - 加载md2模型;
load3DS - 加载3DS模型;
loadOBJ - 加载obj模型;
代码实现
首先看MainActivity.java
package com.threed.jpct.example; import java.lang.reflect.Field; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.opengles.GL10; import android.annotation.SuppressLint; import android.app.Activity; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.view.MotionEvent; import com.threed.jpct.Camera; import com.threed.jpct.FrameBuffer; import com.threed.jpct.Light; import com.threed.jpct.Logger; import com.threed.jpct.Object3D; import com.threed.jpct.Primitives; import com.threed.jpct.RGBColor; import com.threed.jpct.SimpleVector; import com.threed.jpct.Texture; import com.threed.jpct.TextureManager; import com.threed.jpct.World; import com.threed.jpct.util.BitmapHelper; import com.threed.jpct.util.MemoryHelper; /** * A simple demo. This shows more how to use jPCT-AE than it shows how to write * a proper application for Android. It includes basic activity management to * handle pause and resume... * * @author EgonOlsen * */ public class MainActivity extends Activity { // Used to handle pause and resume... private static MainActivity master = null; private GLSurfaceView mGLView; private MyRender render = null; private FrameBuffer fb = null; private World world = null; private RGBColor back = new RGBColor(50, 50, 100); private float touchTurn = 0; private float touchTurnUp = 0; private float xpos = -1; private float ypos = -1; private Object3D cube = null; private int fps = 0; private Light sun = null; @SuppressLint("NewApi") protected void onCreate(Bundle savedInstanceState) { Logger.log("onCreate"); if (master != null) { copy(master); } super.onCreate(savedInstanceState); mGLView = new GLSurfaceView(getApplication()); mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() { public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { // Ensure that we get a 16bit framebuffer. Otherwise, we'll fall // back to Pixelflinger on some device (read: Samsung I7500) int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] result = new int[1]; egl.eglChooseConfig(display, attributes, configs, 1, result); return configs[0]; } }); render = new MyRender(this); mGLView.setRenderer(render); setContentView(mGLView); } @Override protected void onPause() { super.onPause(); mGLView.onPause(); } @Override protected void onResume() { super.onResume(); mGLView.onResume(); } @Override protected void onStop() { super.onStop(); } private void copy(Object src) { try { Logger.log("Copying data from master Activity!"); Field[] fs = src.getClass().getDeclaredFields(); for (Field f : fs) { f.setAccessible(true); f.set(this, f.get(src)); } } catch (Exception e) { throw new RuntimeException(e); } } public boolean onTouchEvent(MotionEvent me) { if (me.getAction() == MotionEvent.ACTION_DOWN) { xpos = me.getX(); ypos = me.getY(); return true; } if (me.getAction() == MotionEvent.ACTION_UP) { xpos = -1; ypos = -1; touchTurn = 0; touchTurnUp = 0; render.setTouchTurn(0); render.setTouchTurnUp(0); return true; } if (me.getAction() == MotionEvent.ACTION_MOVE) { float xd = me.getX() - xpos; float yd = me.getY() - ypos; xpos = me.getX(); ypos = me.getY(); touchTurn = xd / -100f; touchTurnUp = yd / -100f; render.setTouchTurn(touchTurn); render.setTouchTurnUp(touchTurnUp); //System.out.println("Move"); return true; } try { Thread.sleep(15); } catch (Exception e) { // No need for this... } return super.onTouchEvent(me); } }
MyRender .java
package com.threed.jpct.example; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.util.Log; import com.threed.jpct.Camera; import com.threed.jpct.FrameBuffer; import com.threed.jpct.Light; import com.threed.jpct.Loader; import com.threed.jpct.Logger; import com.threed.jpct.Matrix; import com.threed.jpct.Object3D; import com.threed.jpct.Primitives; import com.threed.jpct.RGBColor; import com.threed.jpct.SimpleVector; import com.threed.jpct.Texture; import com.threed.jpct.TextureManager; import com.threed.jpct.World; import android.annotation.SuppressLint; import android.content.Context; import com.threed.jpct.util.BitmapHelper; import com.threed.jpct.util.MemoryHelper; @SuppressLint("NewApi") public class MyRender implements GLSurfaceView.Renderer { private long time = System.nanoTime(); private FrameBuffer fb = null; private Light sun = null; private Object3D cube = null; private World world = null; private int fps = 0; private Object3D rockModel; private Object3D chongLou; private Object3D mdModel; private Context mContext; private float touchTurn = 0; private float touchTurnUp = 0; // 行走动画 private int an = 2; private float ind = 0; public MyRender(Context c) { mContext = c; } public void onSurfaceChanged(GL10 gl, int w, int h) { if (fb != null) { fb.dispose(); } fb = new FrameBuffer(gl, w, h); GLES20.glViewport(0, 0, w, h); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); world = new World(); world.setAmbientLight(150, 150, 150); Texture texture = new Texture(BitmapHelper.rescale(BitmapHelper.convert(mContext.getResources().getDrawable(R.drawable.rock)), 64, 64)); Texture texture2 = new Texture(BitmapHelper.rescale(BitmapHelper.convert(mContext.getResources().getDrawable(R.drawable.texture2)), 64, 64)); Texture texture3 = new Texture(BitmapHelper.rescale(BitmapHelper.convert(mContext.getResources().getDrawable(R.drawable.snork)), 64, 64)); TextureManager.getInstance().addTexture("texture", texture); TextureManager.getInstance().addTexture("texture2", texture2); TextureManager.getInstance().addTexture("texture3", texture3); cube =Primitives.getCube(10); cube.calcTextureWrapSpherical(); cube.setTexture("texture"); cube.strip(); cube.build(); rockModel = loadModel("rock.3ds", 1); //rockModel.setTexture("texture"); rockModel.setTexture("texture"); rockModel.strip(); rockModel.build(); rockModel.translate(0, 5, 0); chongLou = loadModel("hu.3ds", 1); chongLou.setTexture("texture2"); chongLou.strip(); chongLou.build(); mdModel = loadMdModel("snork.md2", 0.3f); mdModel.setTexture("texture3"); mdModel.strip(); mdModel.build(); mdModel.translate(-2, 0, 0); //System.out.println(mdModel.getAnimationSequence().getName(1)); world.addObject(rockModel); world.addObject(chongLou); world.addObject(mdModel); sun = new Light(world); sun.setIntensity(250, 250, 250); Camera cam = world.getCamera(); cam.moveCamera(Camera.CAMERA_MOVEOUT, 10); cam.lookAt(cube.getTransformedCenter()); SimpleVector sv = new SimpleVector(); sv.set(cube.getTransformedCenter()); sv.y -= 100; sv.z -= 100; sun.setPosition(sv); MemoryHelper.compact(); } public void onDrawFrame(GL10 gl) { // Clears the screen and depth buffer. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | // OpenGL docs. GL10.GL_DEPTH_BUFFER_BIT); doAnim(); fb.clear(RGBColor.BLACK); world.renderScene(fb); world.draw(fb); fb.display(); if (touchTurn != 0) { rockModel.rotateY(touchTurn); chongLou.rotateY(touchTurn); mdModel.rotateY(touchTurn); touchTurn = 0; } if (touchTurnUp != 0) { rockModel.rotateX(touchTurnUp); chongLou.rotateX(touchTurnUp); mdModel.rotateX(touchTurnUp); touchTurnUp = 0; } if (System.nanoTime() - time >= 1000000000) { Logger.log(fps + "fps"); Log.d("FPSCounter", "fps: " + fps); //System.out.println(fps+"fps"); fps = 0; time = System.nanoTime() ; } // fps++; } public static int loadShader(int type, String shaderCode){ // create a vertex shader type (GLES20.GL_VERTEX_SHADER) // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); // add the source code to the shader and compile it GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } public Object3D loadModel(String filename, float scale){ InputStream is = null; try { is =mContext.getAssets().open(filename); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } Object3D[] model = Loader.load3DS(is, scale); Object3D o3d = new Object3D(0); Object3D temp = null; for (int i = 0; i < model.length; i++) { temp = model[i]; temp.setCenter(SimpleVector.ORIGIN); temp.rotateX((float)( -.5*Math.PI)); temp.rotateMesh(); temp.setRotationMatrix(new Matrix()); o3d = Object3D.mergeObjects(o3d, temp); o3d.build(); } return o3d; } public Object3D loadMdModel(String filename, float scale) { InputStream is = null; try { is =mContext.getAssets().open(filename); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } Object3D model = Loader.loadMD2(is, scale); return model; } public void doAnim() { //每一帧加0.018f ind += 0.018f; if (ind > 1f) { ind -= 1f; } mdModel.animate(ind, an); } public void setTouchTurn(float count) { touchTurn = count; } public void setTouchTurnUp(float count) { touchTurnUp = count; } }
代码还是比较简单 。。。
运行结果:
×××(包含模型)