移动端利用OpenGL展示3D模型文件STL

原创 2016年01月25日 16:48:49

移动端利用OpenGL展示3D模型文件STL

突然发现上次写博客都是一年前了,没养成分享的习惯挺郁闷的,所以分享下个人感觉好玩的东西吧。纯理工科生笔杆子不硬,写的不好,哪里有看不懂的或者写的不好的希望指正讨论。

为了大家有兴趣看下去加点成果图:(本想着录个屏,各种软件都不好使,要是有好的录屏软件求推荐)
个人喜欢这个
愤怒的小鸟

首先我们要掌握以下几个问题

一:什么是STL文件?

stl 文件是在计算机图形应用系统中,用于表示三角形网格的一种文件格式。 它的文件格式非常简单, 应用很广泛。
STL是最多快速原型系统所应用的标准文件类型。STL是用三角网格来表现3D CAD模型。
详细请查询——[百度百科]

实际上我们所要关注只不过是STL的两种展示形式

1.ASCII格式

ASCII码格式的STL文件逐行给出三角面片的几何信息,
每一行以1个或2个关键字开头。
在STL文件中的三角面片的信息单元 facet 是一个带矢量方向的三角面片,STL三维模型就是由一系列这样的三角面片构成。
整个STL文件的首行给出了文件路径及文件名。
在一个 STL文件中,每一个facet由7 行数据组成,
facet normal 是三角面片指向实体外部的法矢量坐标,
outer loop 说明随后的3行数据分别是三角面片的3个顶点坐标,3顶点沿指向实体外部的法矢量方向逆时针排列。
ASCII格式的STL 文件结构如下:

明码://字符段意义
solidfilenamestl//文件路径及文件名
facetnormalxyz//三角面片法向量的3个分量值
outerloop
vertexxyz//三角面片第一个顶点坐标
vertexxyz//三角面片第二个顶点坐标
vertexxyz//三角面片第三个顶点坐标
endloop
endfacet//完成一个三角面片定义

......//其他facet

endsolidfilenamestl//整个STL文件定义结束

2.二进制格式

二进制STL文件用固定的字节数来给出三角面片的几何信息
文件起始的80个字节是文件头,用于存贮文件名;
紧接着用 4 个字节的整数来描述模型的三角面片个数,
后面逐个给出每个三角面片的几何信息。每个三角面片占用固定的50个字节,依次是:
3个4字节浮点数(角面片的法矢量)
3个4字节浮点数(1个顶点的坐标)
3个4字节浮点数(2个顶点的坐标)
3个4字节浮点数(3个顶点的坐标)个
三角面片的最后2个字节用来描述三角面片的属性信息。
一个完整二进制STL文件的大小为三角形面片数乘以 50再加上84个字节。
二进制:

UINT8//Header//文件头
UINT32//Numberoftriangles//三角面片数量
//foreachtriangle(每个三角面片中)
REAL32[3]//Normalvector//法线矢量
REAL32[3]//Vertex1//顶点1坐标
REAL32[3]//Vertex2//顶点2坐标
REAL32[3]//Vertex3//顶点3坐标
UINT16//Attributebytecountend//文件属性统计

二:怎么解析STL文件?

文件的格式比较大 数据包含二进制和ASCII的格式 所以我将文件以字节的形式进行读取

    inputStream = context.getContentResolver().openInputStream(uri);
    stlBytes = IOUtils.toByteArray(inputStream);

IOUtils 只是个读取字节文件的工具类 具体实现我就不在这里展示了 之后会附上可用的源码到时下下来看就行了 ,如果你等不及想直接看 也可以点击这里——[github源码]

获取到字节文件后 就要判断是ASCII还是二进制的
/**
     * checks 'text' in ASCII code
     * 
     * @param bytes
     * @return
     */
    boolean isText(byte[] bytes) {
        for (byte b : bytes) {
            if (b == 0x0a || b == 0x0d || b == 0x09) {
                // white spaces
                continue;
            }
            if (b < 0x20 || (0xff & b) >= 0x80) {
                // control codes
                return false;
            }
        }
        return true;
    }
确定好之后 就要解析啦

ASCII解析

float[] processText(String stlText) throws Exception {
                List<Float> vertexList = new ArrayList<Float>();
                normalList.clear();

                String[] stlLines = stlText.split("\n");
                vertext_size=(stlLines.length-2)/7;
                vertex_array=new float[vertext_size*9];
                normal_array=new float[vertext_size*9];
                progressDialog.setMax(stlLines.length);

                int normal_num=0;
                int vertex_num=0;
                for (int i = 0; i < stlLines.length; i++) {
                    String string = stlLines[i].trim();
                    if (string.startsWith("facet normal ")) {
                        string = string.replaceFirst("facet normal ", "");
                        String[] normalValue = string.split(" ");
                        for(int n=0;n<3;n++){
                            normal_array[normal_num++]=Float.parseFloat(normalValue[0]);
                            normal_array[normal_num++]=Float.parseFloat(normalValue[1]);
                            normal_array[normal_num++]=Float.parseFloat(normalValue[2]);
                        }
                    }
                    if (string.startsWith("vertex ")) {
                        string = string.replaceFirst("vertex ", "");
                        String[] vertexValue = string.split(" ");
                        float x = Float.parseFloat(vertexValue[0]);
                        float y = Float.parseFloat(vertexValue[1]);
                        float z = Float.parseFloat(vertexValue[2]);
                        adjustMaxMin(x, y, z);
                        vertex_array[vertex_num++]=x;
                        vertex_array[vertex_num++]=y;
                        vertex_array[vertex_num++]=z;
                    }

                    if (i % (stlLines.length / 50) == 0) {
                        publishProgress(i);
                    }
                }
                //vertext_size=vertex_array.length;
                return vertex_array;
            }

二进制解析

    float[] processBinary(byte[] stlBytes) throws Exception {

                vertext_size=getIntWithLittleEndian(stlBytes, 80);;
                vertex_array=new float[vertext_size*9];
                normal_array=new float[vertext_size*9];

                progressDialog.setMax(vertext_size);
                for (int i = 0; i < vertext_size; i++) {
                    for(int n=0;n<3;n++){
                        normal_array[i*9+n*3]=Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50));
                        normal_array[i*9+n*3+1]=Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 4));
                        normal_array[i*9+n*3+2]=Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 8));
                    }
                    float x = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 12));
                    float y = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 16));
                    float z = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 20));
                    adjustMaxMin(x, y, z);
                    vertex_array[i*9]=x;
                    vertex_array[i*9+1]=y;
                    vertex_array[i*9+2]=z;

                    x = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 24));
                    y = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 28));
                    z = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 32));
                    adjustMaxMin(x, y, z);
                    vertex_array[i*9+3]=x;
                    vertex_array[i*9+4]=y;
                    vertex_array[i*9+5]=z;

                    x = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 36));
                    y = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 40));
                    z = Float.intBitsToFloat(getIntWithLittleEndian(stlBytes, 84 + i * 50 + 44));
                    adjustMaxMin(x, y, z);
                    vertex_array[i*9+6]=x;
                    vertex_array[i*9+7]=y;
                    vertex_array[i*9+8]=z;

                    if (i % (vertext_size / 50) == 0) {
                        publishProgress(i);
                    }
                }

                return vertex_array;
            }

“vertex_array”这个里面存取的三角形定点信息

“normal_array”这个里面存取的三角形法线信息

其实上述的代码没什么关键的东西,就是对着未见格式进行解析而已。这里我想说一下很多自诩为大神的人总爱说协议基本法什么的,说的好像很牛一样。其实就是定好了规则和格式,知道格式对应解析就行,不用听他们胡扯。之前大学比赛,写过硬件的驱动,最开始就被这帮人吓到过,之后对着规则实现就行没啥吓人的。工作中也碰到这种人,不理他们就行,做好自己的东西就行不用听他们扯。好像说远了,别喷我哦,嘻嘻。

到这里STL文件就解析好了 我们就拿到了包含模型信息的三角形的数据集了(这里插一句,为什么是三角形呢?因为三角形基本形状可以组成任意的图形,当然一般的图形绘制API所支持的也都是三角形)

三:利用OpenGL在移动端上展示STL文件(android为例)

这里为什么说是在android 应为我是写android 当然OpenGl是跨平台的其他平台也是可以展示的所以,IOS的小伙伴只需要找到对应的API替换下就好了

在OpenGL展示使用的缓冲器 所以在用之前我们要将数据转化为对应的数据个数

                ByteBuffer vbb = ByteBuffer.allocateDirect(vertex_array.length * 4);
                vbb.order(ByteOrder.nativeOrder());
                triangleBuffer = vbb.asFloatBuffer();
                triangleBuffer.put(vertex_array);
                triangleBuffer.position(0);
                finishcallback.readstlfinish();

有了对应的数据结构 哈哈 就开始用Opengl展示吧 这里呢我就不过多的写Opengl的东西了,写到这估计都已经快没心情看了,而且OpenGL东西有点多也写不下 ,下一篇我再具体的写写OpenGL的。

回归主题核心的绘制代码如下吧 ,主要数利用OpenGl的GL10这个类 类似于画板一样的东东吧,利用这个东西进行绘制

public void draw(GL10 gl) {
        if (normalList == null || triangleBuffer == null) {
            return;
        }

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
        //gl.glFrontFace(GL10.GL_CCW);
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, triangleBuffer);
        gl.glVertexPointer(GL10.GL_FLOAT,0, normalBuffer);
        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vertext_size*3);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);


    }

上面的代码主要实现的是如下功能

Created with Raphaël 2.1.0开始开启状态器使用状态器进行绘制关闭状态器结束

开启所需要使用的状态器glEnableClientState
设置数据:定点信息和法线信息glVertexPointer,glVertexPointer
设置好之后就利用glDrawArrays开始画了
画完了记得关闭状态器glDisableClientState

到这核心的代码就ok了 其他细节的东西就去看源码吧,欢迎去我的github下载,这个代码都是写了1年多了,不好的地方欢迎大家讨论改正了

项目下载地址https://github.com/zhe8300975/STLShowView

ANDROID应用中嵌入Unity3D视图(展示3D模型)

本文通过一个在Android中展示3D模型的实例,介绍如何将Unity3d以View的形式嵌入到Android应用中。并提供Deom和代码下载。...

Android OpenGL显示任意3D模型文件

前面两篇文章我们介绍了OpenGL相关的基本知识,现在我们已经会绘制基本的图案了,但是还远远不能满足我们的需求。我们要做的是显示任意的模型,这也是本文所要做的事情。在阅读本文之前,请先确保你已经看过我...

【Android界面实现】可旋转的汽车3D模型效果的实现

转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992     今天要给大家介绍的是如何实现可旋转的汽车3D模型。     先看实现效果   ...

Min3D测试-在Android里面载入MMD模型-快速3D模型显示测试-By黑月君

快速在Android里面实现3D人物显示 一直在研究Android上实现女仆美豆酱(个人助理)的研究,在制作完毕短信和电话提醒功能后,希望可以实现3D人物的快速显示 至少可以独立显示模型及其动作,主要...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

iOS显示3D模型,iOS和U3D混编(互相跳转)

之前做一个项目时,需要一个界面展示3D模型并进行交互。所以针对这个问题研究了很久,并总结了一些经验。在论坛中总遇到相同需求的人,也有很多人问我,所以写出这篇博客供大家参考。网上资料很少,即时有也比较模...

Android OpenGL ES显示3D模型

这篇文章我们来来看如何将一个STL文件显示出来,把STL文件显示出来,那么我们就可以显示任意的3D模型了。下面是显示一把狙击枪的效果图:  什么是STL文件网上的解释是这样的:.stl 文件是在计算...
  • zxc123e
  • zxc123e
  • 2016年12月12日 17:35
  • 2468

【Unity3D自学记录】Android应用中嵌入Unity3D视图(展示3D模型)

效果展示:                 开篇废话: 我现在所在的Team每周需要一个人给大家介绍一个知识点,或者新技术。这礼拜正好轮到我...
  • hackdjh
  • hackdjh
  • 2014年08月22日 15:03
  • 3229

Opengl教程之读取stl文件并绘制在picturecontrol控件内

Opengl教程之读取stl并绘制在picturecontrol控件内 By Cracent 作为机械专业方向的人员,我们经常需要用SolidWorks构建模型。有些时候我们被要求将这些模型通过OPE...
  • Cracent
  • Cracent
  • 2016年04月03日 10:30
  • 5674

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:移动端利用OpenGL展示3D模型文件STL
举报原因:
原因补充:

(最多只允许输入30个字)