ARGENTO CUORE

Category : Android

--.--.--[--] スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

2010.06.07[月] Arrays.copyOfRangeはAndroidにない

配列の一定範囲を取り出すときに、Arrays.copyOfRangeっていうのが便利で使っていたら、Androidでは存在しないものでした。
JavaSE6で登場したメソッドらしいです。使ってる言語はScalaだけど。

同じような処理をSystem.arraycopyで書いてみました。こんな感じの書き方で平気かなー……。
def copyOfRange(array_byte:Array[Byte], index1:Int, index2:Int):Array[Byte] = {
if((index2 - index1) <= 0) return null
val array_ret = new Array[Byte](index2 - index1)
System.arraycopy(array_byte, index1, array_ret, 0, index2 - index1)
return array_ret
}

スポンサーサイト

2010.05.30[日] Activityを追加する方法を忘れないようにメモ

ハマってしまったのでメモしておく。

Activityの開始
val intent = new Intent(Intent.ACTION_VIEW)
intent.setClassName("hoge.piyo.Fuga", "hoge.piyo.Fuga.ActivityClassName")
startActivity(intent)


AndroidManifest.xmlにactivityを追加しないと駄目

<activity android:name="ActivityClassName" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>


などというところとは全然関係なくて、ソースコードをsrc/hoge/piyo/Fuga/じゃなくて間違ってトップディレクトリに作ってたのが原因でした。あぁバカだ。

2010.05.30[日] GCを抑えたい……

電車の中でプログラミングしたいよ。
ちょっとした待ち時間にプログラミングしたいよ。
Android上で書いて動かせたら良くない?

でも、Rubyとか、そういうの、書くのに文字数が多いから、携帯だと面倒だよね。
括弧とか要らない。関数と変数と描画処理とループと分岐があれば良い。

それってベーシックってやつ?
ベーシックは、一日だけ触ったことある。

あんまり考えず、とりあえず、 四則演算とかやってみよー。

と、ふと思って、Scalaで書いたインタープリターがなんとなく形になった(と言っても四則演算+PRINTで文字を指定位置に表示できるだけです)。とりあえず制御構造や描画処理、関数とかはすぐ追加できるんで、すぐできることは後回しにして(ぁ、Android上で動かしてみました。

で、あれです……。
調子に乗ってnewしまくってるコードを書きました。
Androidにきつい仕事させてごめんなさい。この場を借りてお詫び申し上げます。


ログに残るGCのメッセージと、それにかかる時間に絶望した。
1回のGCに100msかかるのに絶望した。
アニメーションかくつくのに絶望した。

逆に考えよう。
何も考えずにnewしまくったからこれだけのGCが発生しているのであって、オブジェクト生成に気をつけてリライトすれば何も問題はなくなるはずだ。

2010.05.24[月] Scala on android

http://d.hatena.ne.jp/pillows916/20100519/p1

上記サイトでわくわくするような記事があったので試してみた。

環境は微妙に違うけど、

  • Scala2.8.0RC2

  • AndroidSDK2.0

  • ProGuard4.3

  • Apache Ant1.7.1

  • Xubuntu9.10


です。

けど、android_rules.xmlを拾って書き換えるだけで、Android上でHello,worldプログラムが実行できてしまった。
OSが違うから? バージョンがちょっと違うから? 何なんだろうー。
とりあえず、使ってないscala-android.jarが何してるのか気になるので、ソースを見よう。

- 追記 -
android-libraryについて

/src/library/
/src/android-library/
の二つのディレクトリがある。library以下には通常のscala-library.jarのためのソースコードが設置されており、android-library以下にはandroid用のscala-library.jarのファイル(差分)が設置されている。

android-libraryには、
ScalaObject.scala
reflect/ScalaBeanInfo.scala
がある。

ScalaObject.scalaの変更点
trait ScalaObject extends java.lang.Object

trait ScalaObject extends AnyRef
Android用に用意されたScalaObjectは、AnyRefを継承するようになっている。

reflect/ScalaBeanInfo.scalaの変更点
コードは全部消されていて、空のScalaBeanInfo.scalaがあるだけ。

みたいな感じだった。ビルドしたファイルを物置(http://ronor.web.fc2.com/scala-android/)に置いてみた。
コンパイル時だけは、CPU速度が欲しいと思う。

2010.01.25[月] Blenderでモデリングして表示

Misfit Model 3Dというソフトでどうしてもテクスチャを描くための展開図の作り方がわからず……できないのかな……。
仕方なくBlenderを使うことにしました。

で、↓こんな感じのものを作ってみた。
blender_ss.png


人生初の3Dっぽいゲームはスターフォックスでした。
でも、なんとなくの記憶で作ったので、こんな形だったかなー……と言う程度。
というか、あのゲームってFXチップ?によって馬鹿みたいに強化したスプライト機能を駆使しまくって描画していると前聞いたことがあります。けど、それを言うならPlaystationもまともな3次元の計算してな──いやそもそもまともって何だ……、見えれば良いのです。立体的に広がる空間と、広大な世界と3D酔いが君を待っているー。




で、Blenderのモデリングわからないーと四苦八苦していたのですが、なんとか(しょぼいのが)できたー(←言いたかったこと)


◆モデリングの手順メモ
・スペースキーでメニューを呼び出しEmpty Meshを作成
・CTRL+左クリックで座標追加
・二点を選んだ状態でfキーを押すとエッジを追加。
・三点を選んだ状態でfキーを押すと面を追加。
・選んでxを押すと削除メニュー表示。
・Editing(F9)のMesh tools moreのDraw NormalsをONにすると面の向きを視覚化。
・面を選択してwを押してFlip Normalsで面を裏返す。
・左右対称な物体を作るには、まず片側を作成し、EditingのModifiersからAdd ModifierでMirrorを選択して設定する。最終的に編集が終わったらApplyする。

※Linux用に三角女王?とか誰か作ってくれないかな……六角大王のシンプルさ加減は非常に好ましいと言えます。ぜひともどこかのスーパーハッカーの方がオープンソースで、Linux対応で、六角大王そのままに(無料で)。

で、GtkGLExtなるものをインストールしてちょっと触ってみた。
使い慣れたGUIライブラリ+OpenGL = 三角女王(仮)

でもglDrawElements使おうとしたらなんかエラー出た。なんで……。


◆テクスチャマッピングの手順メモ
・展開図を作成するためにビューを分割し片方をUV/image Editorにする。
・どの辺から切って展開するかを定めるには、エッジを選択し、CTRL+Eでmark seamを選択。
・↑で間違っちゃったら、間違って指定したエッジを選択してCTRL+Eでclear seamを選択。
・エッジは同時にいくつでも選択しててOK。
・展開するには、全ての面を選択し、uキーを押してUnwrapを選択。
・間違っちゃったらuキーを押してリセット。
・UV/Image EditorのUVs -> scripts -> Save UV Face Layoutを選択してtga形式で展開図を保存。
・Gimpとかで適当に(ほんとは魂を込めて)テクスチャを描く。
・UV/Image Editorにテクスチャを読み込むには、Image->Openを選択して読む。
・F5を押して下のパネルのTextureのMap InputをUVに変更。
・F6を押してTextureパネルのTexture Typeをimageにし、先ほど作ったテクスチャファイルを読む。
・もし変更した場合は、パネルからReloadを選ぶ。
・F12でレンダリング。ちゃんとできてるか確認。



そしてAndroidへ!

tekito_model.png


使ったテクスチャは↓みたいな感じ。
starfox.png

見た目はちゃんとできたっぽい……?。 ヨカッタヨカッタ。
もうちょっとモデリングとテクスチャ頑張ろう(それ以前にテクスチャは、塗りつぶしで色を流し込んだだけです)。

http://ronor.web.fc2.com/files/model.zip
にモデルを晒しておきます。ネット上を見ると、lwo形式やmqo形式やrok形式は多いけれど、普通にフリー&Linuxで動作するようなソフトで読み込める形式のモデルデータあんまり公開されてない?と思ったので(mqoはWindows環境に限られるけれど、汎用的なフォーマットだよなーと思ったりも)。

正直モデリングやってらんないー。なんか、こう、ぱぱっと!できたら良いのに。
ぱぱっとできるように練習するか……。


先は長いー……。

2010.01.21[木] Android OpenGLでobjファイルを読み込む編 part 5

とりあえず現在のソースコードをまるごと載せてみる。

・複数テクスチャ
・グループ
・材質設定

にはまだ対応してません。

あとすごく遅い。
読み込む時点では頂点数やテクスチャ座標の数が不明なので、ArrayListを使ってaddしてるんですが、
配列と比べて遅いだろうから、あらかじめ数を取得して中間ファイルを作るようにしたほうが良いのかも…。
再配置も、Android上でやるのではなくて、PC上であらかじめ計算してそれを読み込ませたほうが良いかな。

現状mtlにはテクスチャ名を取得しに行っている以外何もしていないので、この読み込みもかなりの無駄…。

どなたかこのコードをレビューしていただけたら感謝しまくりです。

package com.gmail.argent_cuore.graphics;

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.lang.reflect.Field;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.content.Context;
import android.util.Log;

/**
* Object3D
* objファイル形式のデータを処理し、OpenGL上で扱えるデータへと変換する。
*/
public class Object3D {
final String class_name = getClass().getName();

private static Class raw_class;
private static Class drawable_class;
private final static int RAW = 0;
private final static int DRAWABLE = 1;

private static Context context = null;

private String current_material = null; //現在読み込み中のマテリアル名

private float[] vertex_data = null;
private byte[] index_data = null;
private float[] texture_data = null;

private String texture_name = null;


/**
* コンストラクタ
*/
public Object3D(Context c, Class raw, Class drawable){
context = c;
raw_class = raw;
drawable_class = drawable;
}


public Bitmap getBitmap(){
Bitmap bmp = null;
Field f = selectField(DRAWABLE,texture_name);
int id = 0;
try{
id = f.getInt(drawable_class);
}catch(IllegalAccessException e){}
if (id!=0) {
bmp = BitmapFactory.decodeResource(context.getResources(), id);
}
return bmp;
}

/**
* MTLファイルを読む。
*/
private void interpretMTL(BufferedReader buffer){
String line = null;
try {
while((line = buffer.readLine()) != null) {
if (line.length() > 0) {
char c = line.charAt(0);
if (c == 0x23) { //コメント行

} else if (c == 0x09) { //タブ
String s[] = line.split(" ");
if (s[0].equals(new String(new byte[] { 0x09 }) + "map_Kd")) { //[TAB]mak_Kdとイコール
if (current_material != null) {
texture_name = s[1];
} else {
Log.e(class_name, "マテリアル宣言がない状態で材質が定義されている可能性があります");
}
}
} else {
String s[] = line.split(" ");
if (s[0].equals("newmtl")) {
current_material = s[1];
}
}
}
}
}catch(IOException e){
Log.e(class_name, e.toString());
}
}

/**
* objファイル名を引数に受け取る。
* 解釈して頂点座標、テクスチャ座標、面データのインデックス値を生成。
* 他のは無視。
*/
public void interpretOBJ(String filename){
ArrayList<Byte> indices = new ArrayList<Byte>();
ArrayList<Float> vertices = new ArrayList<Float>();
ArrayList<Float> textures = new ArrayList<Float>();
ArrayList<Integer> texnum = new ArrayList<Integer>();

BufferedReader buff_obj = createBufferFromRawFile(filename);
String line;
try {
while ((line = buff_obj.readLine()) != null){
if (line.length() > 0) { //空行は無視
char c1 = line.charAt(0); //一文字目
char c2 = line.charAt(1); //二文字目
char c3 = line.charAt(2); //三文字目
if (c1 == 0x20) { //スペースはスキップ
continue;

} else if (c1 == 0x23) { //#はコメント行であるため解釈しない
continue;

} else if (c1 == 0x6D && c2 == 0x74 && c3 == 0x6C) { //mtl(lib)
String s[] = line.split(" ");
BufferedReader buff_mtl = createBufferFromRawFile(s[1]); //mtlファイルをロード
interpretMTL(buff_mtl); //mtlファイルを解釈
continue;

} else {
switch (c1) {
case 0x76: //v
switch (c2) {
case 0x20: //v(space)
String vs[] = line.split(" ");
vertices.add(Float.parseFloat(vs[1])); //x
vertices.add(Float.parseFloat(vs[2])); //y
vertices.add(Float.parseFloat(vs[3])); //z
break;

case 0x6E: //vn
break;
case 0x74: //vt
String vts[] = line.split(" ");
textures.add(Float.parseFloat(vts[1])); //u
textures.add(Float.parseFloat(vts[2])); //t
break;
default:
Log.w(class_name, "v" + c2 + "というラベルに対する処理が定義されていません");
break;
}
break;
case 0x66: //f
switch (c2) {
case 0x20: //space
String s[] = line.split(" ");

//indicesは三角ポリゴンの面を構成する頂点座標へのインデックス値
//texnumは、頂点座標に対応するテクスチャ座標へのインデックス値
//1番目と2番目しか読まない
String ss1[] = s[1].split("/");
indices.add((byte)(Integer.parseInt(ss1[0])-1)); //0からはじまるようマイナス1
texnum.add((int)Integer.parseInt(ss1[1])-1);

String ss2[] = s[2].split("/");
indices.add((byte)(Integer.parseInt(ss2[0])-1));
texnum.add((int)Integer.parseInt(ss2[1])-1);

String ss3[] = s[3].split("/");
indices.add((byte)(Integer.parseInt(ss3[0])-1));
texnum.add((int)Integer.parseInt(ss3[1])-1);
break;
default:
break;
}
break;
default:
Log.w(class_name, c1 + "というラベルに対する処理が定義されていません");
break;
}
}
}
}
}catch(IOException e){
}

//-------------------------------------------------------
//f行の頂点座標の指定にしたがって新しいリストを作成する
//-------------------------------------------------------
ArrayList<Float> vertices2 = new ArrayList<Float>();
ArrayList<Float> textures2 = new ArrayList<Float>();
int count_max = indices.size();

index_data = new byte[count_max];
for (int i=0; i<count_max; i++){
//頂点座標をindicesの配列に従って再配置
int num = indices.get(i)*3;
vertices2.add(vertices.get(num));
vertices2.add(vertices.get(num+1));
vertices2.add(vertices.get(num+2));

//テクスチャ座標をtexnumの配列に従って再配置
num = texnum.get(i)*2;
textures2.add(textures.get(num));
textures2.add(textures.get(num+1));

//インデックスデータは1から順に並ぶだけ
index_data[i] = (byte)i;
}

//再配置済みの頂点座標を配列にセット
count_max = vertices2.size();
vertex_data = new float[count_max];
for (int i=0; i<count_max; i++){
vertex_data[i] = vertices2.get(i);
}

//テクスチャ座標を配列にセット
count_max = textures2.size();
texture_data = new float[count_max];
for (int i=0; i<count_max; i+=2){
texture_data[i] = textures2.get(i);
texture_data[i+1] = 1.0f - textures2.get(i+1); //t座標は上下が反転するため1.0から引く
}
}

private BufferedReader createBufferFromRawFile(String filename){
BufferedReader buffer = null;
Field f = selectField(RAW,filename);
int id = 0;
try{
id = f.getInt(raw_class);
}catch(IllegalAccessException e){}
return loadFileFromFieldId(id);
}

private BufferedReader loadFileFromFieldId(int id){
InputStream is = context.getResources().openRawResource(id);
return new BufferedReader(new InputStreamReader(is));
}

private Field selectField(int resource_type, String name){
Field f = null;
try {
if (resource_type == RAW){
f = raw_class.getField(name);
} else if (resource_type == DRAWABLE) {
f = drawable_class.getField(name);
}
}catch(NoSuchFieldException e){}

if (f==null){
Log.e(class_name, name + "というフィールドが存在しません。");
}
return f;
}

/**
* 頂点座標のデータをint型の配列で返す。
* interpretOBJ()を呼ぶ前に実行しちゃダメ。
*/
public int[] getVertexDataAsInt(){
int[] vd = null;
if ( vertex_data != null) {
vd = new int[vertex_data.length];
for(int i=0; i<vertex_data.length; i++){
vd[i] = (int)(vertex_data[i] * 65535); //浮動小数点数を固定小数点数に変換
}
}
return vd;
}

public float[] getVertexData(){
return vertex_data;
}

public byte[] getIndexData(){
return index_data;
}

public float[] getTexCoords(){
return texture_data;
}
}

2010.01.20[水] Android OpenGLでobjファイルを読み込む編 part 4

前回part3で、できたと思っていたら、痛恨のミスを犯していたというか、まったく理解が足りてなかったというか。

問題が起きたのは、

・頂点を共有するトライアングルポリゴンが二枚以上
・共有している頂点に対応するテクスチャ座標が異なる

という情報の入ったobjファイルをロードしたときでした。

glVertexPointer
glTexCoordPointer

で頂点座標とテクスチャ座標を設定し、

glDrawElements

で描画していたのですが、頂点座標とテクスチャ座標のデータが入った配列要素は、1対1で設定しなければいけなかったのと、手元の最初に作ったサンプルのobjファイルではある頂点座標に対するテクスチャ座標は固定だったため、

「そりゃ、同じ頂点座標だから同じテクスチャ座標になるに決まってるじゃん」

とでかいミスを犯して、

頂点座標数 < テクスチャ座票数

だったのを、ある頂点座標に対応するテクスチャ座標のうち重複するものを削除して、

頂点座票数 = テクスチャ座票数

にしてました……。
でも、

トライアングルポリゴン2つで構成された四角形に、3DCGソフトでUVマップしたテクスチャを貼り付けようとすると問題発覚。

tex.png

上記のようなテクスチャを張り付けると表示が壊れました。
残るは、

頂点座標+重複する頂点座標を加算 = テクスチャ座標

にしないとだめなのかなー増えるのやだなーと思って、色々情報を漁ってみたんですけど、glDrawElements描画時、頂点座標とテクスチャ座標のデータの並び的なオプションもなさそうで……。
結局頂点増やしました。
テクスチャ座標指定の重複を削除するより処理がシンプルー。

しかしロード長いです…。
かなりゴリゴリ書き殴っているので、高速化しなければ……。

以下、蛇足
少しでも早くなってくれればと思い、浮動小数点から固定小数点で計算するようにしました。
変換は、

浮動小数点数 x 65535 = 固定小数点数

らしいです?
iPhoneなどは固定小数点数を使わない方が逆によいみたいーだけどもってないし測ったことない。
どうなんだろ。

2010.01.19[火] Androidのresourceについて

objファイルを読み込むときに、描画物だからdrawable以下に設置していたのですが、
AndroidのResource関連の情報を見るとどうやらこれは間違いだったようで、

http://developer.android.com/intl/ja/guide/topics/resources/resources-i18n.html

上記ページを見るとリソースファイルは

res/anim/
res/drawable/
res/layout/
res/value/
res/xml/
res/raw/

という6つの種別に区別されています。
で、drawable内には.png .jpgなどのbitmap系ファイルのみ設置可能で、objのような任意のファイルはraw以下に設置するようになっていました。リソースを使用するには、Resources.openRawResource()を呼び出し。

2010.01.16[土] setRenderModeとか、Threadのこととか

三次元グラフィックスを表示するのにGLSurfaceViewを使っているのですが、
onDrawFrame
というメソッドが定期的に呼ばれるっていう説明を見て、こちら側でこの時に描画してほしいというタイミングを制御できないかなーと思っていたら、
setRenderMode
というメソッドで、RENDERMODE_WHEN_DIRTYを指定してやれば、
requestRender()
で描画を要求することができるらしいです。

フレームレートはオブジェクトの数や大きさで随分変化してしまい、PCのように60fps固定とか30fps固定とかが無理そうです……。

で、今はGameThreadというプログラム中の色々な計算をするスレッドを作っていて、そこのメインループからrequestRender()を呼び出すようにしました。そうしたら、1つ問題を発見。

なぜかアプリを終了させても、GameThreadのメインループが終了しない。

で、Threadのインスタンスの参照先をnullにしてみたり、そのあと強制GCかけてみたりしたんですが、メインループ終了しない……。なんでだろう……。

とりあえず、メインループ内でboolean値を参照するようにしてbreakさせるかどうかを分岐、UIのほうから終了が選択されたら、boolean値をfalseにして、Threadインスタンスの参照をnullにするようにしました。

これで良いのかなぁ……。

2010.01.11[月] Android OpenGLでobjファイルを読み込む編 part 3

3Dテクスチャ

作った円柱にandroidのマスコットのテクスチャを張り付けて表示~。
objファイルロード&テクスチャ適用完了です。が、テストとかまだやってないので読み込めないobjとか絶対あります。これから一度ロードするクラスを書き直して機を見てブログにアップロードしようかなと思います。

テクスチャ座標と頂点座標って1対1で対応しているものなのですが(OpenGL上だと)、objファイルだと、三角形を定義するf行に、この三角形のこの頂点座標に対してX番目のテクスチャ座標を使ってねっていう感じで定義されているので、それを解釈して処理しないとだめでした。
読み込み時に処理するため、ロード時間を短くするために、OpenGLで直に解釈できるファイルにコンバートするツールを書いて、それを読み込んだ方がAndroidのバッテリーやCPUにも優しい感じがします……。

まだグループ分けとかの解釈をしていない&アニメーションってどういう風なデータをmm3dが出力するかわかっていないので、ゲームにするには程遠い状態です。

というかまともな3Dのあたり判定をやったことがないので、そこが鬼門?
二点の距離で判定して押し戻すといった感じの処理しか書いたことない。
当たる可能性のある壁(オブジェクト)だけを判別して、それに対してのあたり判定をするーとか、なんか考えるだけで面倒くさそう、難しそう。数学ー……。

お勉強しなきゃ。


Scala Feed

scala feed

FC2カウンター

プロフィール

RoNor

Author:RoNor
得意呪文はScalaですって言えるようになるのが夢です。
デスマーチ中、パーティメンバーの防御力を向上させたりさせなかったり。

検索フォーム

QRコード

QRコード

Copyright © 2009-2010 ARGENTO CUORE and RoNor All rights reserved.

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。