Android加载三维模型

这几天在Android项目中需要加载三维模型,找了多种方法,最后决定使用jpct引擎。话不多说,上代码。

一、代码解析

首先创建一个活动MainActivity,活动布局如下:

Android加载三维模型_第1张图片

包括五个button,一个ImageView作为背景,一个GLSurfaceView显示模型,布局代码如下:


    
    
    
    

新建RenderView.class用于加载模型和显示处理:(如果有Unity或者其他游戏引擎的开发经验的话,下面的代码非常好理解。)

public class RenderView implements GLSurfaceView.Renderer{

    public World myWorld;
    private FrameBuffer frameBuffer;
    private Object3D selectedObj;

    public RenderView(Context context) {
        myWorld = new World();
        myWorld.setAmbientLight(25, 25, 25);

        Light light = new Light(myWorld);
        light.setIntensity(250, 250, 250);
        light.setPosition(new SimpleVector(0, 0, -15));

        Camera cam = myWorld.getCamera();
        cam.setFOVLimits(0.1f,2.0f);
        cam.setFOV(1.08f);
        cam.setYFOV(1.92f);
        cam.setClippingPlanes(0f,2000f);
        System.out.println(cam.getFOV());
        System.out.println(cam.getYFOV());
        System.out.println(cam.getPosition());
        String[] names=Config.getParameterNames();
        for(String i:names){
            System.out.println(i);
        }

    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        if (frameBuffer != null) {
            frameBuffer.dispose();
        }
        frameBuffer = new FrameBuffer(gl, w, h);
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        gl.glClearColor(1.0f,1.0f,1.0f,0.3f);
    }

    public void onDrawFrame(GL10 gl) {
        frameBuffer.clear(Color.TRANSPARENT);
        myWorld.renderScene(frameBuffer);
        myWorld.draw(frameBuffer);

        frameBuffer.display();
    }


    public void addObject(Context context) {
        Object3D newObject = null;
        try {
            createTextures(context);
            Object3D[] objectsArray2 = Loader.loadOBJ(context.getResources().getAssets().open("policecar.obj"), context.getResources()
                    .getAssets().open("policecar.mtl"), 1f);
            newObject = Object3D.mergeAll(objectsArray2);
            newObject.setTexture("policecar_texture");
            newObject.setOrigin(new SimpleVector(0, 0, 300));
            newObject.rotateZ(3.1415926f);
            newObject.setName("policecar.obj");
        } catch (IOException e) {
            e.printStackTrace();
        }

        newObject.strip();
        newObject.build();
        Message msg=new Message();
        msg.what=MainActivity.MSG_LOAD_MODEL_SUC;
        msg.obj=newObject;
        MainActivity.handler.sendMessage(msg);
        selectedObj=newObject;
    }
    public void applyTranslation(float incX, float incY, float incZ) {
        if (this.selectedObj != null) {

            SimpleVector objOrigin = this.selectedObj.getOrigin();
            SimpleVector currentPoition=this.selectedObj.getTransformedCenter();
            System.out.println(currentPoition);

            this.selectedObj.translate(incX, incY, incZ);
        }
    }
    private void createTextures(Context context) {

        Bitmap bitmap=BitmapFactory.decodeResource(context.getResources(),R.mipmap.policecar);
        Texture texture = new Texture(bitmap);
        if(!TextureManager.getInstance().containsTexture("policecar_texture")){
            TextureManager.getInstance().addTexture("policecar_texture", texture);
        }
    }
}

 myWorld是当前的场景,所有的物体都在当前场景中,FrameBuffer是显示缓存,selectedObj是当前选择的物体。下面我们来详细解释这些代码。

public RenderView(Context context) {
        myWorld = new World();
        myWorld.setAmbientLight(25, 25, 25);

        Light light = new Light(myWorld);
        light.setIntensity(250, 250, 250);
        light.setPosition(new SimpleVector(0, 0, -15));

        Camera cam = myWorld.getCamera();
        cam.setFOVLimits(0.1f,2.0f);
        cam.setFOV(1.08f);
        cam.setYFOV(1.92f);
        cam.setClippingPlanes(0f,2000f);
        System.out.println(cam.getFOV());
        System.out.println(cam.getYFOV());
        System.out.println(cam.getPosition());
        String[] names=Config.getParameterNames();
        for(String i:names){
            System.out.println(i);
        }

    }

RenderView是类的 构造函数,定义了场景中的一些关键要素,例如摄像机,光照等。myWorld.setAmbientLight设置全局光,Light是当前场景中的光线设置,light.setIntensity设置光照强度,light.setPosition设置光源位置,此处光源是点光源。cam是场景中的摄像机,cam.setFOVLimits设置摄像机的视场角范围,此处设置为0.1-2.0。摄像机的X和Y方向的视场角可以分别设置。cam.setClippingPlanes设置相机的视距,此处为0-2000。

public void onSurfaceChanged(GL10 gl, int w, int h) {
        if (frameBuffer != null) {
            frameBuffer.dispose();
        }
        frameBuffer = new FrameBuffer(gl, w, h);
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        gl.glClearColor(1.0f,1.0f,1.0f,0.3f);
    }

    public void onDrawFrame(GL10 gl) {
        frameBuffer.clear(Color.TRANSPARENT);
        myWorld.renderScene(frameBuffer);
        myWorld.draw(frameBuffer);

        frameBuffer.display();
    }

这几个函数是用于定义GLSurfaceView创建时和刷新时的函数,gl.glClearColor定义了SurfaceView刷新颜色。frameBuffer.clear定义了frameBuffer刷新颜色。

public void addObject(Context context) {
        Object3D newObject = null;
        try {
            createTextures(context);
            Object3D[] objectsArray2 = Loader.loadOBJ(context.getResources().getAssets().open("policecar.obj"), context.getResources()
                    .getAssets().open("policecar.mtl"), 1f);
            newObject = Object3D.mergeAll(objectsArray2);
            newObject.setTexture("policecar_texture");
            newObject.setOrigin(new SimpleVector(0, 0, 300));
            newObject.rotateZ(3.1415926f);
            newObject.setName("policecar.obj");
        } catch (IOException e) {
            e.printStackTrace();
        }

        newObject.strip();
        newObject.build();
        Message msg=new Message();
        msg.what=MainActivity.MSG_LOAD_MODEL_SUC;
        msg.obj=newObject;
        MainActivity.handler.sendMessage(msg);
        selectedObj=newObject;
    }

addObject用于向场景中添加物体,loadOBJ函数采用的是二进制流的形式加载模型数据。加载OBJ格式的模型需要三个文件,分别是模型.obj,模型.mtl以及模型的贴图文件,Obj文件是模型的面信息,mtl文件定义了模型贴图与面位置信息。

public void applyTranslation(float incX, float incY, float incZ) {
        if (this.selectedObj != null) {

            SimpleVector objOrigin = this.selectedObj.getOrigin();
            SimpleVector currentPoition=this.selectedObj.getTransformedCenter();
            System.out.println(currentPoition);

            this.selectedObj.translate(incX, incY, incZ);
        }
    }

此函数用于模型的位移,其中object.translate()函数定义了模型的位移,translate将会将模型位移到当前世界坐标分别加上函数参数之后的坐标。也就是新的坐标=旧坐标+参数。getTransfromedCenter获取当前物体的当前世界坐标。、

 

下面解析MainActivity中的代码

public class MainActivity extends AppCompatActivity{
    public final static int MSG_LOAD_MODEL_SUC=0;


    private GLSurfaceView myGLView;
    private RenderView myRenderer;
    private Button btnLoad;
    private Button btnLeft;
    private Button btnRight;
    private Button btnTop;
    private Button btnDown;

    private Thread threadLoadModel;
    public static Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler=new Handler(){
            @Override
            public void handleMessage(Message msg){
                switch (msg.what){
                    case MSG_LOAD_MODEL_SUC:
                        Toast.makeText(MainActivity.this, "模型加载成功", Toast.LENGTH_SHORT).show();
                        Object3D object3D=(Object3D) msg.obj;
                        myRenderer.myWorld.addObject(object3D);
                        break;
                }
            }
        };
        btnLoad=findViewById(R.id.btnLoadModel);
        btnLeft=findViewById(R.id.btnLeft);
        btnRight=findViewById(R.id.btnRight);
        btnTop=findViewById(R.id.btnTop);
        btnDown=findViewById(R.id.btnDown);

        btnLoad.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainActivity.this, "开始加载模型", Toast.LENGTH_SHORT).show();
                threadLoadModel.start();
            }
        });
        btnLeft.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myRenderer.applyTranslation(-10,0,0);
            }
        });
        btnRight.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myRenderer.applyTranslation(10,0,0);
            }
        });
        btnTop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myRenderer.applyTranslation(0,-10,0);
            }
        });

        btnDown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myRenderer.applyTranslation(0,10,0);
            }
        });

        myGLView = (GLSurfaceView) this.findViewById(R.id.surfaceView);
        myGLView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        myGLView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        myGLView.setZOrderOnTop(true);
        myRenderer = new RenderView(this);
        myGLView.setRenderer(myRenderer);

        threadLoadModel=new Thread(new Runnable() {
            @Override
            public void run() {
                myRenderer.addObject(MainActivity.this);
            }
        });

    }

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

    @Override
    protected void onResume() {
        super.onResume();
        myGLView.onResume();
    }
}

有关View的代码就不多做解析。具体解释一下模型的加载和GLView的设置。

为了使加载模型的过程中程序不堵塞住,开辟一个子线程进行模型解析。模型解析之后,向主线程发送消息,提醒主线程将模型添加到场景中,即myRenderer.myWorld.addObjct()。同时也为了避免模型过大导致加载过程失败导致的程序崩溃。

myGLView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        myGLView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        myGLView.setZOrderOnTop(true);

这段代码将GLSurfaceView的刷新设置为透明并将View设置到所有View的最顶层。

二、jpct的世界坐标系

jpct中的正世界坐标系格式如下,以GlSurfaceView中心为原点,垂直于屏幕向内为z轴正方向,平行于屏幕向右为X轴正方向,向下为Y轴正方向,与Android设备的屏幕xy正方向相一致。

Android加载三维模型_第2张图片

 jpct下载地址:http://www.jpct.net/

项目下载地址:https://download.csdn.net/download/njnutzhou/10968296

你可能感兴趣的:(Android)