RenderScript には Android3D_ff というデータフォーマットがあります。
FileA3D を使うと Android3D_ff を読み込んで Mesh を生成できるのですが、
バイナリファイルでコンバート方法もわからなかったのであきらめました。
別の方法を使うことにします。
● IndexBuffer/VertexBuffer を自分で組み立てる
script (*.rs) 側で宣言しなくても IndexBuffer の Allocation を自分で作る
方法がわかりました。
// java short[] indexdata= { 0, 1, 2, 0, 2, 3 }; Element.Builder builder= new Element.Builder( mRS ); builder.add( Element.I16( mRS ), "index" ); Allocation ibuffer= Allocation.createSized( mRS, builder.create(), indexdata.length, Allocation.USAGE_GRAPHICS_VERTEX ); ibuffer.copyFromUnchecked( indexdata );
配列から直接コピーできるため初期化も簡単です。
VertexBuffer も全く同じように自分で構築することができます。
// java float[] vertexdata= { 0f, 0f, 0f, ~ }; Element.Builder builder= new Element.Builder( mRS ); builder.add( Element.F32_3( mRS ), "position" ); builder.add( Element.F32_3( mRS ), "normal" ); builder.add( Element.F32_2( mRS ), "texcoord0" ); Element vtype= builder.create(); Allocation vbuffer= Allocation.createSized( mRS, vtype, vertexdata.length/strideSize, Allocation.USAGE_GRAPHICS_VERTEX ); vbuffer.copyFromUnchecked( vertexdata );
ここまで出来ればデータファイル側に頂点構造を記述しておくことができそうです。
任意の頂点形式を持ったモデルデータを読み込めるようになります。
● Shader をもっと簡単に読み込む
以前 シェーダーを読み込むために LoadResource() という関数を使いましたが
無くても読み込めることがわかりました。
vsh_builder.setShader( LoadResource( mContext, R.raw.mesh2dc_vsh ) ); ↓ vsh_builder.setShader( mRes, R.raw.mesh2dc_vsh );
setShader() に直接リソース名を渡すことができました。
でも今回は、モデルデータを読み込むために再び LoadResource() を使います。
● Model データフォーマットを作る
簡単に読み込めそうだったので JSON を使ってモデルデータを記述しました。
[ { "primitive" : "TRIANGLE", "element" : [ "position" , "F32_3", "normal" , "F32_3", "texcoord0", "F32_2" ], "vertex" : [ -0.500000, -0.500000, 0.500000, 0.000000, 0.000000, 1.000000, 0.375000, 1.000000, 0.500000, -0.500000, 0.500000, 0.000000, 0.000000, 1.000000, 0.625000, 1.000000, ~ ], "index" : [ 0,1,2, 0,2,3, 4,5,6, 4,6,7, 8,9,10, 8,10,11, 12,13,14, 12,14,15, 16,17,18, 16,18,19, 20,21,22, 20,22,23 ] } ]
こんな感じでテキストとして記述します。
● JSON を読み込む
モデルデータファイルを res/raw/model00.json に置いておきます。
JSONTokener を使ってモデルを読み込み、情報を取り出して Mesh を組み立てます。
// java // Mesh を作るための準備 mMeshBuilder= new Mesh.AllocationBuilder( mRS ); // リソースから model00.json を読み込む String data= LoadResource( mContext, R.raw.model00 ); try { JSONArray toparray= (JSONArray)new JSONTokener( data ).nextValue(); int length= toparray.length(); for( int i= 0 ; i< length ; i++ ){ // 読み込んだデータはこれ JSONObject object= toparray.getJSONObject( i ); // object から element, vertex, index を取り出す LoadElement( object ); LoadVertex( object ); LoadIndex( object ); } }catch( JSONException e ){ }
最初に Element の情報を読み込んで組み立てます。
まずは "F32_3" などの文字列を Element.F32_3() に変換するためのテーブルを
作ります。同時に頂点のサイズ (byte数) も求められるようにしておきます。
// java // データ形式を Element に対応付けするためのテーブルを準備する class VertexType { public Element element; public int size; public VertexType( Element e, int s ) { element= e; size= s; } } ~ // テーブルを作る HashMapmap= new HashMap (); map.put( "F32", new VertexType( Element.F32(mRS), 4 ) ); map.put( "F32_2", new VertexType( Element.F32_2(mRS), 8 ) ); map.put( "F32_3", new VertexType( Element.F32_3(mRS), 12 ) ); map.put( "F32_4", new VertexType( Element.F32_4(mRS), 16 ) ); ~
JSON の "element" の情報を読み込んで Element を作ります。
// java Element.Builder bulider= new Element.Builder( mRS ); int strideSize= 0; // JSON から element の配列を取り出す JSONArray element= object.getJSONArray( "element" ); int esize= element.length(); for( int ei= 0 ; ei< esize ;){ String name= element.getString( ei++ ); // "position" 等 String typename= element.getString( ei++ ); // "F32_3" 等 // テーブルを使ってデータ形式文字列を Element に変換する if( map.containsKey( typename ) ){ VertexType vtype= map.get( typename ); bulider.add( vtype.element, name ); // 頂点のサイズも計算しておく strideSize+= vtype.size; } } // Element を作る mElement= bulider.create(); mStirdeSize= strideSize; // 頂点あたりの byte サイズ
Element と頂点サイズが求まったので、この情報を元に頂点バッファを作ります。
// java private void LoadVertex( JSONObject object ) throws JSONException { // JSON からのデータ読み込み JSONArray array= object.getJSONArray( "vertex" ); int size= array.length(); float[] vertexdata= new float[size]; for( int i= 0 ; i< size ; i++ ){ vertexdata[i]= (float)array.getDouble( i ); } // バッファを作成して転送する Allocation vbuffer= Allocation.createSized( mRS, mElement, vertexdata.length / (mStirdeSize/4), Allocation.USAGE_GRAPHICS_VERTEX ); vbuffer.copyFromUnchecked( vertexdata ); // Mesh の要素として追加 mMeshBuilder.addVertexAllocation( vbuffer ); }
同じように Index も読み込んで作ります。
Index が無い場合にも対応。
// java private void LoadIndex( JSONObject object ) throws JSONException { if( !object.has( "index" ) ){ // index が無い場合 mMeshBuilder.addIndexSetType( Mesh.Primitive.TRIANGLE ); return; } // JSON から読み込む JSONArray array= object.getJSONArray( "index" ); int size= array.length(); short[] indexdata= new short[size]; for( int i= 0 ; i< size ; i++ ){ indexdata[i]= (short)array.getInt( i ); } // バッファを作成する Element.Builder eb= new Element.Builder( mRS ); eb.add( Element.I16( mRS ), "index" ); Allocation ibuffer= Allocation.createSized( mRS, eb.create(), indexdata.length, Allocation.USAGE_GRAPHICS_VERTEX ); ibuffer.copyFromUnchecked( indexdata ); // Mesh に追加 mMeshBuilder.addIndexSetAllocation( ibuffer, Mesh.Primitive.TRIANGLE ); }
あとは Mesh を作るだけです。
Mesh mesh= mMeshBuilder.create();
これでモデルデータを読み込んで Mesh を作ることが出来ました。
(今回は "primitive" は参照していません。)
● Depth Buffer の利用
RenderScriptGL 作成時に Depth の最小値を与えます。
// java RenderScriptGL.SurfaceConfig sc= new RenderScriptGL.SurfaceConfig(); sc.setDepth( 16, 24 ); // ← 追加 mRS= createRenderScriptGL( sc );
ProgramStore で depth test を有効にします。
このオブジェクトを script に渡します。
// java mScript.set_dsbstate( ProgramStore.BLEND_NONE_DEPTH_TEST( mRS ) );
script 側では Depth Buffer のクリアを追加し、受け取った
ProgramStore (DepthStencilState) を設定します。
// RenderScript rs_program_store dsbstate; int root() { rsgClearColor( 0.0f, 0.2f, 0.5f, 0.0f ); rsgClearDepth( 1.0f ); // ← 追加 ~ rsgBindProgramStore( dsbstate ); // 設定 ~
● 3D 描画用の ConstantBuffer を作る
今回は Mesh の頂点は書き換えませんが、Matrix を script で書き換えるため
Constant Buffer の構造体を RenderScript で宣言します。
// RenderScript typedef struct ConstantType { rs_matrix4x4 PView; rs_matrix4x4 World; } ConstantType_t; ConstantType_t* ConstantType_n; // unuse
OpenGL ES 2.0 には 3x4 matrix が無いので残念ながら RenderScript にもありません。
PView は ProjectionMatrix * ViewMatrix です。
初期値は Java から与えてみます。
// java // Uniform vconst= new ScriptField_ConstantType( mRS, 1, Allocation.USAGE_SCRIPT|Allocation.USAGE_GRAPHICS_CONSTANTS ); Matrix4f projection= new Matrix4f(); projection.loadPerspective( 30.0f, mWidth/mHeight, 0.1f, 100.0f ); Matrix4f view= new Matrix4f(); view.loadTranslate( 0.0f, 0.0f, -10.0f ); Matrix4f pview= new Matrix4f(); pview.loadMultiply( projection, view ); // ProjectionMatrix * ViewMatrix Matrix4f world= new Matrix4f(); world.loadIdentity(); // 書き込み vconst.set_PView( 0, pview, true ); vconst.set_World( 0, world, true );
シェーダーでは UNI_PView と UNI_World で参照できます。
// GLSL : mesh3d_vsh.vsh varying vec3 onormal; varying vec2 otexcoord0; void main() { vec4 wpos= UNI_World * vec4( ATTRIB_position.xyz, 1.0 ); gl_Position= UNI_PView * wpos; onormal.xyz= mat3(UNI_World) * ATTRIB_normal.xyz; otexcoord0.xy= ATTRIB_texcoord0.xy; }
● RenderScript で Matrix 書き換え
Java や Shader だけでなく script からも ConstantType のデータに
アクセスできます。
あらかじめ mScript.bind_vconst( vconst ) でバッファを登録しておきます。
RenderScript には Matrix 演算のための関数が用意されているため
このような geometry 演算は容易に出来ます。
// RenderScript : rendermesh.rs #pragma version(1) #pragma rs java_package_name(jp.flatlib.ap02c) #include "rs_graphics.rsh" typedef struct ConstantType { rs_matrix4x4 PView; rs_matrix4x4 World; } ConstantType_t; ConstantType_t* ConstantType_n; // unuse rs_program_vertex vsh; rs_program_fragment psh; rs_allocation texture; rs_sampler sampler; rs_mesh mesh; rs_program_store dsbstate; rs_program_raster rstate; ConstantType_t* vconst; static float rot= 0.0f; int root() { rsgClearColor( 0.0f, 0.2f, 0.5f, 0.0f ); rsgClearDepth( 1.0f ); // 回転させてみる rs_matrix4x4 world; rsMatrixLoadRotate( &world, rot, 0.0f, 1.0f, 0.0f ); rot+= 1.0f; if( rot >= 360.0f ){ rot-= 360.0f; } // WorldMatrix を書き換える rsMatrixLoad( &vconst->World, &world ); rsgAllocationSyncAll( rsGetAllocation( vconst ) ); // 読み込んだ mesh の描画 rsgBindSampler( psh, 0, sampler ); rsgBindTexture( psh, 0, texture ); rsgBindProgramFragment( psh ); rsgBindProgramVertex( vsh ); rsgBindProgramStore( dsbstate ); rsgBindProgramRaster( rstate ); rsgDrawMesh( mesh ); return 33; }
●ソースリスト
これで JSON に変換した任意のモデルデータを読み込んで RenderScript で
描画できるようになりました。
Material や Geometry、Animation 情報も欲しくなります。
・flatlib_ap02c.zip ソースコード
次はまとめなど 「Android 3.x RenderScript (6) Graphics まとめ」
関連エントリ
・Android 3.x RenderScript (4) script で頂点を書き換える
・Android 3.x RenderScript (3) 独自シェーダーの割り当てとメッシュの描画(2D)
・Android 3.x RenderScript (2) 描画と Allocation
・Android 3.x RenderScript (1)