关于java解析bvh动作文件

来源:互联网 发布:pi开关电源设计软件 编辑:程序博客网 时间:2024/06/07 19:56

最近正在学习安卓openGL(其实各个平台相差不多),为了让人物模型上来自己动,而且不要动的那么露骨,我就在网上找现成的动作数据想将它绑定到模型对象上,搜了一下才发现原来这种数据有几种专门的文件格式来存储的,我就点了百度出来的第一个结果(果断入坑),我点的这篇文章介绍的是bvh,本来想找个现成的解析器来用,但是一个都没有,哎,,只好自己发功了(大家闪开,我要装b了)。
果断百度bvh的文件格式。

注意//并不是bvh文件的注释这样写只是为了方便说明

//---------------------bvh文件HIERARCHY //必须的关键字ROOT rootName{//听说这个括号按bvh标准必须另起一行    OFFSET 0 0 0 //相对父节点的坐标(x,y,z) 因为是根节点所以一般全为0    CHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation    JOINT jointName{        OFFSET 0 1 0 //相对父节点的坐标        CHANNELS 3 Xrotation Yrotation Zrotation        ......递归joint            End Site{//此节点只有offset一个字段                OFFSET 0 0 1            }        ......递归回来    }}Frames: 50 //本文件包含了多少帧数据Frame Time: 0.033333 //每帧间隔(是秒。。。。)MOTION0 1 3 3 2 4.....//数据内容//------------------文件结束

为了通俗易懂咱们不说什么矩阵运算什么的,也没有必要。

节点描述部分
OFFSET 只指出了每个节点相对父节点的坐标,数据部分又只给出根节点的位置偏移和每个节点(End Site节点除外)相对父节点的旋转角度,因此无论节点如何旋转节点相对父节点的距离是不会发生变化的,也就不会出现胳膊被拉长什么的惊悚事件了
CHANNELS 是一个很重要的字段
每个节点有几个channel值每帧就有几条数据,如

Root hip{    OFFSET 0 0 0    CHANNELS 6 xposition zposition yposition yrotation xrotation zrotation}

则hip节点每帧有6条数据 0:x坐标 1:z坐标 2:y坐标 3:y旋转角度 4:x旋转角度 5:z旋转角度
我故意没有按照x,y,z的顺序写是想突出一下channels定义顺序的重要性,因为数据段是按照channels的定义顺序来排列的,bvh的传统做法是使用z,x,y的顺序,有的文件却是以x,y,z的顺序出现的(就是有人这么的放纵不羁)。
注意Root节点是可以有多个的,但我下载的所有文件都只有一个Root节点,我的解析器也是以一个Root节点来解析的。Channels也可以有多个,我的解析器同样只允许一个(同样没有见过一个以上的)。
而每个节点在数据部分的位置又是以其出现顺序而定的,如有主从关系的节点结构如下:

  - hip       - chest          - neck              - head              - head2//惊喜不可怕不      - lcolar          - lhand      - rcolar          - rhand      - lForeLeg          - lLeg      - rForeLeg          - rLeg

则数据段每个节点的数据按照上图从上到下的顺序从左向右出现。
bb了这么多,是时候表演真正的技术了,关门放代码。
慢着容我再bb两句
解析过程我感觉有点慢,亲测(红米note2)740k多点的文件需要4秒左右,1m以上的话就慢的难以忍受了,如有什么效率更高的办法希望各位不吝高见,慷慨陈辞,当头棒喝,发给本猿
Segment.calcBest()方法本来是想给各位客观找一个好的位子来看自己,但找到的位置并不好瞎将就吧。
真的要放代码了
Parser.java

import java.util.zip.*;import java.util.*;import java.io.*;/*** 此类用于读取文件并对文件进行一定程度的格式化访问*/public class Parser{    RandomAccessFile in;    public Parser(String filePath)throws IOException,FileNotFoundException{        this.in=new RandomAccessFile(filePath,"r");    }    //    public char readChar()throws IOException{        return (char)in.readByte();    }    public void close(){       try{            in.close();       }catch(Exception e){            e.printStackTrace();       }    }    public String readLine()throws IOException{        return in.readLine();    }    public void skipBytes(int i)throws IOException{        if(i<0){            in.seek(in.getFilePointer()+i);        }else{            in.skipBytes(i);        }    }    //读取完成后光标将位于endFlag之后    //返回的字符串不会包含endFlag    //convertFlag 第一个字符为转意字符 后面是所有允许转意的字符    public String readString(char endFlag, char[]convertFlag)throws IOException,DataFormatException{        StringBuilder tmp=new StringBuilder();        boolean flag=true;        char c;        while(flag){            c=readChar();            if(c==endFlag){                break;            }            if(convertFlag!=null){                if(c==convertFlag[0]){                    tmp.append(c);                    c=readChar();                    if(Arrays.binarySearch(convertFlag,c)>0){                        tmp.append(c);                    }else{                        throw new DataFormatException("\""+tmp+"\"之后出现非法转意符");                    }                }            }else{                tmp.append(c);            }        }        return tmp.toString();    }    //如果没有读取到任何字符将抛出异常    //读取到非允许的字符后将返回,指针将指向结尾的字符后面    public String readWord(char...enable)throws IOException,DataFormatException{        StringBuilder tmp=new StringBuilder();        while(true){            char c=readChar();            if(Character.isLetter(c)||Character.isDigit(c)||c=='_'||c=='$'||               (enable!=null&&Arrays.binarySearch(enable,c)!=-1)){                tmp.append(c);            }else{                //将指针回退一个位置                skipBytes(-1);                if(tmp.length()==0){                    throw new DataFormatException("没有读取到任何字符");                }                break;            }        }        return tmp.toString();    }    //默认跳过所有空字符    //可以指定disable来阻止这种行为    public void skipBlank(char...disable)throws IOException{        if(disable!=null){            while(true){                char c=readChar();                if(Arrays.binarySearch(disable,c)!=-1){                    break;                }                if(!Character.isWhitespace(c)){                    break;                }            }        }else{            while(true){                char c=readChar();                if(!Character.isWhitespace(c)){                    break;                }            }        }        skipBytes(-1);    }    //如果没有发现任何空字符则抛出异常    //将指向空白字符之后的第一个位置    public void skipBlankRequired(char...disable)throws IOException,DataFormatException{        boolean flag=true;        if(disable!=null){//避免重复判断            while(true){                char c=readChar();                if(Arrays.binarySearch(disable,c)!=-1){                    break;                }                if(!Character.isWhitespace(c)){                    break;                }                flag=false;            }        }else{            while(true){                char c=readChar();                if(!Character.isWhitespace(c)){                    break;                }                flag=false;            }        }        if(flag){            throw new DataFormatException("没有发现必须的空白字符");        }        skipBytes(-1);//回退一个位置    }    //如果没有出现空格将抛出异常    public void skipSpaceOnly()throws DataFormatException,IOException{        boolean flag=true;        while(readChar()==' '){            flag=false;        }        if(flag){            throw getError("必须的空格没有出现");        }        skipBytes(-1);    }    //跳过指定字符串之前的所有数据    //跳过后光标将指向endFlag之后的位置    public void skipUntill(String endFlag)throws IOException,DataFormatException{        char[]cs=endFlag.toCharArray();        boolean flag=true;        while(true){            char c=readChar();            if(c==cs[0]){                for(int i=1;i<cs.length;i++){                    c=readChar();                    if(c!=cs[i]){                        //跳回cs[0]之后的位置                        skipBytes(-i+1);                        flag=false;                        break;                    }                }                if(flag){                    break;                }                flag=true;            }        }        if(!flag){            throw new DataFormatException("没有找到指定目标串:"+endFlag);        }    }    public String readNumber()throws IOException,DataFormatException,NumberFormatException{        StringBuilder tmp=new StringBuilder();        while(true){            char c=readChar();            if(Character.isDigit(c)||c=='-'||c=='.'){                tmp.append(c);            }else{                //将指针回退一个位置                skipBytes(-1);                if(tmp.length()==0){                    throw new DataFormatException("无法从文件中读取数字,没有读取到任何字符");                }                break;            }        }        return tmp.toString();    }    public boolean requiredKeyWord(String key)throws IOException,DataFormatException{        for(int i=0;i<key.length();i++){            if(key.charAt(i)!=readChar()){                throw getError("指定的关键字没有找到:"+key);            }        }        return true;    }    //如果下一个单词是给出的单词将返回true并将指针移动到key之后的字符上    //否则指针指回调用此函数之前的位置并返回false    public boolean isNextString(String key)throws IOException{        for(int i=0;i<key.length();i++){            if(key.charAt(i)!=readChar()){                skipBytes(-i-1);                return false;            }        }        return true;    }    private DataFormatException getError(String msg)throws IOException{        return new DataFormatException(msg+",Position:"+in.getFilePointer());    }    public char testChar()throws IOException{        char c=(char)in.readByte();        skipBytes(-1);        return c;    }}

ActionReader.java

import java.util.*;import java.net.*;import java.io.*;import java.util.zip.*;import java.nio.channels.*;public class ActionReader{    /*使用示例    public static void main(String[] args){        try{            Parser p=new Parser("/storage/emulated/0/#c/action/backflip_583ad.bvh");            ActionReader m=new ActionReader();            long b=System.currentTimeMillis();            for(int i=0;i<1;i++){                p.in.seek(0);                Segment s=new Segment();                m.read(p,s);            }            System.out.println(System.currentTimeMillis()-b);        }catch(IOException e){            e.printStackTrace();        }catch(DataFormatException e){            e.printStackTrace();        }    }    */    //返回第一个值为总帧数,第二个值为帧间隔    public float[] read(Parser p,Segment s)        throws IOException,DataFormatException{        p.requiredKeyWord("HIERARCHY");        p.skipBlank();        //暂时只支持一个Root节点的情况        p.requiredKeyWord("ROOT");        readSegment(p,s,0);        p.skipBlank();        p.requiredKeyWord("MOTION");        p.skipBlankRequired();        p.requiredKeyWord("Frames:");        p.skipBlankRequired();        int frames=Integer.parseInt(p.readNumber());        p.skipBlankRequired();        p.requiredKeyWord("Frame Time:");        p.skipSpaceOnly();        String time=p.readNumber();        //p.skipBlankRequired();//由数据读取方法跳过        for(int i=0;i<frames;i++){            readLineData(p,s);        }        //Loger.l("root data length:"+s.data.size());        float[]result={frames,Float.parseFloat(time)};        return result;    }    //此函数预期的指针位置为ROOT或JOINT关键字之后    public boolean readSegment(Parser p,Segment s,int iii)throws IOException,DataFormatException{        p.skipBlankRequired();        String str=p.readWord();        s.name=str;        p.skipBlank();        p.requiredKeyWord("{");        p.skipBlank();        p.requiredKeyWord("OFFSET");        p.skipBlankRequired();        float x=Float.parseFloat(p.readNumber());        p.skipBlankRequired();        float y=Float.parseFloat(p.readNumber());        p.skipBlankRequired();        float z=Float.parseFloat(p.readNumber());        p.skipBlankRequired();        s.line.put(new float[]{x,y,z});        s.line.position(0);        //此数组的作用为告知data读取方法当前位置读取的数据应该放到数据数组的第几位        byte[]chans=s.chanels=new byte[6];        //只支持一个通道的情况        if(p.isNextString("CHANNELS")){            p.skipSpaceOnly();            //要求通道数必须为36            int channelNum=s.dataLen=(byte)Integer.parseInt(p.readNumber());            for(int i=0;i<channelNum;i++){                p.skipSpaceOnly();                String channel=p.readWord();                if(channel.equalsIgnoreCase("xrotation")){                    chans[i]=0;                }else if(channel.equalsIgnoreCase("yrotation")){                    chans[i]=1;                }else if(channel.equalsIgnoreCase("zrotation")){                    chans[i]=2;                }else if(channel.equalsIgnoreCase("xposition")){                    chans[i]=3;                }else if(channel.equalsIgnoreCase("yposition")){                    chans[i]=4;                }else if(channel.equalsIgnoreCase("zposition")){                    chans[i]=5;                }else{                    throw new DataFormatException("无法识别的通道名称");                }            }        }        p.skipBlank();        if(p.isNextString("End Site")){            //遇到结束节点直接处理,不再递归            Segment end=new Segment();            s.sub.add(end);            end.base=s;            p.skipBlank();            p.requiredKeyWord("{");            p.skipBlank();            p.requiredKeyWord("OFFSET");            p.skipBlankRequired();            float ex=Float.parseFloat(p.readNumber());            p.skipBlankRequired();            float ey=Float.parseFloat(p.readNumber());            p.skipBlankRequired();            float ez=Float.parseFloat(p.readNumber());            float[]d=new float[]{ex,ey,ez};            end.line.put(d);            end.line.position(0);            end.data.add(d);            p.skipBlank();            p.requiredKeyWord("}");            p.skipBlank();        }else{            p.requiredKeyWord("JOINT");            do{                Segment sub=new Segment();                sub.base=s;                s.sub.add(sub);                readSegment(p,sub,iii+1);            }while(p.isNextString("JOINT"));        }        //Log.log("this is:"+p.readWord('|'));        p.requiredKeyWord("}");        p.skipBlank();        return true;    }    public void readLineData(Parser p,Segment s)throws DataFormatException,IOException{        if(s.chanels==null){            return;        }        float[]data=new float[s.dataLen];        for(int i=0;i<s.dataLen;i++){            p.skipBlankRequired();            float f=Float.parseFloat(p.readNumber());            data[s.chanels[i]]=f;        }        s.data.add(data);        for(Segment sub:s.sub){            readLineData(p,sub);        }    }}

Segment.java

import java.util.*;import javax.microedition.khronos.opengles.*;import java.nio.*;public class Segment{    FloatBuffer line;    public String name;    public Segment base;    public List<Segment> sub=new ArrayList<Segment>();    public byte[]chanels;    //当前节点Channel的数量    public byte dataLen;    //放入时即按照xrotate,yrotate,zrotate[,xposition,yposition,zposition]的顺序放入    public List<float[]>data=new ArrayList<float[]>();    public void draw(GL10 gl){    }    public Segment(){        line=ByteBuffer.allocateDirect(6*4).order(ByteOrder.nativeOrder()).asFloatBuffer();        //因为bvh的子节点总是以父节点为原点定位的所以线永远从(0,0,0)画到自己的位置        //Loger.l("data"+x+","+y+","+z);        line.position(0);    }    public void draw(GL10 gl,int index,float x,float y,float z,float...rotate){        float[]p=null;        if(dataLen!=0){            p=data.get(index);        }        //矩阵压栈        gl.glPushMatrix();        if(base==null){//根节点            //先平移到目标位置            gl.glTranslatef(x,y,z);            //旋转基础角度            if(rotate!=null&&rotate.length>1){                gl.glRotatef(rotate[0],1,0,0);                gl.glRotatef(rotate[1],0,1,0);            }            //再偏移到相对位置            gl.glTranslatef(p[3],p[4],p[5]);        }else if(base!=null){            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);            gl.glVertexPointer(3,GL10.GL_FLOAT,0,line);            gl.glDrawArrays(GL10.GL_LINES,0,2);            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);            //绘制完成后将自身坐标置为原点            gl.glTranslatef(line.get(0),line.get(1),line.get(2));        }        //将矩阵旋转        if(p!=null){            gl.glRotatef(p[0],1,0,0);            gl.glRotatef(p[1],0,1,0);            gl.glRotatef(p[2],0,0,1);        }        //迭代子节点        for(Segment s:sub){            s.draw(gl,index,x,y,z);        }        //矩阵出栈        gl.glPopMatrix();    }    //返回y轴和z轴的最佳值    public float[]calcBestPoint(float near,float top,float bottom){        float[]tmp=maxAndMinY();        Loger.l("max min:"+Arrays.toString(tmp));        float max=tmp[0];        float min=tmp[1];        tmp[0]=top/(top-bottom)*(max-min);        tmp[1]=(max-min)/(top-bottom)*near;        //Loger.l("y z:"+Arrays.toString(tmp));        return tmp;    }    public float[] maxAndMinY(){        float[]r=new float[]{this.line.get(1),this.line.get(1)};        if(sub.size()==0){            return r;        }        for(Segment sub:sub){            float[]t=sub.maxAndMinY();            if(t[0]>r[0]){                r[0]=t[0];            }            if(t[1]<r[1]){                r[1]=t[1];            }        }        return r;    }}

GLV.java

import android.opengl.*;import android.content.*;import android.util.*;import javax.microedition.khronos.opengles.*;import javax.microedition.khronos.egl.EGLConfig;import java.nio.*;import android.view.*;import android.widget.*;import java.util.*;import java.io.*;import android.os.*;public class GLV extends GLSurfaceView implements GLSurfaceView.Renderer{    public GLV(Context ctx){        super(ctx);        setRenderer(this);    }    public GLV(Context ctx,AttributeSet as){        super(ctx,as);        setRenderer(this);        place=Toast.makeText(getContext(),"",Toast.LENGTH_SHORT);    }    float rotatex;    float rotatey;    Toast place;    private void alert(Object o){        place.setText(o+"");        place.show();    }    MotionEvent start;    public boolean onTouchEvent(MotionEvent e){        if(e.getAction()==MotionEvent.ACTION_DOWN){            start=MotionEvent.obtain(e);        }else{            rotatey=start.getX()-e.getX();            rotatex=start.getY()-e.getY();        }        return true;    }    Handler progressCallBack;    int index=0;    float z=50;    float y=0;    private Segment seg;    public void setSegment(Segment s){        float[]best=s.calcBestPoint(1,1,-1);        y=best[0];        z=best[1];        this.seg=s;    }    public void onDrawFrame(GL10 gl){        gl.glClear(GL10.GL_DEPTH_BUFFER_BIT|GL10.GL_COLOR_BUFFER_BIT);        //gl.glClearColor(255,255,255,0);        float[]pos={0,0,10,1};        gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_POSITION,pos,0);        gl.glMatrixMode(GL10.GL_MODELVIEW);        gl.glLoadIdentity();        gl.glPushMatrix();        gl.glLineWidth(3);        gl.glColor4f(0,0,0,0);        GLU.gluLookAt(gl,0,y,z,0,0,0,0,1,0);        //*        drawLine(gl,-9999,0,0,9999,0,0);        drawLine(gl,0,-9999,0,0,9999,0);        drawLine(gl,0,0,-9999,0,0,9999);        //*/        gl.glLineWidth(10);        gl.glColor4f(1,0,0,0);        if(seg!=null){            if(!(index<(seg.data.size()/2)-1)){                index=0;            }            seg.draw(gl,index,0,0,0,rotatex,rotatey);            index++;        }        gl.glPopMatrix();    }    public void onSurfaceCreated(GL10 gl,EGLConfig p2){        //gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_FASTEST);    }    public void onSurfaceChanged(GL10 gl,int width,int height){        float rate=getHeight()*1f/getWidth();        gl.glDisable(GL10.GL_CULL_FACE);        gl.glViewport(0,0,width,height);        gl.glClearColor(1,1,1,0);        gl.glMatrixMode(GL10.GL_PROJECTION);        gl.glLoadIdentity();        gl.glFrustumf(-1,1,-rate,rate,1f,9999);        gl.glEnable(GL10.GL_DEPTH_TEST);        /*        gl.glEnable(GL10.GL_LIGHTING);        gl.glEnable(GL10.GL_LIGHT0);        float[]ambient={0f,0f,0f,1};        gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT,ambient,0);        float[]col={0,0.5f,0,1};        gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE,ambient,0);        gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_SPECULAR,col,0);        float[]meta={1f,1f,1f,1};        gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_AMBIENT,ambient,0);        gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_DIFFUSE,ambient,0);        gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_SPECULAR,meta,0);        gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_SHININESS,new float[]{0.8f},0);        */        //gl.glMaterialfv(GL10.GL_FRONT_AND_BACK,GL10.GL_EMISSION,col,0);        gl.glShadeModel(GL10.GL_FLAT);        //gl.glOrthof(-1,1,-rate,rate,0.2f,9999);    }    public void drawPoint(GL10 gl,float x,float y,float z){        gl.glPushMatrix();        ByteBuffer bb=ByteBuffer.allocateDirect(12);        bb.order(ByteOrder.nativeOrder());        FloatBuffer fb=bb.asFloatBuffer();        fb.put(new float[]{x,y,z});        fb.position(0);        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);        gl.glVertexPointer(3,GL10.GL_FLOAT,0,bb);        gl.glDrawArrays(GL10.GL_POINTS,0,1);        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);        gl.glPopMatrix();    }    public static void drawLine(GL10 gl,float x,float y,float z,float tx,float ty,float tz){        gl.glPushMatrix();        ByteBuffer bb=ByteBuffer.allocateDirect(24);        bb.order(ByteOrder.nativeOrder());        FloatBuffer fb=bb.asFloatBuffer();        fb.put(new float[]{x,y,z,tx,ty,tz});        fb.position(0);        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);        gl.glVertexPointer(3,GL10.GL_FLOAT,0,bb);        gl.glDrawArrays(GL10.GL_LINES,0,2);        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);        gl.glPopMatrix();    }}

MainActivity.java

import android.app.*;import android.os.*;import android.widget.*;import android.view.View.*;import android.view.*;import javax.microedition.khronos.opengles.*;import android.content.*;import android.net.*;import android.provider.*;public class MainActivity extends Activity{    private static final String PATH_KEY="PATH";    SharedPreferences sp;    GLV gl;    Toast pl;    public void alert(Object o){        pl.setText(o+"");        pl.show();    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data){        if(resultCode!=0&&data!=null&&data.getData()!=null){            Uri u=data.getData();            String pth=u.getPath();            if(u.getScheme().equals("file")&&pth.toLowerCase().endsWith(".bvh")){                sp.edit().putString(PATH_KEY,pth).commit();                load(pth);            }else{                alert("不支持的文件格式!!必须是bvh动作文件");            }        }else{            alert("您没有选择文件!");        }    }    @Override    protected void onCreate(Bundle savedInstanceState){        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        setProgressBarVisibility(true);        setProgress(100);        pl=Toast.makeText(this,"",Toast.LENGTH_SHORT);        gl=(GLV)findViewById(R.id.main_gl);        sp=getPreferences(MODE_PRIVATE);        String pth=sp.getString(PATH_KEY,null);        if(pth!=null){            load(pth);        }    }    public void click(View v){        switch(v.getId()){                case R.id.mainAdd:{                    gl.z++;                    break;                }                case R.id.mainSub:{                    gl.z--;                    break;                }                case R.id.main_load:{                    if(busy){                        alert("正在解析另一个文件");                    }else{                        Intent i=new Intent(Intent.ACTION_GET_CONTENT);                        i.setType("*/*");                        startActivityForResult(i,0);                    }                    break;                }        }    }    boolean busy;    public boolean load(final String path){        if(busy){            return false;        }        busy=true;        final View v=findViewById(R.id.main_progressContainer);        final ProgressBar pb=(ProgressBar)v.findViewById(R.id.main_progress);        final TextView disp=(TextView)v.findViewById(R.id.main_disp);        v.setVisibility(View.VISIBLE);        AsyncTask at=new AsyncTask<Object,Object,Object>(){            public Object doInBackground(Object...obj){                try{                    Loger.l("load action Begin parse");                    ActionReader ar=new ActionReader();                    final Parser p=new Parser(path);                    publishProgress(0,p.in.length());                    Thread noti=new Thread(){                        boolean exit=false;                        public void run(){                            Loger.l("progress Thread start");                            while(!exit){                                try{                                    synchronized(this){                                        this.wait(100);                                        publishProgress(1,p.in.length(),p.in.getFilePointer());                                    }                                }catch(Exception e){                                    exit=true;                                    break;                                }                            }                            Loger.l("progress Thread exit");                        }                    };                    noti.start();                    Segment s=new Segment();                    ar.read(p,s);                    gl.setSegment(s);                    busy=false;                    Loger.l("load action complete!");                    noti.interrupt();                    publishProgress(2);                }catch(Exception e){                    Loger.l("load action error:"+e);                    e.printStackTrace();                }                return null;            }            public void onProgressUpdate(Object...prog){                int what=prog[0];                if(what==0){                    long l=prog[1];                    pb.setMax((int)l);                }else if(what==1){                    long total=prog[1];                    long curr=prog[2];                    float rate=curr*10000/total/100f;//保留两位小数                    disp.setText("己加载"+rate+"%");                    pb.setProgress((int)curr);                }else if(what==2){                    v.setVisibility(View.INVISIBLE);                }            }        };        at.execute();        return true;    }}

Loger.java 。。。抱歉我只是一个龙套

import android.util.*;public class Loger{    public static void l(Object o){        Log.d("gl test",o+"");    }}

送佛送到西,把我丑的一逼的布局文件也拿出来了,小伙伴们给每个文件加个包名在manifest.xml中配置好就能瞎几把看了
main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:gravity="center">    <LinearLayout        android:layout_height="wrap_content"        android:layout_width="wrap_content"        android:gravity="center"        android:layout_marginBottom="20dp"        android:visibility="invisible"        android:id="@+id/main_progressContainer"        android:orientation="vertical">        <TextView            android:layout_height="wrap_content"            android:layout_width="wrap_content"            android:id="@+id/main_disp"            android:text="Text"/>        <ProgressBar            style="?android:attr/progressBarStyleHorizontal"            android:layout_height="wrap_content"            android:id="@+id/main_progress"            android:layout_width="wrap_content"/>    </LinearLayout>    <com.dance.glTest.GLV        android:id="@+id/main_gl"        android:text="@string/hello_world"        android:layout_width="200dp"        android:layout_height="200dp"/>    <Button        style="?android:attr/buttonStyleSmall"        android:id="@+id/mainSub"        android:layout_height="wrap_content"        android:layout_width="wrap_content"        android:onClick="click"        android:text="nearer"/>    <Button        style="?android:attr/buttonStyleSmall"        android:id="@+id/mainAdd"        android:layout_height="wrap_content"        android:layout_width="wrap_content"        android:onClick="click"        android:text="farer"/>    <Button        style="?android:attr/buttonStyleSmall"        android:id="@+id/main_load"        android:onClick="click"        android:layout_marginTop="20dp"        android:layout_height="wrap_content"        android:layout_width="wrap_content"        android:text="读取bvh动作文件"/></LinearLayout>

更新于一天后
由于一直对速度耿耿于怀,所以又对耗时大户Parser进行了一点优化,原来io操作使用的是RandomAccessFile,本来以为文件指针需要要很多反向跳转但实际上跳转距离都很短,所以用BufferedInputStream是一个很好的选择,关键字:mark(int len) reset() 供不明真相的小伙伴享用。

import java.util.zip.*;import java.util.*;import java.io.*;public class Parser{    private BufferedInputStream in;    private byte[]buf=new byte[1];    public long length;    public long pos;    public Parser(String filePath)throws IOException,FileNotFoundException{        File f=new File(filePath);        length=f.length();        this.in=new BufferedInputStream(new FileInputStream(f));    }    public char readChar()throws IOException{        in.read(buf);        return (char)buf[0];    }    //注意此方法按每个字两字节读取    public String readString(int charNum)throws IOException,DataFormatException{        byte[]buf=new byte[charNum*2];        int len=in.read(buf);        if(len!=buf.length){            //没有读取到指定数量的字符            throw new DataFormatException();        }        pos+=charNum;        return new String(buf);    }    public void close(){        try{            in.close();        }catch(IOException e){        }    }    //读取完成后光标将位于endFlag之后    //返回的字符串不会包含endFlag    //convertFlag 第一个字符为转意字符 后面是所有允许转意的字符    public String readString(char endFlag, char[]convertFlag)throws IOException,DataFormatException{        StringBuilder tmp=new StringBuilder();        boolean flag=true;        char c;        while(flag){            c=readChar();            if(c==endFlag){                break;            }            if(convertFlag!=null){                if(c==convertFlag[0]){                    tmp.append(c);                    c=readChar();                    if(Arrays.binarySearch(convertFlag,c)>0){                        tmp.append(c);                    }else{                        throw new DataFormatException("\""+tmp+"\"之后出现非法转意符");                    }                }            }else{                tmp.append(c);            }        }        String s=tmp.toString();        pos+=s.length();        return s;    }    //如果没有读取到任何字符将抛出异常    //读取到非允许的字符后将返回,指针将指向结尾的字符后面    public String readWord(char...enable)throws IOException,DataFormatException{        StringBuilder tmp=new StringBuilder();        while(true){            in.mark(1);            char c=readChar();            if(Character.isLetter(c)||Character.isDigit(c)||c=='_'||c=='$'||               (enable!=null&&Arrays.binarySearch(enable,c)!=-1)){                tmp.append(c);            }else{                //将指针回退一个位置                in.reset();                if(tmp.length()==0){                    throw new DataFormatException("没有读取到任何字符");                }                break;            }        }        pos+=tmp.length();        return tmp.toString();    }    //默认跳过所有空字符    //可以指定disable来阻止这种行为    public void skipBlank(char...disable)throws IOException{        if(disable!=null){            while(true){                in.mark(1);                pos++;                char c=readChar();                if(Arrays.binarySearch(disable,c)!=-1){                    break;                }                if(!Character.isWhitespace(c)){                    break;                }            }        }else{            while(true){                in.mark(1);                pos++;                char c=readChar();                if(!Character.isWhitespace(c)){                    break;                }            }        }        in.reset();        pos--;    }    //如果没有发现任何空字符则抛出异常    //将指向空白字符之后的第一个位置    public void skipBlankRequired(char...disable)throws IOException,DataFormatException{        boolean flag=true;        if(disable!=null){//避免重复判断            while(true){                in.mark(1);                pos++;                char c=readChar();                if(Arrays.binarySearch(disable,c)!=-1){                    break;                }                if(!Character.isWhitespace(c)){                    break;                }                flag=false;            }        }else{            while(true){                in.mark(1);                pos++;                char c=readChar();                if(!Character.isWhitespace(c)){                    break;                }                flag=false;            }        }        if(flag){            throw new DataFormatException("没有发现必须的空白字符");        }        in.reset();        pos--;    }    //如果没有出现空格将抛出异常    public void skipSpaceOnly()throws DataFormatException,IOException{        boolean flag=true;        in.mark(1);        pos++;        while(readChar()==' '){            flag=false;            in.mark(1);            pos++;        }        if(flag){            throw getError("必须的空格没有出现");        }        in.reset();        pos--;    }    //跳过指定字符串之前的所有数据    //跳过后光标将指向endFlag之后的位置    public void skipUntill(String endFlag)throws IOException,DataFormatException{        char[]cs=endFlag.toCharArray();        boolean flag=true;        while(flag){            char c=readChar();            pos++;            if(c==cs[0]){                in.mark(endFlag.length());                for(int i=1;i<cs.length;i++){                    c=readChar();                    if(c!=cs[i]){                        //跳回cs[0]之后的位置                        in.reset();                        flag=false;                        break;                    }                }                if(flag){                    break;                }                flag=true;            }        }        if(!flag){            throw new DataFormatException("没有找到指定目标串:"+endFlag);        }else{            pos+=endFlag.length();        }    }    public String readNumber()throws IOException,DataFormatException,NumberFormatException{        StringBuilder tmp=new StringBuilder();        while(true){            in.mark(1);            char c=readChar();            if(Character.isDigit(c)||c=='-'||c=='.'){                tmp.append(c);            }else{                //将指针回退一个位置                in.reset();                if(tmp.length()==0){                    throw new DataFormatException("无法从文件中读取数字,没有读取到任何字符");                }                break;            }        }        pos+=tmp.length();        return tmp.toString();    }    public boolean requiredKeyWord(String key)throws IOException,DataFormatException{        for(int i=0;i<key.length();i++){            if(key.charAt(i)!=readChar()){                throw getError("指定的关键字没有找到:"+key);            }        }        pos+=key.length();        return true;    }    //如果下一个单词是给出的单词将返回true并将指针移动到key之后的字符上    //否则指针指回调用此函数之前的位置并返回false    public boolean isNextString(String key)throws IOException{        in.mark(key.length());        for(int i=0;i<key.length();i++){            if(key.charAt(i)!=readChar()){                in.reset();                return false;            }        }        pos+=key.length();        return true;    }    private DataFormatException getError(String msg)throws IOException{        return new DataFormatException(msg+",Position:");    }    public char testChar()throws IOException{        in.mark(1);        char c=readChar();        in.reset();        return c;    }}