FC2ブログ

頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


ARKitのARFaceGeometryでのメッシュの切られ方を確認する

ARFaceGeometryのプロパティには
triangleCount
triangleIndices
などがあり、これを使うとどの頂点を使い三角形を表示しているのかがわかる。
{
    const int16_t *triangleIndices = arFaceGeometry.triangleIndices;

    for (NSUInteger ite=0;ite<10;ite++) {
        NSUInteger itex3 = ite * 3 ;
        NSLog(@"%d,%d,%d",triangleIndices[itex3],triangleIndices[itex3+1],triangleIndices[itex3+2]);
}
↑例えばこのようなコードで最初の10個の三角形を構成する頂点の頂点番号を表示させてみる。

頂点番号の出力結果

↑出力結果。
頂点番号がわかれば、verticesプロパティからその頂点の座標がわかる。残念ながら法線情報はなさそう。


メッシュの切り方を確認するため、三角形を構成する3つの頂点から3つの直線を描く。
triangleIndicesが持つ1つの三角形に関する情報は上記のように3つの頂点の頂点番号だ。直線を描くには始点と終点それぞれ頂点番号の二つが必要。なので、あらかじめそのようなデータ列を作成しておく。
int16_t *lineVertexIndices;//インスタンス変数

{
    lineVertexIndices = calloc(arFaceGeometry.triangleCount*6, sizeof(int16_t));
    const int16_t *triangleIndices = arFaceGeometry.triangleIndices;
    for (NSUInteger ite=0;ite<arFaceGeometry.triangleCount;ite++) {
        NSUInteger itex3 = ite * 3 ;
        NSUInteger itex6 = ite * 6;
        lineVertexIndices[itex6+0] = triangleIndices[itex3+0];//始点1
        lineVertexIndices[itex6+1] = triangleIndices[itex3+1];//終点1
        lineVertexIndices[itex6+2] = triangleIndices[itex3+1];//始点2
        lineVertexIndices[itex6+3] = triangleIndices[itex3+2];//終点2
        lineVertexIndices[itex6+4] = triangleIndices[itex3+2];//始点3
        lineVertexIndices[itex6+5] = triangleIndices[itex3+0];//終点3
    }
}
↑なおこの処理は毎フレーム行う必要はない。

以下のようなコードでたくさんの線を持つ1つのSCNNodeオブジェクトを作成できる。
+(SCNNode *)makeTriangleLinesNodeFor_simd_float3:(simd_float3 *)positions
                                     nofDots:(NSUInteger)nofDots
                             lineVertexIndices:(int16_t *)lineVertexIndices
                                nofTriangles:(NSUInteger)nofTriangles
{
    NSData *data = [NSData dataWithBytes:positions length:sizeof(float)*4*nofDots];// simd_float3は4要素あるのでx4
    
    SCNGeometrySource *vertexSource = [SCNGeometrySource
                                       geometrySourceWithData:data
                                       semantic:SCNGeometrySourceSemanticVertex
                                       vectorCount:nofDots
                                       floatComponents:YES
                                       componentsPerVector:3
                                       bytesPerComponent:sizeof(float)
                                       dataOffset:0
                                       dataStride:sizeof(float)*4];
    
    NSData *indexData = [NSData dataWithBytes:lineVertexIndices length:sizeof(int16_t)*nofTriangles*6];
    
    SCNGeometryElement *element = [SCNGeometryElement geometryElementWithData:indexData primitiveType:SCNGeometryPrimitiveTypeLine primitiveCount:nofTriangles*3 bytesPerIndex:sizeof(int16_t)];
    
    SCNGeometry *triangleLines = [SCNGeometry geometryWithSources:@[vertexSource] elements:@[element]];
    
    SCNNode *triangleLinesNode = [SCNNode nodeWithGeometry:triangleLines];
    
    return triangleLinesNode;
}
↑実行結果はこんな感じ。






iOS版のドラクエ4を買った

あれ以来、マインクラフト(iOS版)はやっていない(※1)。
それからというもの何か他にいいのないかなあ?と物色していたのだが、年末セール(※2)で安くなったこともありドラゴンクエスト4を購入した。

ドラクエは1〜3は子供の頃にファミコンでやった。30年くらい前なんだろうか。ドラクエ3で4人のパーティに付けた名前はコナン、ラナ、ジムシー、ダイスだった。
それ以降は全然やったこともなく、ストーリーも呪文も忘れてるし、1からやり直そうかな?とも思ったのだが、別シリーズっぽいのでまあいいや、と4をやってみることにした。

プレイしてまだ数日、第3章の途中というところだけど、現時点で思うことを書いてみる。



【いいところ】

iPhoneを縦向きでプレイできるのはとてもいい。ナイスな判断だ。
呪文もほとんど忘れていたが説明があるので大丈夫。
そして、なによりドラゴンクエストだ!
当時のドラクエ4をどこまで再現しているのかわからないが、なんとなく覚えているドラクエが少しだけ進化した感じだ。


【操作性が微妙】

App Storeのレビューで他の人が言うように操作性がよくない。かなりストレスだ。意図せず階段へ行き画面の切り替わりを繰り返してしまうのが特に煩わしい。そうならないように画面下部のコントローラ部分へ視線を向けるというのも煩わしい。
移動以外での操作性はさほど気にならない。


【終わり方がわからない】

操作性以外で微妙なのがゲームの終え方。最初はよくわからず混乱した。アプリのApp Storeにも何も書いてないし。
最近はこんな感じでやってる、というところを書くと、、、

ゲーム開始時の選択画面1

↑アプリ開始時にこのような画面が出てくる場合。
「冒険をする」は教会でおいのり(※3)でゲームを終えた時に保存されてるデータ。
「中断した冒険をする」はさくせん/ちゅうだん(※4)でゲームを終えた時に保存されてるデータ。
なお、「ちゅうだん」は城内や地下などできないところもある。
この二つが見えた時、前回自分がどっちでゲームを終えたのかを覚えていればそちらからすればいい。忘れている場合はそれぞれをタップして記録されてる数字(プレイ時間?)を比較して選択する必要がある。これが面倒だ。


ゲーム開始時の選択画面2

↑このように
「オートセーブから再開する」
というボタンが表示される場合もある。これは教会でもちゅうだんでもなくゲームを終えた時に表示されるらしい。
ところが、このオートセーブのタイミングがよくわからない。アプリ終了時に記録しているわけでもなさそうだ。なのでオートセーブから再開すると前回終了時付近のが保存されていないことがあって混乱するということが起きる。
オートセーブ機能はあくまで緊急避難的なものと考え、なるべく教会かちゅうだんでゲームを終えるようにしている。






※1

iOSのマイクラのバグで、地下にいてゲーム終了、再スタート時になぜか地上にいる、というのが時々ある。そのバグで見知らぬ砂漠でスタートし、ゾンビやクリーパーに襲われて死んでしまった。二度とやらねえ。 pic.twitter.com/Iaqm489yYG

— Tatsuya (@yt) 2018年12月18日


※2 前年も年末セールをしていたので安くなるまで待ってた。

※3 ↓教会のシーン。おいのりするとデータを保存してくれる(一部ネタバレのため処理してます)。

教会でおいのりする場面

※4 ↓この画面の下部にあるちゅうだんでデータを保存してくれる。

ちゅうだんする場面



Objective-Cでsmoothstep関数を使いたい

よくある関数らしい(?)smoothstep関数をObjective-Cで使いたくなったのだが、なさそうなので書いてみた。C++でもあるらしいけどそれだけのためにObjective-C++にするのが面倒なので。

参考サイトを見つつ書いてみたのがこんなの↓
-(float)smoothstepEdge0:(float)edge0 edge1:(float)edge1 x:(float) x
{
    x = [self clamp:(x - edge0) / (edge1 - edge0) minX:0.0 maxX:1.0];
    float r = x * x * (3 - 2 * x);
    
    return r;
}

-(float)clamp:(float)x minX:(float)minX maxX:(float)maxX
{
     return fmin(fmax(x, minX), maxX);
}

{
    // 実行してみる 
    float edge0 = 0.1;//—(1)
    float edge1 = 0.4;//—(1)

    float dX = 0.01;
    for(NSUInteger ite=0;ite<50;ite++) {
        float x = dX * (float)ite;
        float y = [self smoothstepEdge0:edge0 edge1:edge1 x:x];
        
        NSLog(@"%f %f”,x,y);
    }
}
clamp関数を使うみたいだが、なぜかclamp関数は以前に作っていた。
(1)この場合、0.1より小さいと0、0.4より大きいと1となり、その間はなめらかにつないでくれる。
0から0.5で入力xを変化させ、その時の出力yを計算してグラフ化(MacのNumbersを使用)した。

smoothstep関数のグラフ

↑スムーズにつながっている。
こんな関数でこんなグラフになるって、、、考えた人すごいよなあ。


参考サイト
c++ - Smoothstep function - Stack Overflow
smoothstep


Metalシェーダのfloat3とpacked_float3の違い

sizeof(simd_float3)=16という罠
↑ここと似たような話。ここで躓いてたので解決は早かったかもしれない。

そもそもはSCNVector3のデータ”列”をシェーダに渡したかった。1つだけではなく複数のSCNVector3を渡したい。
{
    SCNVector3 *offsets = calloc(… );//—(1)
    //(値を入れる処理)
    …

    NSData *data = [NSData dataWithBytes:offsets length:sizeof(SCNVector3)*NofVertices];
    [node.geometry.firstMaterial setValue:data forKey:@"offsets”];//—(2)
}
↑ (1)メモリ確保して、なんらかの値を入れる処理がある、とする。
(2)データ列をNSDataオブジェクトにしてそれをセットする。
ここまではよくある話。

これを頂点シェーダで受ける時に最初は下のようにして受けていた↓
…
 constant float3 *offsets [[buffer(3)]],
…
ところがこれだとなにやらおかしい。

Metal-Shading-Language-Specification
↑シェーダで使える関数とかここで見てる。
ここでfloat3について調べると、、、

リファレンスのスクショ1
↑サイズが16バイト?float3よ、お前もか、、、!
じゃあどうするの?と思って他のところを見ると

リファレンスのスクショ2
↑packed_float3というのがあってこれだと12バイトで受けてくれる。
…
//constant float3 *offsets [[buffer(3)]],
constant packed_float3 *offsets [[buffer(3)]],
uint vertexID [[vertex_id]] )
{
    float3 offset = offsets[vertexID];
    …
}
↑このように変更してSCNVector3を受け取ることができた。


simd_float4x4の作成方法

ARKitやSceneKit、Metalなどを使っているとAccelerateフレームワークのsimdなんとか、というタイプの値を使うことが多い。

floatで4x4のマトリックスを作成しようとしたのだが、
simd_make_float4x4
というのがない。
たいていはこのような形式なのでsimd_make_float...などと入力するとXcodeがそれっぽいのを

Xcodeのサジェスト内容

↑このようにサジェストしてくれるが、マトリックス作成用のが見つからない。

結局、simd_float4x4のプロパティにはcolumnsというのがあるとリファレンスで読んだので以下のようにした。
{
    simd_float4x4 matrix;

    simd_float4 c0 = simd_make_float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f);
    simd_float4 c1 = simd_make_float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f);
    simd_float4 c2 = simd_make_float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f);
    simd_float4 c3 = simd_make_float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f);
    
    matrix.columns[0] = c0;
    matrix.columns[1] = c1;
    matrix.columns[2] = c2;
    matrix.columns[3] = c3;
}





Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.

FC2Ad