2015/04/19 岩手県盛岡市 石割桜。
見る機会は10回以上あったきがするが、ちょうどいい満開に遭遇したのはこれが初めてかもしれない。
花自体はとても見事に咲いているのだが、支えの棒がこうもたくさんあっては、老体を無理矢理酷使させられているように見えて哀れに思えてくる。いい加減休ませてやったらどうか。
ngPlantで木を作る作業の続きで、今日は枝に葉っぱをつける。
葉はテクスチャでつけるので、まずは葉の画像を用意する。gimpなどで葉っぱっぽい何かを描いて、.tga .jpg .pngのいずれかの形式で保存する。別にペイントでもいいが、背景を透明にできないのでせめてアルファを使えるソフトがいい。
早速やってみよう。
・・・できただろうか。
まずngPlantを起動し、葉っぱが生える枝を用意する。
今回は説明のために主幹をそのまま使用。
① 葉っぱをつけたいBranchを右クリック→[append branch]→[New branch]で枝を追加する。

② 追加されたBranchを右クリック→[Stem model]→[Quad]を選択。

③ 見づらいので、まず[Stem]のLengthを大きくする。
④ このままWidthを大きくすると重なってしまうので、[Branching]へ行きLimit maxをOnし、Max. numberを3程度にする。これで枝から生えている葉っぱは三枚となる(はず)。
⑤ [Stem]に戻りWidthを大きくする。少し細長い四角形にする。
⑥ [Material]タブへ移動
解説:
① Sampleはメンバ変数A,Bを持つ
② SampleのインスタンスはA=1,B=2で初期化される
③ showA,showBはA,Bを画面に表示する
④ A,Bはprivateで、setもfriend関数も用意していない。従ってA,Bを書き換える方法は存在しない
class Sample{ int A; int B; public: Sample(){ A=1; B=2; } void showA(){ std::cout << "A = " << A << std::endl; } void showB(){ std::cout << "B = " << B << std::endl; } }; int _tmain(int argc, _TCHAR* argv[]) { Sample instance; instance.showA(); instance.showB(); int k; std::cin >> k; return 0; }
はずなんだが、Sampleに一切手を加えないでA,Bを編集することができる。
int _tmain(int argc, _TCHAR* argv[]) { Sample instance;
instance.showA(); instance.showB(); int k; std::cin >> k; return 0; }
意図してこれをやる人間はそうそういないと思うが、間違ってこうなっしまうケースは結構頻繁にある。そして謎の理屈でポインタがだめなやつだという烙印を押されるのである。
というわけでngPlantの続き。主幹に枝を追加した後、枝振りを作っていく。
で、ngPlantは、木をポリゴンで作成するツールだ。
初期状態の画面はこうなっている。左側がビューで、円筒がこれから作成する木のべーすとなる。
この画面での右側の上の方は使わない。
重要なのは、右下の、図の赤枠で囲った部分。ここから、どの幹や枝を編集対象にするかを選択する。
まず、最初は[Plant]が選択されているので、[Branch-1]をクリックし選択する。選択された枝は太文字になる。
枝が選択されると、右上の画面がその枝の編集画面に変わる。ここからその枝の調整をしていく。
ちなみに以下、サムネイル画像を貼り付けたところ本来の画像から上下が切れてしまった。クリックするとフルで見られる。意外と上下を見ないと理解できない画像があったりする。遠い未来に直すかもしれない。
次に、枝を追加してみる。
[Branch-1]を右クリックし、[Append branch]→[New branch]を選択。
くしのように新しい枝が生える。右下に表示されている新しい枝[Branch-1-1]が選択(強調表示)されていることを確認して、右上をもう一度見てみる。
大体は同じで、違いは、主幹一本の時と違い、枝がたくさんあるときは各パラメータが影響を与えるのはその階層の枝全てだと言うこと。例えば、Axis Variationの数字をあげると、横に伸びている枝全てが複雑に折れ曲がる。
Axis Variationは0にしておくとして、この状態で、先ほど飛ばしたVariationとOffset influenceについてみてみる。
| ② | Variation 枝が複数あるとき、長さにばらつきを与える |
![]() |
| ③ | Offset influence 枝が複数あるとき、長さを 曲線的に変化させる |
|
俺がstd::removeを理解できないのはどう考えても仕様が悪い。
というわけで、std::removeの挙動の再確認。
std::removeは、第一引数から第二引数までのコンテナ内の、第三引数で指定された値を削除する(嘘)、STLの関数だ。
今回使うのは以下。removeのためにalgorithm。iteratorはstd::beginを気分で使う。
#include <vector> #include <iostream> #include <algorithm> #include <iterator>
まず配列を用意して初期化する。
std::vector<int> vec = { 1, 2, 3, 2 , 4, 2 , 5 }; for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
結果は、
1
2
3
2
4
2
5
次に、この配列の値2に対してstd::removeをかける
auto end = std::remove(std::begin(vec), std::end(vec), 2); for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
結果は、
1
3
4
5
4
2
5
さあ、もう訳がわからない。消えたはずの2は残っていてその割に個数は減っていてその上位置まで変わっていて、さらにいじったつもりのない4と5が増えている。
Erase-Removeエディオムというのがあり、eraseと併用すれば消える。
vec.erase( std::remove( std::begin(vec), std::end(vec), 2 ), std::end(vec) ); for (auto i = std::begin(vec); i != std::end(vec); i++){ std::cout << *i << std::endl; }
これはつまりこういうことだ。
std::removeは、指定した削除対象の値以外のものを配列の左側に再配置する。
そして戻り値として、必要なデータが入っている領域の次の値へのイテレータを返す。
std::removeが行うのはコンテナの中の値の書き換えだけなので、高速だが、そのままではいわば「破棄推奨領域」が生じてしまう。
(※) end2 = std::remove(begin,end,value);というようにして、以後の処理を begin~end2の範囲に対して行うようにすれば、消えたものとして処理できる。
この破棄推奨領域を削除する行為は、確保されているメモリ領域の変更なので、std::eraseを用いる必要がある。
今年の冬はかなり暖かかったようで、部屋の中が氷点下になることはなかったように思う。
その甲斐あってか、まだ3月だというのに部屋に入れておいた桜がほんのりと咲き始めた。買って三年ぐらいだろうか。まだ選定をしたことがない。今年あたりやってみようと思う。
・「おれ」の一人称。
・~た。
・超能力系のSF
・ページ数:250
・筒井康隆
一度文明が滅んだ世界で、主人公ラゴスの旅の最中に起こった様々な事件が、一話完結で12ほど納められている。
一話完結とはいえ前後のつながりはちゃんとあるので順番に読むべき。
きわめて哲学要素が強い作品。超能力もたいしたものはなく、人や他の生物と共感するとか、壁抜け(ただしすごく時間がかかる)とか、そんな類で、あくまでも世界観としての立ち位置に忠実。戦闘ものではないため戦いのシーンはほぼ出てこない。そういう意味ではアクション性はない。ただし馬で盗賊から逃げたりはする。
笑い所はないが作品としてはとても「きれい」な類で、落ち着いて読める。
-------------------------------------------------------------------------
遅い夕食は夫人と令嬢の吟味によるまさに貴族の晩餐と呼ぶにふさわしいもので、それはただのホテルの宿泊客に出す料理のようではなく、その上その食事はたったひとりの客であるおれと、主人のドリド氏との会食であり、夫人と令嬢の給仕によるものであった。夫人と令嬢はこの上なく高貴であり、令嬢の愛らしさは例えようがなかった。おれは夢見心地で食べ続けた。
-------------------------------------------------------------------------
「ラゴスさん、あんた石火箭が扱えるかね」
おれはそのことばで一も二もなくとび起きた。「襲撃だな。盗賊団か」
身支度するおれにシャクロは揉み手を続けながら説明した。「奴隷狩りだよ。この町の南西二百キロのところに銀山があって採掘業者が銀鉱を開いているのだが、鉱夫を傭わずに奴隷を使っている。その奴隷の数が少なくなると近くの村へ出かけて奴隷狩りをやるのだが、まさかこんな大きな町を襲うとは思わなかった。奴隷の数がよほど不足しているんだろう」
「連中、どれぐらいいるんだ」
「朝がた、見張りの傭兵が城壁の上から見たところでは、約八十人が町を取り囲んでいるそうだ」
「こっちの傭兵の数は」
「二十人とちょっと、それに警備隊員が予備役も入れて二十五人前後」
「そいつはえらいことだな」
------------------------------------------------------------
/////////////////////////////////////////////////////////////// //頭に0がついて8進数表記になったときに呼び出される //第一テンプレート引数:二進数に直したい数(8進数表記で渡された) //第二テンプレート引数:何桁シフトするか(演算に使用・呼び出し時は0) //第三テンプレート引数:何桁の二進数が入力されたか template <int N,int shift,int digit> struct ToDec8toBin{ enum { Val = ( ToDec8toBin<N/8,shift+1,digit>::Val ) + ( N % 2 << shift ), }; }; template <int N,int digit> struct ToDec8toBin<N,digit,digit>{ enum { Val = 0, }; }; ///////////////////////////////////////////////////////////// //頭に0がつかず10進数表記になったときに呼び出される template <int N, int shift, int digit> struct ToDec10toBin{ enum { Val = (ToDec10toBin<N / 10, shift + 1, digit>::Val) + (N % 2 << shift), }; }; template <int N, int digit> struct ToDec10toBin<N, digit, digit>{ enum { Val = 0, }; }; ///////////////////////////////////////////////////////////// //C++で二進数表記をするための関数・エントリポイント template <bool Cond,int N,int digit> struct ToDecCore; //頭に0がついて8進数になっているときはこちらを呼び出す template <int N, int digit> struct ToDecCore<true, N,digit>{ enum { Val = ToDec8toBin<N,0,digit>::Val,}; }; //頭に0がついていないときは10進数からの変換を行う template <int N, int digit> struct ToDecCore<false, N,digit>{ enum { Val = ToDec10toBin<N, 0, digit>::Val,}; }; //真のエントリポイント(4bit) template <int H> struct ToDecimal4{ enum { Val = ToDecCore < H <= 0111, H, 4>::Val, }; }; //真のエントリポイント(8bit) template <int HH,int HL> struct ToDecimal8{ enum { Val = (ToDecCore < HH <= 0111, HH, 4>::Val << 4 )+ ToDecCore < HL <= 0111, HL, 4>::Val }; }; //真のエントリポイント(16bit) //4ビットずつ区切って渡す template <int B4, int B3,int B2, int B1> struct ToDecimal16{ enum { Val = (ToDecCore < B4 <= 0111, B4, 4>::Val << 12) + (ToDecCore < B3 <= 0111, B3, 4>::Val << 8) + (ToDecCore < B2 <= 0111, B2, 4>::Val << 4) + ToDecCore < B1 <= 0111, B1, 4>::Val }; }; //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// int _tmain(int argc, _TCHAR* argv[]) { printf("1101 0010 0101 0011:%d\n", ToDecimal16<1101, 0010, 0101, 0011>::Val); getchar(); return 0; }
探せばいくらでも出てきそうだが勉強のため自作。
やってることはテンプレートを再帰してるだけ。
ただし、C/C++は数値の頭がゼロだと8進数表記にするというヨクワカラナイ言語仕様を持っているため、1000以降と0111以前で処理を分岐している。「C++テンプレートテクニック」に書いてあったものを好みに改良した。
多分、4桁ずつじゃなく8桁ずつ書いた方が使いやすいという人の方が多いのだろうが、個人的には4桁くらいで区切らないと読みにくい。
Boostとか、Cのマクロとか、確か同じような処理をする機能が提供されていたと思う。
ただ、Boostは一つのことをやりたいだけに導入するにはあまりにでかすぎる。
ちゃんと名前空間で囲って、エントリポイントをさらにconstexprした関数でくくれば実用的になると思う。
VC++にはconstexprないけど。というかconstexprtまで対応してるなら0b対応してそうだからこんなもの意味がなくなるわけだけど。
草を生やす続き。
好きな場所に草を生やすことを考える。
注意点として、一枚ポリゴンだと以下のペイント機能が使えない。
そこでSubdivideして面数を増やす
1. Edit Modeへ移行
2. 3D View上にマウスがある状態でキーボードの[W]をおす
3. 出てきたメニューから[Subdivide]を選択。
4. Subdivideを何回か繰り返して升を十分細かくする
Weight Paintモードへ移行
ブラシで色をつけていく。
青 = 生えない
赤 = 最も多く生える
前回までと同じようにParticleの設定をする。
最後に、ParticleのVertex GroupsのDensityでGroupを選択
レンダリング