关于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(); //要求通道数必须为3或6 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; }}
- 关于java解析bvh动作文件
- BVH文件 Java解析器
- 关于BVH 动作捕捉数据文件
- BVH文件解析
- BVH文件解析
- BVH 文件
- BVH文件格式解析
- BVH文件格式解析
- [转]BVH文件介绍
- 表示三维运动的BVH文件格式解析
- 骨骼动画入门----BVH文件的载入和播放
- BVH File
- Biovision BVH
- splunk解析数据动作
- JSP动作 解析
- JSP动作 解析
- 关于动作感应传感器
- 关于Action动作
- didiv合作发<style> *{ margin: 0; padding: 0; } .outer{ margin: 0 auto; width: 80%;灌灌灌
- 重载了fragment的构造方法
- PopupWindow 的基本使用
- 第二章 if语句
- lib文件与dll文件的区别与联系
- 关于java解析bvh动作文件
- 数据库聚合函数
- Android----------属性动画
- POJ __2923 Relocation
- Java学习第四天
- 定位
- 2017.7.13
- 2017.7.10~2017.7.13总结
- springmvc工作原理