2015年1月25日日曜日
2012年5月15日火曜日
確率操作とか煽ってますが
コンプガチャ、カギはネトゲ廃人・搾り取り加減
これって、当たり前じゃないの?
「MMOであるアイテムのドロップ率が異様に低くて掲示板に苦情が殺到したので、次のアップデートで確率を上げました。」
を、すごい短いスパンでやってるよ! ってだけだと思うんだけども・・・。
この記事を見ると、
「廃人リストみたいなのがあって、その人にだけ確率を低めで設定して、対象者の課金が10万に達した途端に確率を上げて目的のカードを出させている!」
みたいにも読み取れてしまうのがなんていうか罠。
さすがにんなことやってないと思いたい。 いや、実際のところわからないけども、そこが明確になってないこの記事からは叩くところも擁護するところも出てこないと思うんだけどな。
個人的にはUFOキャッチャーもリアルタイムで確率変動されてると思うんだけどどうでしょか。
これって、当たり前じゃないの?
「MMOであるアイテムのドロップ率が異様に低くて掲示板に苦情が殺到したので、次のアップデートで確率を上げました。」
を、すごい短いスパンでやってるよ! ってだけだと思うんだけども・・・。
この記事を見ると、
「廃人リストみたいなのがあって、その人にだけ確率を低めで設定して、対象者の課金が10万に達した途端に確率を上げて目的のカードを出させている!」
みたいにも読み取れてしまうのがなんていうか罠。
さすがにんなことやってないと思いたい。 いや、実際のところわからないけども、そこが明確になってないこの記事からは叩くところも擁護するところも出てこないと思うんだけどな。
個人的にはUFOキャッチャーもリアルタイムで確率変動されてると思うんだけどどうでしょか。
2012年5月10日木曜日
Android OpenGLゲームプログラミング
Android OpenGLゲームプログラミング Android 2/3/4 & OpenGL ES 1.0/2.0対応 まったくもって売ってない。
しかもAmazonの評価は一つしか無い上に星2つ。 なんなんだ。 どうしてほしいんだ。
2012年5月9日水曜日
ソーシャルゲーム業界が慌ただしいんだが
なんでそんなに騒ぐのかよくわからない。
声高々に「ザマー見ろ!」と言っている人というのはなんかそれによって実害を被った人なのでしょうか。
実際にガチャで月10万だの20万だのを支払った人だとすれば多分その時は自分の意思でボタンを押して購入したんでしょうし。
明細を見て正気に戻ったとか言うんであればそれはそいつが愚かなだけでしょ。普通に。
それで「射幸心を煽られた! 金返せ!」ってみっともないにも程がある。
あと、小学生が数十万使ったとか抜かす親御が幾人かいるみたいだけど、それはあんたらの教育が悪いよ。
なんかのきっかけで海外の友達が出来て毎日海外にその携帯で電話してて月末に数十万の請求が来たら誰を訴えるのさ。
電話代ってそもそも常に確認できるものでもなくて、自分で管理するものでしょ。
その管理能力が無いガキンチョに簡単に規制もかけてない携帯を渡す親の気が知れない。
んで。
なんていうか、このweb界隈で「さっさと規制しろ!」って言っている人っていうのは、ソーシャルゲームをやったことがない、もしくはやってみたけど何が面白いか理解できない人が、「あんなのの何が面白いんだっふんっ!」って拗ねているようにしか見えない。 イソップ童話の「どうせあの葡萄はすっぱいだろう」って言っている狐のごとく。
もしくは。
視点を変えると、ユーザではなく、ゲームクリエイターの可能性もあるかなと思い始めてきた。
つまりソーシャルの方がローリスクハイリターンで会社のマンパワーがそこに集中されていて、作りたくもないソーシャルゲームを作らされている技術者の訴え??
だとすると納得が行くんだけど、そんなに大人数いるとも思えないしなぁ。
ゲーム好きが「最近面白いゲームが出ないのはどの会社もソーシャルゲーム開発に注力してるからだ!」っていう
閑話休題
今回のコンプリートガチャ規制の一番のポイントは
「デジタルデータって景品なの?」
という一点につきると思う。
各種レアカードの『絵』に価値があるわけではないですよね。見たいだけならちょっとググれば出てくるんだし(そもそもそれが問題なのかもだけど)、そのカードを『ゲーム内で使う権利』を示すビットデータのフラグを上げ下げしているだけ。 すなわち『アクセス権』ですよね。
この『アクセス権』を一種の優待権、まぁ、ゴルフの会員権みたいなものだと捉えて「景品」とするのでしょうか。
まぁ、なんにせよ消費者庁はコンプで得られるデジタルデータを「景品」とみなしたと。
でもそのコンプのためのカードは、ただの「絵合わせ」の絵であってそれらは景品ではないという状態。
だから結局ガチャそのものは規制されないし、また抜け道のようなコンプガチャよりも秀逸なシステムが出来上がるだけでしょう。
そうだなぁ。
今ちょっと思いついたのは、特定の5枚を集めたら、すごいレアなカードと「交換」してくれるとか。
当然元の5枚はなくなるわけなんだけど、そのレアをまた5枚のカードに「再交換」も出来るなんてのはどうでしょうかね。 あくまでも「トレード」。 そのトレードの相手が胴元であるゲーム提供会社そのものってだけで。
あんまり変わりないかな。 法律には詳しくないなりに調べながら書いたけどー。
まぁ、なんにせよ。
面白いゲームやりたいなら自分でつくりなってことですな。
声高々に「ザマー見ろ!」と言っている人というのはなんかそれによって実害を被った人なのでしょうか。
実際にガチャで月10万だの20万だのを支払った人だとすれば多分その時は自分の意思でボタンを押して購入したんでしょうし。
明細を見て正気に戻ったとか言うんであればそれはそいつが愚かなだけでしょ。普通に。
それで「射幸心を煽られた! 金返せ!」ってみっともないにも程がある。
あと、小学生が数十万使ったとか抜かす親御が幾人かいるみたいだけど、それはあんたらの教育が悪いよ。
なんかのきっかけで海外の友達が出来て毎日海外にその携帯で電話してて月末に数十万の請求が来たら誰を訴えるのさ。
電話代ってそもそも常に確認できるものでもなくて、自分で管理するものでしょ。
その管理能力が無いガキンチョに簡単に規制もかけてない携帯を渡す親の気が知れない。
んで。
なんていうか、このweb界隈で「さっさと規制しろ!」って言っている人っていうのは、ソーシャルゲームをやったことがない、もしくはやってみたけど何が面白いか理解できない人が、「あんなのの何が面白いんだっふんっ!」って拗ねているようにしか見えない。 イソップ童話の「どうせあの葡萄はすっぱいだろう」って言っている狐のごとく。
もしくは。
視点を変えると、ユーザではなく、ゲームクリエイターの可能性もあるかなと思い始めてきた。
つまりソーシャルの方がローリスクハイリターンで会社のマンパワーがそこに集中されていて、作りたくもないソーシャルゲームを作らされている技術者の訴え??
だとすると納得が行くんだけど、そんなに大人数いるとも思えないしなぁ。
ゲーム好きが「最近面白いゲームが出ないのはどの会社もソーシャルゲーム開発に注力してるからだ!」っていう
閑話休題
今回のコンプリートガチャ規制の一番のポイントは
「デジタルデータって景品なの?」
という一点につきると思う。
各種レアカードの『絵』に価値があるわけではないですよね。見たいだけならちょっとググれば出てくるんだし(そもそもそれが問題なのかもだけど)、そのカードを『ゲーム内で使う権利』を示すビットデータのフラグを上げ下げしているだけ。 すなわち『アクセス権』ですよね。
この『アクセス権』を一種の優待権、まぁ、ゴルフの会員権みたいなものだと捉えて「景品」とするのでしょうか。
まぁ、なんにせよ消費者庁はコンプで得られるデジタルデータを「景品」とみなしたと。
でもそのコンプのためのカードは、ただの「絵合わせ」の絵であってそれらは景品ではないという状態。
だから結局ガチャそのものは規制されないし、また抜け道のようなコンプガチャよりも秀逸なシステムが出来上がるだけでしょう。
そうだなぁ。
今ちょっと思いついたのは、特定の5枚を集めたら、すごいレアなカードと「交換」してくれるとか。
当然元の5枚はなくなるわけなんだけど、そのレアをまた5枚のカードに「再交換」も出来るなんてのはどうでしょうかね。 あくまでも「トレード」。 そのトレードの相手が胴元であるゲーム提供会社そのものってだけで。
あんまり変わりないかな。 法律には詳しくないなりに調べながら書いたけどー。
まぁ、なんにせよ。
面白いゲームやりたいなら自分でつくりなってことですな。
2012年2月28日火曜日
Androidでポリライン
シューティングゲームなんかでよく見るグネグネ曲がったホーミングレーザー
これを実際に作ろうとすると、中々に負荷がかかるです。
普通のテクスチャと違って頂点が固定化できないのでAndroidでは無理かなーと思ってたんだけどもとりあえずやってみた。
これを実際に作ろうとすると、中々に負荷がかかるです。
普通のテクスチャと違って頂点が固定化できないのでAndroidでは無理かなーと思ってたんだけどもとりあえずやってみた。
private static final int VMAX = 128; private static float v[] = new float[VMAX * 4]; private static float t[] = new float[VMAX * 4]; public static void renderPolyLine(Texture tex, Vectorソースはこんな感じ。 どこかで見かけたソースなんだけど、描画しようとしているある点の一つ前と1つ後から進行方向を求めて、直行ベクトル(と、その反対)に幅を広げてあげている頂点をセットする感じ。 3Dだと外積だけど、2Dなおでxとyを入れ替えるだけ。 で、UVをちょっとずつずらす(綺麗に0~1に収まるようにする)。 テストでホーミングレーザーをした図 予想通り激重いけど、馬鹿みたいにばらまかなければ使えるかな。 でも、ホーミングレーザーって馬鹿みたいにばら撒くから綺麗なのに。 だめじゃな。 ちなみに、レーザー画像をにすればこんな感じでファンシー(?)になるのがステキング。vlist, RenderParam param) { if (tex.isEnable == false) return; GL11 gl = Global.gl; int vCnt = vlist.size(); if (vCnt <= 1) return; if (vCnt > VMAX) return; final float w = param.sx / 2; tex.bind(); gl.glColor4f(param.r, param.g, param.b, param.a); param.setAlpha(); float uv = 1.0f; Vec2 prevPos, pos, nextPos; float vx, vy, len; for (int i = 0; i < vCnt; i++) { prevPos = vlist.get(Math.max(0, i - 1)); pos = vlist.get(i); nextPos = vlist.get(Math.min(vCnt - 1, i + 1)); //一つ手前と、一つ先のベクトルから、向いている方向を得る vx = prevPos.x - nextPos.x; vy = prevPos.y - nextPos.y; len = (float) Math.sqrt(vx * vx + vy * vy); if(len == 0.0f)len = 1; vx *= w / len; vy *= w / len; //指定した横幅に広げる v[i * 4] = pos.x + vy; v[i * 4 + 1] = pos.y - vx; v[i * 4 + 2] = pos.x - vy; v[i * 4 + 3] = pos.y + vx; //テクスチャもセットする t[i * 4] = 0; t[i * 4 + 1] = uv; t[i * 4 + 2] = 1; t[i * 4 + 3] = uv; uv -= 1.0f / vCnt; } gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0); gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, Util.makeFloatBufferArchive(t)); gl.glVertexPointer(2, GL11.GL_FLOAT, 0, Util.makeFloatBufferArchive(v)); gl.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, vCnt*2); Vbo.repair(); }
2012年1月11日水曜日
toURI()は使えないの?
一度ハマった穴にもう一度ハマると死にたくなるのでココに書いておく。
Intentで Intent.ACTION_SEND を指定して、ファイルを添付させたい場合に
って感じなんだけども、このURIの指定をFileインスタンス.toURI() でURIにして指定すると、実行時でExceptionが。
結局、
Intentで Intent.ACTION_SEND を指定して、ファイルを添付させたい場合に
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("application/octet-stream");
intent.putExtra(Intent.EXTRA_STREAM,ここに添付ファイルのURIを);
startActivity(intent);
って感じなんだけども、このURIの指定をFileインスタンス.toURI() でURIにして指定すると、実行時でExceptionが。
結局、
intent.putExtra(Intent.EXTRA_STREAM,Uri.parse("file://" + Fileインスタンス.getAbsolutePath()));
ってやったら上手く行った。
2011年12月12日月曜日
a9padをバージョンアップさせたらoggが再生できくなった
a9padがここに書いてある通り、2.3にバージョンアップできるといういことで、ついついやってしまいました。
動作とかMarketとかは順調なんですが、
今まで動いていた自作のアプリが動かなく。
で、デバッグウインドウで見ると、MediaPlayer.createでnullぽ。
よくわからないんですが、createしようとするものがoggだとnullるっぽいです。
mp3にしてみたら再生出来たので。
でも、SoundPoolでは普通にoggも再生できるのです。
なにこれ。バグ?バグなの?
どうすればいいの??
動作とかMarketとかは順調なんですが、
今まで動いていた自作のアプリが動かなく。
で、デバッグウインドウで見ると、MediaPlayer.createでnullぽ。
よくわからないんですが、createしようとするものがoggだとnullるっぽいです。
mp3にしてみたら再生出来たので。
でも、SoundPoolでは普通にoggも再生できるのです。
なにこれ。バグ?バグなの?
どうすればいいの??
2011年10月4日火曜日
AndroidでZipInputStream
最近、ColladaをAndroidで表示しようとか頑張ってたんですが(すでに過去形)
Metaseqoiaしかり、Colladaしかり、バイナリじゃなくて、テキストファイルなんですね。
なので、読みやすいという利点はあるんですが、逆に容量が増えがち(アニメーション入れると平気で2~3Mにはなる)
Androidには何気にファイル容量制限があって、機種によっては1Mを超えたくらいでオープンできなくなるし(経験則なので、実際のところは不明)
なので、荒業で「Zip圧縮したファイルを展開しながら読む」って事ができないかなーーと。
JavaにはもともとZipInputStreamという素敵なストリームクラスがあるので、リソース読むにもAssetsから直接読むにも、ZipInputStreamを挟むだけ。
というわけで、Meshクラスのロード部分抜粋
気をつけるのは getNextEntry(); をやってあげないと、zip内が1ファイルだとしても読み込み準備ができないということ。
ちなみに複数ファイルを圧縮しちゃった場合は知らんぜよ。
これによって、2MぐらいのColladaファイルも圧縮したら200kぐらいになってくれたので、今まで読めなかったファイルも読めるようになったりしました。
いや、もう、Colladaはアニメーションで挫けたので今更ではあるんですが。
Metaseqoiaしかり、Colladaしかり、バイナリじゃなくて、テキストファイルなんですね。
なので、読みやすいという利点はあるんですが、逆に容量が増えがち(アニメーション入れると平気で2~3Mにはなる)
Androidには何気にファイル容量制限があって、機種によっては1Mを超えたくらいでオープンできなくなるし(経験則なので、実際のところは不明)
なので、荒業で「Zip圧縮したファイルを展開しながら読む」って事ができないかなーーと。
JavaにはもともとZipInputStreamという素敵なストリームクラスがあるので、リソース読むにもAssetsから直接読むにも、ZipInputStreamを挟むだけ。
というわけで、Meshクラスのロード部分抜粋
/**
* Assetsフォルダ内のzipファイルからMeshを作成
* @param zipName zip圧縮されたファイル
* @param loader 使用するローダー
* @return
* @throws IOException
*/
public static Mesh getFromAssetsZip(String zipName,MeshLoader loader) throws IOException{
int resID = zipName.hashCode();
//既に読み込まれている
if (meshMap.containsKey(resID)) {
return meshMap.get(resID);
}
ZipInputStream zis = new ZipInputStream(Global.view.getContext().getAssets().open(zipName));
zis.getNextEntry(); //これやらないと、ファイルが読み込めないよ。 1zipに複数ファイル入れないでね。
Mesh newMesh = loader.load(zis);
meshMap.put(resID, newMesh);
return newMesh;
}
/**
* リソースに登録されたzipからMeshを作成
* @param resID リソースID
* @param loader 使用するローダー
* @return
* @throws IOException
*/
public static Mesh getResZip(int resID,MeshLoader loader) throws IOException{
//既に読み込まれている
if (meshMap.containsKey(resID)) {
return meshMap.get(resID);
}
ZipInputStream zis = new ZipInputStream(Global.view.getResources().openRawResource(resID));
zis.getNextEntry(); //これやらないと、ファイルが読み込めないよ。 1zipに複数ファイル入れないでね。
Mesh newMesh = loader.load(zis);
meshMap.put(resID, newMesh);
return newMesh;
}
気をつけるのは getNextEntry(); をやってあげないと、zip内が1ファイルだとしても読み込み準備ができないということ。
ちなみに複数ファイルを圧縮しちゃった場合は知らんぜよ。
これによって、2MぐらいのColladaファイルも圧縮したら200kぐらいになってくれたので、今まで読めなかったファイルも読めるようになったりしました。
いや、もう、Colladaはアニメーションで挫けたので今更ではあるんですが。
2011年9月30日金曜日
Assetsフォルダにアンダーバーから始まるファイルを置いてはいけない
はまった! 超ハマったので、殴り書きだけど記述しておく。
Androidでファイルを直接読み込みたい時にAssetsに入れておいて、
getResources().getAssets().open(filename);
って感じなんだけど、なんと、このfilenameはアンダーバーから始まるファイルはFileNotFoundException が飛んでしまうのだ。 なーーーんじゃそらー!!!!!!!!
アンダーバーから始まるファイルなんて普通じゃねぇか・・。 なんだこのうんこ仕様は・・・。
Androidでファイルを直接読み込みたい時にAssetsに入れておいて、
getResources().getAssets().open(filename);
って感じなんだけど、なんと、このfilenameはアンダーバーから始まるファイルはFileNotFoundException が飛んでしまうのだ。 なーーーんじゃそらー!!!!!!!!
アンダーバーから始まるファイルなんて普通じゃねぇか・・。 なんだこのうんこ仕様は・・・。
2011年9月19日月曜日
AndroidGameBaseのソース
ちょいちょいと整形してここに貼り付けてーーーってやってたんですが、複数クラスが絡みあってる部分が多くて無理があるので、全部アップロードしてみました。
本当はgoogle codeとか使えば良いような気もするんだけども。 なんかよくわからなかったので
axfcさんところに。
とーいっても、まだまだ作り途中なんですけど。
しかもコレだけ渡されても使い方分からないかもですが。 まぁ、なんかしらのヒントにはなるのかなーとか。
傾きセンサーあたりがまだ微妙なので修正中です。
それでは、良いandroidライフを。
本当はgoogle codeとか使えば良いような気もするんだけども。 なんかよくわからなかったので
axfcさんところに。
とーいっても、まだまだ作り途中なんですけど。
しかもコレだけ渡されても使い方分からないかもですが。 まぁ、なんかしらのヒントにはなるのかなーとか。
傾きセンサーあたりがまだ微妙なので修正中です。
それでは、良いandroidライフを。
2011年9月8日木曜日
フィードバックブラー
GREEのスマホゲームで、「ぶっとびハリー」って物理演算系の・・・
なんというか、「ア○グリーバードのパクリ」みたいなゲームがあるんですが、そのゲーム内で
シーンが切り替わる時に、なんかカッコイイエフェクト(前の画面が残像で残る感じ。 きっと加算合成)
ぽいのをやっているのです。
これは、俗にいうフィードバックブラーってやつですな。
いいなぁ。 ローコストでそれなりのエフェクトに見えるなぁ。
・・・・ということで。 DirectXで言うところのRenderTargetの切り替え。 OpenGLだとFrameBufferObjectで、フィードバックブラーのテストをしてみた。
方法としては、1フレーム前の画面キャプチャをちょっと薄くして重ねて、またキャプチャしておくーーってやつです。
ついでに、回転や拡大したものを重ねてあげるようにすれば派手になりますな。 もちろん見づらくなりますが。
ソースについては、また今度ー。
なんというか、「ア○グリーバードのパクリ」みたいなゲームがあるんですが、そのゲーム内で
シーンが切り替わる時に、なんかカッコイイエフェクト(前の画面が残像で残る感じ。 きっと加算合成)
ぽいのをやっているのです。
これは、俗にいうフィードバックブラーってやつですな。
いいなぁ。 ローコストでそれなりのエフェクトに見えるなぁ。
・・・・ということで。 DirectXで言うところのRenderTargetの切り替え。 OpenGLだとFrameBufferObjectで、フィードバックブラーのテストをしてみた。
方法としては、1フレーム前の画面キャプチャをちょっと薄くして重ねて、またキャプチャしておくーーってやつです。
ついでに、回転や拡大したものを重ねてあげるようにすれば派手になりますな。 もちろん見づらくなりますが。
ソースについては、また今度ー。
2011年7月13日水曜日
今更法線付けました
キューブを横10縦10奥行き10で1000個描画です(わざとバラつかせている)。
ちなみに、これは以前「弱い」と紹介したa9pad(別名opad renesas、さらに別名 N71 もうわけわからん。)で描画してます。
うーん。そこそこ出ているじゃないか。
と・・・。いうか・・・。
実は何の工夫も無しに負荷テストをやってみると、a9padの方がIconia TAB A500 よりFPSが高かったりします。
しっかり調べてないんですが、想像するにもともとの解像度が高い故に起きているんじゃないかと。
glViewportとかで機種毎の解像度の差を吸収しようとすると、この現象が起きると思う。
例をあげると、FF14ベンチでLow(1280x720)とHigh(1920x1080)をごっちゃにするようなもん。
なので、条件を一致させなきゃベンチマークとしての正当性が無くなっちゃうよなぁ。
解決案としては、同じサイズのFBOを作成して、FBOから取ってきたテクスチャを全画面にして描画・・・かなぁー。
もちろん、FBOを全画面に広げる際のコストは解像度によってかわるけどー。 まぁ、それは誤差レベルだと信じたい。
2011年7月6日水曜日
Iconia A500のスペックテストで3D描画をしてみた。
1体3600ポリ(3角)のグラドリエルたんを25体表示(色はランダムで乗算)テストでFPSは見ての通り13fps。
ふむん。
3600x25=90000ポリ? いや、でも行列セットのコストとか、カラーリングとか、テクスチャ切り替えとかあるから、単純にそうも言えないんですよね。
まぁ、でも、速い。 僕の持っている端末の中では当然ダントツ。
でも、あんまりスペックの高い端末で開発しても意味ないんだよなぁ。 低スペックの端末を切り捨てる気が無い限りは。
Iconia A500 も購入
というわけで、既にAmazonでポチっていたIconia A500が4日に届きました。
まぁ、ゲームがプリインストールされてたりするんですが、まったくもってスルーして、PCとデバッグ接続。 俺に開発させてくれぃーーー!
AcerのHPにUSBドライバがあるらしいんですが・・・。 必要なの?例のごとく、今のUSBドライバのinfファイル書き換えるだけで行けるんじゃないの?
っちゅーことで、
android_winusb.inf
の
[Google.NTx86]
の下に
を入力して、ドライバ当てたらサクっと認識しました。
記念SS
うーん。 しかし、A9Padの出番がほとんどなくなるぞ・・・こりゃ・・・。
まぁ、でもあれだ。 ブルートゥースのテストとかで活躍してもらおう。
まぁ、ゲームがプリインストールされてたりするんですが、まったくもってスルーして、PCとデバッグ接続。 俺に開発させてくれぃーーー!
AcerのHPにUSBドライバがあるらしいんですが・・・。 必要なの?例のごとく、今のUSBドライバのinfファイル書き換えるだけで行けるんじゃないの?
っちゅーことで、
android_winusb.inf
の
[Google.NTx86]
の下に
;Iconia Tab a500 %SingleAdbInterface% = USB_Install, USB\Vid_0502&Pid_3325 %CompositeAdbInterface% = USB_Install, USB\Vid_0502&Pid_3325&MI_01
を入力して、ドライバ当てたらサクっと認識しました。
記念SS
うーん。 しかし、A9Padの出番がほとんどなくなるぞ・・・こりゃ・・・。
まぁ、でもあれだ。 ブルートゥースのテストとかで活躍してもらおう。
2011年6月28日火曜日
a9pad とやらを買ってみたんだけど
正式名称がopad renesas なんだか、a9padなんだかよくわかりませんが、値段とマルチタッチ対応ってところで、開発の手助けになるかなーと思って買ってしまいました。(実はIconiaA500もポチり済みなのに、物欲センサーに負けた・・・)
んで、結果から言えば
「買わなくても良かったんじゃないかな!?」
っていう出来。
いや、普通に使う分にはいいのかもですが、開発機としてはかなり微妙。
一番愕然としたのは、加速度センサーが整数でしか返ってこない事かなぁ。
何の冗談かと思ったけど、本当です。なんか-16~16までの整数が帰ってくるっぽいです。
深度バッファもなんかヤバいのか、まだしっかりテストしてないけど、スカイボックスが表示されないのは何故だろう(その他IS01やLiftTouchNote,MEDIASなどではでてる)
あと、デュアルコアで1Gって書いてあったんだけども、これって、1コアが500GHzで足して1GHzって事なのかなぁ。
なんか弱い。具体的には言えないけど、なんか弱い。
そして、メーカーが存在しないので、最新ファームがどこにあるのかさっぱりわからないのも難点です。
まぁ、価格相応っちゃー相応なのかなぁーーーー。
んで、結果から言えば
「買わなくても良かったんじゃないかな!?」
っていう出来。
いや、普通に使う分にはいいのかもですが、開発機としてはかなり微妙。
一番愕然としたのは、加速度センサーが整数でしか返ってこない事かなぁ。
何の冗談かと思ったけど、本当です。なんか-16~16までの整数が帰ってくるっぽいです。
深度バッファもなんかヤバいのか、まだしっかりテストしてないけど、スカイボックスが表示されないのは何故だろう(その他IS01やLiftTouchNote,MEDIASなどではでてる)
あと、デュアルコアで1Gって書いてあったんだけども、これって、1コアが500GHzで足して1GHzって事なのかなぁ。
なんか弱い。具体的には言えないけど、なんか弱い。
そして、メーカーが存在しないので、最新ファームがどこにあるのかさっぱりわからないのも難点です。
まぁ、価格相応っちゃー相応なのかなぁーーーー。
2011年6月20日月曜日
AndroidでMetasequoiaのmqoを読み込むコード その5 MQOLoaderクラス
やっとメタセコファイルを読み込む所です。
とは言っても、テキストを1行づつ解釈していくだけなんですけど。
のように、2つの3角ポリに変換をしているところでしょうか。
同じように、UVなんかも割り振ってあげる必要が。
とは言っても、テキストを1行づつ解釈していくだけなんですけど。
package com.dividebyzero.KszGameBase;
import java.io.BufferedReader;
import java.util.StringTokenizer;
import android.util.Log;
public class MQOLoader extends MeshLoader{
private Subset[] subset;
private int materialCnt;
@Override
public Mesh load(BufferedReader in) throws Exception {
TokenReader tokenReader = new TokenReader(in);
float v[] = null;
int isVisible = 1;
for (String token = tokenReader.getLine(); (token = tokenReader.getString()) != null;) {
if ("visible".equals(token)) {
isVisible = tokenReader.getInt();
Log.v("debug", "visible:"+isVisible);
}
if (isVisible != 0) {
if ("vertex".equals(token)) {
v = vertexParse(tokenReader);
} else if ("color".equals(token)) {
Log.v("debug", "color finded.");
} else if ("face".equals(token)) {
faceParse(tokenReader, v);
} else if ("Material".equals(token)) {
materialParse(tokenReader);
}
}
}
//subset配列 で、Meshクラスを作る
return new Mesh(subset);
}
private void materialParse(TokenReader in) throws Exception {
Log.v("debug", "material finded.");
String tToken;
materialCnt = in.getInt() + 1;
Log.v("debug", "material is " + materialCnt + ".");
subset = new Subset[materialCnt];
for (int i = 0; i < materialCnt; i++){
subset[i] = new Subset();
}
tToken = in.getString();
Subset mat;
if ("{".equals(tToken)) {
for (int i = 0; i < materialCnt; i++) {
Log.v("debug", "material name is " + in.getString() + ".");
mat = subset[i]; //ローカル化して速度を稼ぐ(雀の涙程度)
while (!in.empty()) {
tToken = in.getString();
if ("tex".equals(tToken)) {
mat.addTexture(Texture.get(in.getString2()));
} else if ("col".equals(tToken)) {
mat.r = in.getFloat();
mat.g = in.getFloat();
mat.b = in.getFloat();
mat.a = in.getFloat();
} else if ("dif".equals(tToken)) {
mat.diffuse = in.getFloat();
} else if ("amb".equals(tToken)) {
mat.ambient = in.getFloat();
}
}
}
}
//マテリアル無し用マテリアル
mat = subset[materialCnt-1];
mat.r = 0.5f;
mat.g = 0.5f;
mat.b = 0.5f;
mat.a = 0.5f;
mat.diffuse = 0.5f;
mat.ambient = 0.5f;
}
private float[] vertexParse(TokenReader in) throws Exception {
Log.v("debug", "vertex parse start.");
String tToken;
float[] tVertices;
int tNumberOfVertex = in.getInt();
Log.v("debug", "vertex is " + tNumberOfVertex + ".");
tVertices = new float[tNumberOfVertex * 3];
tToken = in.getString();
if ("{".equals(tToken)) {
for (int i = 0; i < tNumberOfVertex; i++) {
tVertices[i * 3 + 0] = in.getFloat();
tVertices[i * 3 + 1] = -in.getFloat();
tVertices[i * 3 + 2] = -in.getFloat();
}
tToken = in.getString();
}
Log.v("debug", "vertex parse end.");
return tVertices;
}
private void faceParse(TokenReader in, float v[]) throws Exception {
Log.v("debug", "face finded.");
String tToken;
int tNumberOfFace = in.getInt();
Log.v("debug", "face is " + tNumberOfFace + ".");
tToken = in.getString();
int vTemp[] = new int[4];
int matNo = 0;
if ("{".equals(tToken)) {
for (int i = 0; i < tNumberOfFace; i++) {
int vCnt = in.getInt(); //そのfaceが何頂点か。
boolean isUV = false;
boolean isMaterial = false;
while (!in.empty()) {
tToken = in.getString();
if ("V".equals(tToken)) {
for (int j = 0; j < vCnt; j++) {
vTemp[j] = in.getInt();
}
} else if ("M".equals(tToken)) {
//順番的にMがあとで来てしまうので、頂点をとっておいて、マテリアル番号毎に
isMaterial = true;
matNo = in.getInt();
subset[matNo].addVertex(v[vTemp[2] * 3], v[vTemp[2] * 3 + 1], v[vTemp[2] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[1] * 3], v[vTemp[1] * 3 + 1], v[vTemp[1] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[0] * 3], v[vTemp[0] * 3 + 1], v[vTemp[0] * 3 + 2]);
if (vCnt == 4) {
subset[matNo].addVertex(v[vTemp[3] * 3], v[vTemp[3] * 3 + 1], v[vTemp[3] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[2] * 3], v[vTemp[2] * 3 + 1], v[vTemp[2] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[0] * 3], v[vTemp[0] * 3 + 1], v[vTemp[0] * 3 + 2]);
}
} else if ("UV".equals(tToken)) {
float uvTemp[] = new float[8];
isUV = true;
switch (vCnt) {
case 3: //三角ポリなので、そのまま
for (int j = 0; j < 6; j++) {
uvTemp[j] = in.getFloat();
}
subset[matNo].addUV(uvTemp[4], uvTemp[5]);
subset[matNo].addUV(uvTemp[2], uvTemp[3]);
subset[matNo].addUV(uvTemp[0], uvTemp[1]);
break;
case 4://4角ポリなので、分割して放り込む
for (int j = 0; j < 8; j++) {
uvTemp[j] = in.getFloat();
}
subset[matNo].addUV(uvTemp[4], uvTemp[5]);
subset[matNo].addUV(uvTemp[2], uvTemp[3]);
subset[matNo].addUV(uvTemp[0], uvTemp[1]);
subset[matNo].addUV(uvTemp[6], uvTemp[7]);
subset[matNo].addUV(uvTemp[4], uvTemp[5]);
subset[matNo].addUV(uvTemp[0], uvTemp[1]);
break;
}
}
}
//マテリアルが当たってなかった
if(isMaterial == false){
matNo = materialCnt - 1; //無名マテリアルに当てる
subset[matNo].addVertex(v[vTemp[2] * 3], v[vTemp[2] * 3 + 1], v[vTemp[2] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[1] * 3], v[vTemp[1] * 3 + 1], v[vTemp[1] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[0] * 3], v[vTemp[0] * 3 + 1], v[vTemp[0] * 3 + 2]);
if (vCnt == 4) {
subset[matNo].addVertex(v[vTemp[3] * 3], v[vTemp[3] * 3 + 1], v[vTemp[3] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[2] * 3], v[vTemp[2] * 3 + 1], v[vTemp[2] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[0] * 3], v[vTemp[0] * 3 + 1], v[vTemp[0] * 3 + 2]);
}
}
//UVが当たってなかった
if (isUV == false) {
subset[matNo].addUV(0, 0);
subset[matNo].addUV(0, 0);
subset[matNo].addUV(0, 0);
if (vCnt == 4) {
subset[matNo].addUV(0, 0);
subset[matNo].addUV(0, 0);
subset[matNo].addUV(0, 0);
}
}
}
tToken = in.getString();
}
Log.v("debug", "face parse end.");
Log.v("debug", "v=" + subset[0].getVCnt());
}
}
final class TokenReader {
private BufferedReader mReader;
private StringTokenizer mStringTokenizer;
private boolean mIsInitialized;
public TokenReader(BufferedReader bufferedReader) throws Exception {
mReader = bufferedReader;
mIsInitialized = refreshTokenizer();
}
private boolean refreshTokenizer() throws Exception {
String line;
while ((line = mReader.readLine()) != null)
if (line.length() != 0 && line.charAt(0) != '#') {
mStringTokenizer = new StringTokenizer(line, ",; \t()", false);
return true;
}
return false;
}
public String next() throws Exception {
if (mIsInitialized) {
if (mStringTokenizer.hasMoreTokens()) {
String token = mStringTokenizer.nextToken();
return token;
}
if (!(mIsInitialized = refreshTokenizer())) return null;
if (mStringTokenizer.hasMoreTokens()) return mStringTokenizer.nextToken();
}
return null;
}
public boolean empty() {
return !mStringTokenizer.hasMoreTokens();
}
public float getFloat() throws Exception {
return Float.parseFloat(next());
}
public int getInt() throws Exception {
return Integer.parseInt(next());
}
public String getString() throws Exception {
return next();
}
public String getString2() throws Exception {
return next().replace('"', ' ').trim();
}
public String getLine() throws Exception {
nextLine();
return mReader.readLine();
}
public void nextLine() {
while (mStringTokenizer.hasMoreTokens()) {
mStringTokenizer.nextToken();
}
}
public void close() throws Exception {
mReader.close();
}
}
ちゅーか、TokenReaderはどこかから拾った物です・・・。 どこだったかなぁ・・。
肝としては、全て3角ポリに変換するために、4角の面があった場合に
subset[matNo].addVertex(v[vTemp[2] * 3], v[vTemp[2] * 3 + 1], v[vTemp[2] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[1] * 3], v[vTemp[1] * 3 + 1], v[vTemp[1] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[0] * 3], v[vTemp[0] * 3 + 1], v[vTemp[0] * 3 + 2]);
if (vCnt == 4) {
subset[matNo].addVertex(v[vTemp[3] * 3], v[vTemp[3] * 3 + 1], v[vTemp[3] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[2] * 3], v[vTemp[2] * 3 + 1], v[vTemp[2] * 3 + 2]);
subset[matNo].addVertex(v[vTemp[0] * 3], v[vTemp[0] * 3 + 1], v[vTemp[0] * 3 + 2]);
}
のように、2つの3角ポリに変換をしているところでしょうか。
同じように、UVなんかも割り振ってあげる必要が。
2011年6月7日火曜日
AndroidでMetasequoiaのmqoを読み込むコード その4 Meshクラス
まだ続くのかよって感じですね。 なんでこんなに小出しかって? 仕事中にこっそり書いてるからだよ!!
と、いうわけで、やっとMeshクラスです。 今後メタセコイア以外のファイル形式にも対応するかもなので、先にMeshLoaderなんつー抽象クラスを作っておいて、継承して各種ローダーを作ってもらおうかと。
とりあえず、今あるのはmqoだけ。 (学生が同じ要領でfbxローダーを作ったらしい。未確認)
ともかく、ローダークラス
このローダークラスを指定して実際にMeshクラスを作る流れ。 拡張子で自動で選ぶとか、クラス名からロードしてプラグインみたいな感じにするとか・・・。 まぁ、夢はひろがリングだけど、そこまでする必要ないかなって気もします。
後はローダー部分だけです。
いや、そこがメインなんですけどね・・・。 需要があるのかないのか・・・。
本当はもうプロジェクト毎ザクっとどこかにアップしたいところなんですけど。 bloggerってそういうファイル置き場って無いのかな・・・。
うむん。
と、いうわけで、やっとMeshクラスです。 今後メタセコイア以外のファイル形式にも対応するかもなので、先にMeshLoaderなんつー抽象クラスを作っておいて、継承して各種ローダーを作ってもらおうかと。
とりあえず、今あるのはmqoだけ。 (学生が同じ要領でfbxローダーを作ったらしい。未確認)
ともかく、ローダークラス
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* 抽象クラス 3Dモデルを読み込むためのインタフェース
* これを軽傷して自前のローダークラスをつくってもらいたい
* @author ksz
*
*/
public abstract class MeshLoader {
public Mesh load(int id) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(Global.view.getResources().openRawResource(id)));
return load(in);
}
public Mesh load(String name) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(Global.view.getContext().getAssets().open(name)));
return load(in);
}
/**
* コレをオーバーライドして、自前のローダー等を作ってください。
* @param in
* @return
* @throws Exception
*/
abstract public Mesh load(BufferedReader in) throws Exception;
}
このローダークラスを指定して実際にMeshクラスを作る流れ。 拡張子で自動で選ぶとか、クラス名からロードしてプラグインみたいな感じにするとか・・・。 まぁ、夢はひろがリングだけど、そこまでする必要ないかなって気もします。
import java.util.HashMap;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import android.content.Context;
/**
* @author ksz
*
*/
public class Mesh {
private Vbo[] vbo;
private Texture[] texs;
/**
* 指定されたSubsetの配列からVBOとテクスチャのセットを作成
* そのままSubsetを使えば良いと思うかもだけど、SubsetクラスのFloarArrayListとかのインスタンスが残るのが嫌だったので。
* @param subset
*/
public Mesh(Subset[] subset) {
vbo = new Vbo[subset.length];
texs = new Texture[subset.length];
for (int i = 0; i < subset.length; i++) {
vbo[i] = subset[i].makeVBO();
texs[i] = subset[i].getTex();
}
}
/**
* 全サブセット描画
* @param x 座標x
* @param y 座標y
* @param z 座標z
* @param param レンダリングパラメーター
*/
public void render(float x, float y, float z, RenderParam param) {
GL11 gl = Global.gl;
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glPushMatrix();
gl.glTranslatef(x, y, z);
gl.glRotatef(param.az, 0, 0, 1);
gl.glRotatef(param.ay, 0, 1, 0);
gl.glRotatef(param.ax, 1, 0, 0);
gl.glScalef(param.sx, param.sy, param.sz);
int len = vbo.length;
for (int i = 0; i < len; i++) {
if (texs[i] != null) {
Texture.setTexture(texs[i].texID);
} else {
Texture.setTexture(0);
}
vbo[i].draw();
}
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glPopMatrix();
}
/**
* 管理用マップ
*/
public static HashMap meshMap = new HashMap();
/**
* 文字列からリソース読み込み
* @param resName 読み込むリソース名
* @param loader 使用するローダー
* @return Meshインスタンス。 すでに読み込まれている場合はそれを返します。
* @throws Exception
*/
public static Mesh get(String resName,MeshLoader loader)throws Exception {
Context context = Global.view.getContext();
int point = resName.lastIndexOf(".");
if (point != -1) {
resName = resName.substring(0, point);
}
int id = context.getResources().getIdentifier(resName, "drawable", context.getPackageName());
return get(id,loader);
}
/**
* 文字列からリソース読み込み
* @param resID 読み込むリソースのID
* @param resName 読み込むリソース名
* @param loader 使用するローダー
* @return Meshインスタンス。 すでに読み込まれている場合はそれを返します。
* @throws Exception
*/
public static Mesh get(int resID,MeshLoader loader)throws Exception {
//既に読み込まれている
if (meshMap.containsKey(resID)) {
return meshMap.get(resID);
}
//まだ読み込まれていないので新規で読み込み
Mesh newMesh = loader.load(resID);
meshMap.put(resID, newMesh);
return newMesh;
}
}
後はローダー部分だけです。
いや、そこがメインなんですけどね・・・。 需要があるのかないのか・・・。
本当はもうプロジェクト毎ザクっとどこかにアップしたいところなんですけど。 bloggerってそういうファイル置き場って無いのかな・・・。
うむん。
2011年6月1日水曜日
AndroidでMetasequoiaのmqoを読み込むコード その3 Subsetクラス
まだまだ続いています。 メタセコファイル読み込み下準備。
頂点配列、UV配列をモデルのファイルから読み込んで管理するために、Subsetクラスを作ってます。
モデルデータをパースする際、頂点数などが先に分かっている場合はいいのですが、それが明記されてないファイル形式も多々あるので、可変長にせざるを得ない。 でも可変長だからといって、単純にArrayListみたいにするとシンタックスシュガーで隠れてはいるけどオートボクシング(裏でラッパークラスへの変換)が行われているはずであってそんなのやってもらっちゃー困る。
ので、FloatArrayListなんつーものも作ってあります。
そして、↑を使ってSubsetクラス。
ただデータを貯めこむのと、そこからVBOを作れるだけ。
ところどころメンバ変数がpublicなのは単純に面倒だったのと、これを直接いじる人もおらんだろうという想像から。 加えて言うならこれをセッターゲッターにしたときのオーバーヘッドを減らしたかった・・・という欺瞞もありで。
頂点配列、UV配列をモデルのファイルから読み込んで管理するために、Subsetクラスを作ってます。
モデルデータをパースする際、頂点数などが先に分かっている場合はいいのですが、それが明記されてないファイル形式も多々あるので、可変長にせざるを得ない。 でも可変長だからといって、単純にArrayList
ので、FloatArrayListなんつーものも作ってあります。
package com.dividebyzero.KszGameBase; /** * 勝手に作ったFloat型のArrayList * ArrayListでも悪くはないけど、オートボクシングがかかって内部でfloat→new Float()がかかってると考えると耐えられないので * 作ってみた。効果は不明。 * @author ksz * */ class FloatArrayList { private int lastIndex; private float[] array; private static final float[] emptyArray = new float[0]; public FloatArrayList() { this(0); } public FloatArrayList(int capacity) { lastIndex = 0; try { array = newElementArray(capacity); } catch (NegativeArraySizeException e) { throw new IllegalArgumentException(); } } private float[] newElementArray(int size) { if (size == 0) { return emptyArray; } return new float[size]; } public boolean add(float object) { if (lastIndex == array.length) { //いっぱいになっちゃった int size = size(); int increment = size / 2; if (increment < 12) { increment = 12; } float[] newArray = newElementArray(size + increment); if (size > 0) { System.arraycopy(array, 0, newArray, 0, size); lastIndex = size; } array = newArray; } array[lastIndex++] = object; return true; } public float[] toArray() { int size = size(); float[] result = new float[size]; System.arraycopy(array, 0, result, 0, size); return result; } public float[] getFloatArray() { return array; } public int size() { return lastIndex; } }
そして、↑を使ってSubsetクラス。
ただデータを貯めこむのと、そこからVBOを作れるだけ。
/**
* Meshを作る前段階のクラス。 必要かどうかよくわかんなくなってきた
* @author ksz
*
*/
public class Subset {
private FloatArrayList vertexList = new FloatArrayList();
private FloatArrayList uvList = new FloatArrayList();
private Texture tex;
private int vCnt, uvCnt;
public float r, g, b, a, diffuse, ambient;
public Texture getTex() {
return tex;
}
public int getVCnt() {
return vCnt;
}
public int getUVCnt() {
return uvCnt;
}
/**
* 頂点を格納していく
* @param x
* @param y
* @param z
*/
public void addVertex(float x, float y, float z) {
vertexList.add(x);
vertexList.add(y);
vertexList.add(z);
vCnt++;
}
/**
* UV座標の格納
* @param u
* @param v
*/
public void addUV(float u, float v) {
uvList.add(u);
uvList.add(v);
uvCnt++;
}
/**
* 仕様しているテクスチャのセット
* @param tex
*/
public void addTexture(Texture tex) {
this.tex = tex;
}
/**
* セットされた頂点、UVからVBOを作成して返す
* @return 作成されたVBO
*/
public Vbo makeVBO() {
Vbo retVBO = new Vbo();
retVBO.makeBuffer(vertexList.getFloatArray(), vertexList.size(), uvList.getFloatArray(), uvList.size());
retVBO.r = r;
retVBO.g = g;
retVBO.b = b;
retVBO.diffuse = diffuse;
retVBO.ambient = ambient;
return retVBO;
}
}
ところどころメンバ変数がpublicなのは単純に面倒だったのと、これを直接いじる人もおらんだろうという想像から。 加えて言うならこれをセッターゲッターにしたときのオーバーヘッドを減らしたかった・・・という欺瞞もありで。
2011年5月31日火曜日
AndroidでMetasequoiaのmqoを読み込むコード その2 Textureクラス
大分空きましたが続きー
VBOとTextureをワンセットにすることで、DirectXで言うサブセットみたいな処理ができるので、Textureクラスも当然必要です(テクスチャの切り替えを最小限にするため)
しかし、ソースのほとんどが2D用だっていうのは僕と君だけの秘密だぞ。
VBOとTextureをワンセットにすることで、DirectXで言うサブセットみたいな処理ができるので、Textureクラスも当然必要です(テクスチャの切り替えを最小限にするため)
package com.dividebyzero.KszGameBase;
import java.util.HashMap;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.util.Log;
/**
* テクスチャクラス
* @author ksz
*
*/
public class Texture {
public int resID, texID, w, h;
private static final RenderParam param = new RenderParam();
/**
* @param resID リソースID
* @param texID OpenGLでのテクスチャID
* @param w 横幅
* @param h 縦幅
*/
public Texture(int resID, int texID, int w, int h) {
this.resID = resID;
this.texID = texID;
this.w = w;
this.h = h;
}
/**
* staticのrenderを呼びやすくしただけ
*/
public void render(float x,float y){
render(this,x,y,0,param.init());
}
/**
* staticのrenderを呼びやすくしただけ
*/
public void render(float x, float y, RenderParam param) {
render(this, x, y, 0, param);
}
/**
* staticのrenderを呼びやすくしただけ
*/
public void render(float x, float y, float z, RenderParam param) {
render(this, x, y, z, param);
}
/**
* staticのポリラインを呼びやすくしただけ
*/
public void renderPolyLine(float sx, float sy, float ex, float ey, RenderParam param) {
renderPolyLine(this, sx, sy, ex, ey, param);
}
/**
* テクスチャのバインド
*/
public void bind() {
setTexture(texID);
}
//////////////////ここから管理ゾーン
public static HashMap<Integer, Texture> texMap = new HashMap<:Integer, Texture>();
/**
* リソースIDではなく、文字列からテクスチャインスタンスを作成
* @param resName 読み込みたい文字列 "***.png"のようにしても、拡張子部分は捨てられる
* @return 作成されたテクスチャインスタンス
*/
public static Texture get(String resName) {
Context context = Global.view.getContext();
int point = resName.lastIndexOf(".");
if (point != -1) {
resName = resName.substring(0, point);
}
Log.v("debug", resName);
int id = context.getResources().getIdentifier(resName, "drawable", context.getPackageName());
return get(id);
}
/**
* リソースIDからテクスチャインスタンスを作成
* @param resID 読み込みたいリソース
* @return 作成されたテクスチャインスタンス。既に読み込まれている場合はそのインスタンス
*/
public static Texture get(int resID) {
if (texMap.containsKey(resID)) {
return texMap.get(resID);
}
GL10 gl = Global.gl;
//リソースBMPを読み込む
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inScaled = false; //勝手に拡大縮小するので止める
Bitmap bitmap = BitmapFactory.decodeResource(Global.view.getResources(), resID, opt);
//グラボのテクスチャとして登録
int[] texID = new int[1]; //グラボからテクスチャIDを受け取るための変数
gl.glGenTextures(1, texID, 0); //グラボから使ってないテクスチャIDを取得
Texture.setTexture(texID[0]);
//BMPをテクスチャに割り当てる
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
//テクスチャパラメータをセット
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, //拡大の時は
GL10.GL_LINEAR); //線形補間
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, //縮小の時は
GL10.GL_LINEAR); //線形補間
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, //UVがはみ出したときどうしますか
GL10.GL_CLAMP_TO_EDGE); //端っこで止める
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, //UVがはみ出したときどうしますか
GL10.GL_CLAMP_TO_EDGE); //端っこで止める
Texture tex = new Texture(resID, texID[0], bitmap.getWidth(), bitmap.getHeight());
bitmap.recycle();
texMap.put(resID, tex);
return tex;
}
private static Vbo vbo = null;
private static int currentTexID = -1;
/**
* テクスチャをOpenGLにセット
* 同じテクスチャが連続でセットされないようにしている
* @param id セットしたいテクスチャ
*/
public static void setTexture(int id) {
if (currentTexID != id) {
currentTexID = id;
Global.gl.glBindTexture(GL10.GL_TEXTURE_2D, id);
}
}
/**
* 指定されたテクスチャを描画 z軸が無いVer
* @param tex 描画したいテクスチャ
* @param x 座標x
* @param y 座標y
* @param param 描画パラメータ
*/
public static void render(Texture tex, float x, float y, RenderParam param) {
render(tex, x, y, 0, param);
}
/**
* 指定されたテクスチャを描画
* @param tex 描画したいテクスチャ
* @param x 座標x
* @param y 座標y
* @param z 座標z 2Dゲームの場合は0で
* @param param 描画パラメータ
*/
public static void render(Texture tex, float x, float y, float z, RenderParam param) {
if (vbo == null) {
//頂点4つ分用意
float v[] = {
//頂点
0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0,
};
float t[] = {
//UV
0, 0, 0, 1, 1, 0, 1, 1,
};
vbo = new Vbo();
vbo.makeBuffer(v, t);
}
vbo.bindVBO();
GL11 gl = Global.gl;
setTexture(tex.texID);
gl.glColor4f(param.r, param.g, param.b, param.a);
if (param.cw + param.ch < 0) {
param.cw = (float) tex.w;
param.ch = (float) tex.h;
}
if (param.isCenterOrigin) {
if (param.isDestsize) {
param.ox = param.dw / 2;
param.oy = param.dh / 2;
} else {
param.ox = param.cw * Math.abs(param.sx) / 2;
param.oy = param.ch * Math.abs(param.sy) / 2;
}
}
if (RenderParam.oldAddAlphaMode != param.isAddAlpha) {
if (param.isAddAlpha) {
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
} else {
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
}
}
RenderParam.oldAddAlphaMode = param.isAddAlpha;
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glTranslatef(param.cx + param.cw / tex.w * param.xindex, param.cy + param.ch / tex.h * param.yindex, 0);
gl.glScalef(param.cw / tex.w, param.ch / tex.h, 1);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glPushMatrix(); //カメラ行列を退避
//移動させつつ、原点指定しつつ
if (param.isDestsize) {
gl.glTranslatef(0.5f * param.dw - param.ox + x, 0.5f * param.dh - param.oy + y, z);
} else {
gl.glTranslatef(0.5f * param.cw * Math.abs(param.sx) - param.ox + x, 0.5f * param.ch * Math.abs(param.sy)
- param.oy + y, z);
}
gl.glRotatef(param.ax, 0, 0, 1); //回転(なぜかラジアンじゃない)
if (param.isDestsize) {
gl.glScalef(param.dw, param.dh, 1); //拡大縮小
} else {
gl.glScalef(param.sx * param.cw, param.sy * param.ch, 1); //拡大縮小
}
gl.glTranslatef(-0.5f, -0.5f, 0); //中心を原点に回転するための小細工
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4); //横1縦1の頂点描画
gl.glPopMatrix(); //カメラ行列を戻す
}
/**
* ポリライン(テクスチャを線のように描画)
* @param tex 描画したいテクスチャ
* @param sx 始点x
* @param sy 始点y
* @param ex 終点x
* @param ey 終点y
* @param param パラメータ、幅など
*/
void renderPolyLine(Texture tex, float sx, float sy, float ex, float ey, RenderParam param) {
Vec3 tang = new Vec3(ex - sx, ey - sy, 0);
float angle = (float) Math.atan2(tang.y, tang.x) * Util.TOANGLE;
param.setDestSize(Math.abs(tang.getLength()), Math.abs(param.sx)).setAngle(angle);
param.setCenterOrigin(true);
render(tex, sx + tang.x / 2, sy + tang.y / 2, param);
}
/**
* 指定されたリソースを描画
* 指定されたリソースがまだ読み込まれていない場合は読み込まれる
* @param resID R.drawable.iconなど、リソースのIDを直指定
* @param x 描画位置x
* @param y 描画位置y
* @param param RenderParamのインスタンスによって描画パラメータを操作
*/
public static void render(int resID, int x, int y, RenderParam param) {
render(get(resID), x, y, param);
}
/**
* 管理されているテクスチャを全て開放
*/
public static void release() {
int textureID[] = new int[1];
for(Texture tex:texMap.values()){
textureID[0] = tex.texID;
Global.gl.glDeleteTextures(1, textureID, 0);
}
texMap.clear();
texMap = null;
}
/**
* 画面クリア処理
* Textureクラスに入れるのが適当がどうかは微妙なところ
* 深度を有効化しないと、深度バッファのクリアがうまくいかない場合があるようなので、毎回入れてます。 負荷だけど。
*/
public static void clear() {
Global.gl.glEnable(GL10.GL_DEPTH_TEST);
Global.gl.glDepthMask(true); // 深度バッファ書き込み有効
Global.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
}
/**
* 画面をクリアします
* @param r 赤(0.0f~1.0f)
* @param g 緑(0.0f~1.0f)
* @param b 青(0.0f~1.0f)
* @param a アルファ値(0.0f~1.0f)
*/
public static void clear(float r,float g,float b,float a) {
Global.gl.glClearColor(r,g,b,a);
clear();
}
/**
* 画面をクリアします
* @param r 赤(0~255)
* @param g 緑(0~255)
* @param b 青(0~255)
* @param a アルファ値(0~255)
*/
public static void clear(int r,int g,int b,int a) {
Global.gl.glClearColor(r/255.0f,g/255.0f,b/255.0f, a / 255.0f);
clear();
}
}
しかし、ソースのほとんどが2D用だっていうのは僕と君だけの秘密だぞ。
2011年5月25日水曜日
AndroidでMetasequoiaのmqoを読み込むコード その1 VBO
以前書いた記事でmqo読んでるぜーって書いたらなんだか奇特な方がソースを晒してくれってな事だったので、ざっと。
でも、結構絡み合ってるんですよ? これが?
まず、VertexObjectで描画が基本なので、VBOクラスから。
ちなみに、ライティングは無視してるので、勝手に拡張してね。
Global.gl とかがさも当然のように使われてますが、GL11インスタンスはいろんなところから使うので、グローバルっぽく使うためにpublic static な領域にGl11インスタンスを渡してある前提で。
まだまだ続きます。
でも、結構絡み合ってるんですよ? これが?
まず、VertexObjectで描画が基本なので、VBOクラスから。
import java.nio.FloatBuffer;
import java.util.ArrayList;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
/**
* VertexBufferObjectクラス
* @author ksz
*/
public class Vbo {
public float r=1,g=1,b=1,a=1,diffuse=1,ambient=1;
public int vSize, tSize;
private int vboID = -1;
private static int oldID = -1;
/**
* GLにVBOをセット
*/
public void bindVBO() {
if (oldID == vboID) return; //既に同じバッファがセットされていたら無視
if (vboID < 0) return; //makeBufferされてないようなので無視
oldID = vboID;
GL11 gl = Global.gl;
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vboID);
gl.glVertexPointer(3, GL11.GL_FLOAT, 0, 0); //頂点バッファ
if (tSize != 0) {
gl.glTexCoordPointer(2, GL11.GL_FLOAT, 0, vSize); //UVバッファ
}
}
/**
* VBOを描画します
*/
public void draw() {
GL11 gl = Global.gl;
//テクスチャ行列を外す
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glColor4f(r, g, b, a);
bindVBO();
//vSizeは頂点配列のバイト数なので、floatにするのに/4 TRIANGLESなので3角ポリで/3 で/12
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, vSize / 12);
}
/**
* 頂点とUVの配列からVBO作成
* @param v 頂点配列
* @param t UV配列
*/
public void makeBuffer(float v[], float t[]) {
vSize = v.length;
tSize = 0;
if (t != null && t.length != 0) {
tSize = t.length;
}
makeBuffer(v,vSize, t,tSize);
}
/**
* 頂点とUVの配列からVBO作成(サイズ指定版)
* @param v 頂点配列
* @param vCnt 頂点配列の有効要素数
* @param t UV配列
* @param tCnt UV配列の有効要素数
*/
public void makeBuffer(float v[],int vCnt, float t[],int tCnt) {
GL11 gl = Global.gl;
vSize = vCnt * 4;
tSize = tCnt * 4;
int vbo[] = new int[1];
gl.glGenBuffers(1, vbo, 0);
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vbo[0]);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, vSize + tSize, null, GL11.GL_STATIC_DRAW);
gl.glBufferSubData(GL11.GL_ARRAY_BUFFER, 0, vSize, FloatBuffer.wrap(v));
if (tSize != 0) {
gl.glBufferSubData(GL11.GL_ARRAY_BUFFER, vSize, tSize, FloatBuffer.wrap(t));
}
vboID = vbo[0];
}
}
ちなみに、ライティングは無視してるので、勝手に拡張してね。
Global.gl とかがさも当然のように使われてますが、GL11インスタンスはいろんなところから使うので、グローバルっぽく使うためにpublic static な領域にGl11インスタンスを渡してある前提で。
まだまだ続きます。
登録:
投稿 (Atom)





