トランスフォーマーロゴアニメーション

#はじめに
あけましておめでとうございます。フジヤマタカシです。新年！ということでトランスフォーマー風のニューイヤーロゴを作りましたので、それについて軽く説明させて頂こうと思います。よろしくお願いします。

![n0c45-jrqfr.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/4981a57a-3890-7aae-7d4e-d19f135937a7.gif)


#ピースのセッティング
![Screenshot from 2019-01-02 23-48-27.png](https://qiita-image-store.s3.amazonaws.com/0/321297/ac6e6866-adcb-d855-fb27-ccd212f3ec1a.png)
まずはdivideかなにかで分割します。

PrimWrangleで`i@baseid = @primnum;`としてbaseidを作ります。

![Screenshot from 2019-01-03 00-22-02.png](https://qiita-image-store.s3.amazonaws.com/0/321297/ea5219b2-9c34-c8d6-91ac-9c9e6210dff9.png)
コピーします。
コピーした後、PrimWrangleを使い下記のコードのpolyneighboursで隣接ポリゴンのプリミティブナンバーのリストを取得します。

```
i@primid = @primnum;
s@name = concat("piece",itoa(@primid));

i[]@neighbours = polyneighbours(0, @primnum); 
```

![Screenshot from 2019-01-03 00-22-55.png](https://qiita-image-store.s3.amazonaws.com/0/321297/f10a71d3-0a77-9a7b-cfac-8a0cd9ca43b7.png)

polyExtrudeで厚みをつけます。

Packした後にプリムアトリビュートのbaseidとneighboursをポイントにプロモートし,下記のコードでコピー先の軸にある隣接オブジェクトのナンバーを取得しneighboursに追加します・

```
string group = concat( "base_",itoa(i@baseid));
int moreNeighbours[] = pcfind(0, group, "P", @P, ch("radius"), 3);
moreNeighbours = moreNeighbours[1::];

append( i[]@neighbours, moreNeighbours );
```

ここまでの操作は隣接PackObjectのナンバーリストさえ取得できれば何でもいいです。

#階層セッティング
階層化は以前に書いた[拡散していくボックス](https://qiita.com/takashiFujiyama/items/179bf28eddef91e55269)の方法に似ています。
Solver内で隣接オブジェクトをアクティブにしていきながら、処理させていった順にそのptnumのリストを追加していきます。この処理によって自身の親階層のリストを取得することになります。
![Screenshot from 2019-01-03 00-57-54.png](https://qiita-image-store.s3.amazonaws.com/0/321297/264d1f6f-b779-960e-9b25-9d800e18c25b.png)

Solver前に準備するアトリビュート。hierarchyに階層リストを格納します。

```
i@rootid = @ptnum;

if((rand(@ptnum + 123123)<0.6)){
    i@top = 1;
    i@connected = 1;
    
    i@level = 0;
    i[]@hierarchy = array(@ptnum);
    }
```

Solver内のコード、事前に準備していた隣接オブジェクトのナンバーを辿っていきます。

```
i@top = 0;

i[]@neighbours;
int unconnecteds[] = {};
foreach(int i; i[]@neighbours){
    if(point(0,"connected",i) == 0){
        append(unconnecteds,i);
    }
}

int active = 0;
if( len(unconnecteds) != 0 ){
    if(i@top == 1){
        active = 1;
    }else{
        if(rand(@ptnum+999 + @Frame) <  0.1 )active = 1;
    }
}

///select Next
if( active ){
    int selNex = floor( rand(@ptnum * 12 + @Frame) * len(unconnecteds) );
    int nextP = unconnecteds[selNex];
    
    setpointattrib(0, "top", nextP, 1);
    setpointattrib(0, "connected", nextP, 1);
    
    setpointattrib(0, "rootid", nextP, i@rootid);
    setpointattrib(0, "level", nextP, i@level + 1);
    int hierarchyPlus[] = i[]@hierarchy;
    append(hierarchyPlus, nextP);
    setpointattrib(0, "hierarchy", nextP, hierarchyPlus );
}
```

#マトリックス階層のアニメーション
階層トランスフォームは子の階層から親へ向かってかけていく必要があります。トランスフォームのマトリックスにはmaketransformを使います。簡単にPivotが設定できるからです。

```
matrix xform = ident();
for(int i=0; i<len(@hierarchy); i++ ){
    int index = @hierarchy[::-1][i];
    vector move;
    move[floor( rand(index + 11)*3 )] = fit01(rand(index + 110),-1,1) * 0.05; 
    vector rotation; 
    rotation[floor( rand(index + 22)*3 )] = rint( fit01(rand(index + 220),-1,1) * 3 ) * 90;
    vector pivot = point(0,"P",index);
    xform *= maketransform(0, 0, move, rotation, set(1,1,1), pivot);
}

@P *= xform;
setattrib(0, "primintrinsic", "transform", @primnum, -1, matrix3(xform), "set");
```
これが基本コード。`int index = @hierarchy[::-1][i];`で階層のナンバーを逆に辿ります。
indexを使ってランダムシードを使い。階層中心点はpoint(0,"P",index)で取得できます。

rottimingListとmovetimingListを精製して各階層のアニメーションタイミングを設定して並べただけのボックスを動かしてみます。

```
i[]@hierarchy;

float rottimingList[] = {};
float movetimingList[] = {};
float startFrame = 1;
for(int i=0; i<len(@hierarchy); i++ ){
    append(rottimingList, startFrame);
    startFrame += 2 + rand(@hierarchy[i] + 666) * 1;
    
    append(movetimingList, startFrame);
    startFrame += 4 + rand(@hierarchy[i] + 999) * 1;
}
matrix xform = ident();
for(int i=0; i<len(@hierarchy); i++ ){
    int index = @hierarchy[::-1][i];
    float rottiming = rottimingList[::-1][i];
    float movetiming = movetimingList[::-1][i];
    vector move;
    move [floor( rand(index + 11)*3 )] = rand(index + 111) * 1 * fit(@Frame,movetiming,movetiming + 5,1,0);
    vector rotation; 
    rotation[floor( rand(index + 22)*3 )] = floor( rand(index + 222) * 3 ) * 90 * fit(@Frame,rottiming,rottiming + 5,1,0);
    vector pivot = point(0,"P",index);
    xform *= maketransform(0, 0, move, rotation, set(1,1,1), pivot);
}

@P *= xform;
setattrib(0, "primintrinsic", "transform", @primnum, -1, matrix3(xform), "set");
```

![ftqnj-9d4z1.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/b175c88a-b98f-4d00-2c1e-eddb3fbb3fb5.gif)
こんな感じです。このコードはほぼそのまま分画した立体ロゴに適応出来ます。多少調整したコードが下記のです。

```
i[]@hierarchy;

float rottimingList[] = {};
float movetimingList[] = {};
float startFrame = rand(@rootid + 333) * 20 + 1;
for(int i=0; i<len(@hierarchy); i++ ){
    append(rottimingList, startFrame);
    startFrame += 5 + rand(@hierarchy[i] + 666) * 5;
    
    append(movetimingList, startFrame);
    startFrame += 5 + rand(@hierarchy[i] + 999) * 15;
}

matrix xform = ident();
for(int i=0; i<len(@hierarchy); i++ ){
    int index = @hierarchy[::-1][i];
    float rottiming = rottimingList[::-1][i];
    float movetiming = movetimingList[::-1][i];
    vector move;
    move[floor( rand(index + 11)*3 )] = fit01(rand(index + 110),-1,1) * 0.05 * fit(@Frame,movetiming,movetiming + 5,1,0); 
    vector rotation; 
    rotation[floor( rand(index + 22)*3 )] = rint( fit01(rand(index + 220),-1,1) * 3 ) * 90 * fit(@Frame,rottiming,rottiming + 15,1,0);
    vector pivot = point(0,"P",index);
    xform *= maketransform(0, 0, move, rotation, set(1,1,1), pivot);
}

@P *= xform;
setattrib(0, "primintrinsic", "transform", @primnum, -1, matrix3(xform), "set");
```
![n0c45-jrqfr.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/4981a57a-3890-7aae-7d4e-d19f135937a7.gif)


作成したファイルはこちらになります。
https://drive.google.com/file/d/1If2sABdUx1ogkYMkiEh6PjcHMrDnBG-h/view?usp=sharing


フォントはこちらからダウンロードしましたが、サンプルファイルのテキストはフリーズさせてあります。
https://www.fontspace.com/category/transformers

では今回は以上になります。ここまで読んでいただきありがとうございます！