ぬの部屋(仮)
nu-no-he-ya
  •      12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • ファイル保存ダイアログにコントロール追加(C#)

    Windows API Code Pack

    Windows API Code Packを追加して使えるCommonSaveFileDialogを使う。

    VC++の[表示]→[その他のウィンドウ]→[パッケージマネージャーコンソール]を開き、以下のコマンドを入力

    Install-Package WindowsAPICodePack-Core
    Install-Package WindowsAPICodePack-Shell

    ソースコード

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    
    using Microsoft.WindowsAPICodePack; using Microsoft.WindowsAPICodePack.Dialogs; using Microsoft.WindowsAPICodePack.Dialogs.Controls;
     
    namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_MouseClick(object sender, MouseEventArgs e) { CommonSaveFileDialog dialog = new CommonSaveFileDialog(); CommonFileDialogCheckBox check = new CommonFileDialogCheckBox("check"); dialog.Controls.Add(check); if (dialog.ShowDialog() == CommonFileDialogResult.Ok) { if (check.IsChecked == true) { MessageBox.Show("Checked"); } else { MessageBox.Show("unChecked"); } } } } }

    実行結果

    WordPress 投稿日と更新日

    投稿日

    投稿日は以下の方法で出力できる

    <time><?php echo get_the_time('Y年n月j日'); ?></time>
    

    更新日

    記事が編集された日が更新日となる。

    記事を書き、そのまま予約投稿した場合は、「予約投稿」ボタンを押した日が更新日となる。

    更新日は以下の方法で出力できる

    <time><?php echo the_modified_date('Y/m/d'); ?></time>
    

    更新日が投稿日より前になるアレ

    予約投稿ボタンを押した日=更新日なので、
    予約投稿すると、

    投稿日 8月10日
    更新日 8月8日

    のような状態になる。ここで問題になるのは更新日のほうなので、get_mtimeで「更新日の方が古ければ、投稿日を表示」という分岐を行う。

    http://www.keni-customize.net/update-date-573/

    ちなみにこのget_mtime、ユーザ定義の関数なのにget_mtimeで検索するとすぐ出てくる。もう標準化しちゃえよ・・・

    Proximity Decimation Technique in Blender のチュートリアルを試す

    Proximity Decimation Technique in Blender

    約八分、Tissueアドオンを使うので、Blender 2.79が必要。

    再分割

    Editモードで[w]→Subdivideで、 もともとあったCubeを7回再分割する。

    球の追加[0:35]

    [Shift+A]→[Ico Sphere]でICO球を追加し、Cubeの端の方へ移動する。

    Vertex Weight Proximity [0:55]

    CubeのEditモードへ行き、 Cubeのすべての頂点を選択し、全ての頂点をVertex Groupへ追加する。

    Cubeを選択し、Vertex Weight Proximityモディファイアを追加する

    Weight Paintモードに切り替える

    モディファイアを以下のように設定

    ICO Sphereを非表示にする。

    Decimate[2:35]

    Decimateモディファイアを追加し、以下のように設定。ワイヤフレーム表示は[Z]キーで切り替えられる。

    この状態で、上から各モディファイアをApplyする。

    Tissueアドオン [05:10~]

    Tissue ToolsのDual Meshをクリックする。

    Wireframeモディファイア [05:35~]

    Wireframeモディファイアを追加し以下のように設定。

    その後Subdivision Surfaceモディファイアを追加する。

    演出[07:15~]

    1.土台と背景用にそれぞれPlaneを追加し配置
    2.Cyclesで、BackgroundのColorを黒にして環境光を消す
    3.Point Lightを追加し、オブジェクトの中に配置

    timespec_getでナノ秒単位で時間計測する

    C++11以上、Visual C++15 以降(?)での時間計測。

    必要ヘッダ

    #include <time.h>
    

    構造体

    time.h内でstruct timespecが定義されている。

        struct timespec
        {
            time_t tv_sec;  // Seconds - >= 0
            long   tv_nsec; // Nanoseconds - [0, 999999999]
        };
    

    timespec_get

    1) ts の指す timespec オブジェクトにタイムベース base における現在のカレンダー時刻を格納します。
    2) timespec_get の引数 base として使用するのに適した値に展開されます。

    https://ja.cppreference.com/w/c/chrono/timespec_get

    基本的な使い方

    timespec tspec;
    timespec_get(&tspec, TIME_UTC);
    
    printf(" %lld.%ld\n", tspec.tv_sec, tspec.tv_nsec);
    

    実行例:
    1562584816.201974300

    timespec_get関数で、tv_secに秒、tv_nsecにナノ秒の値が、それぞれtime_t型とlong型で入る。

    time_t型は環境にもよるがVC++2019,x64では__int64なのでprintfではlldを使用している。

    時間の計り方

    要はtimespec_get関数である時点から数えた現在時刻の秒を整数部分と小数部分(ナノ秒を表す整数値)を分けて取得できる。

    開始時刻、終了時刻を記録して引き算をすればいいのだが、整数部分と小数部分が分かれているためそのまま引き算できない。例えば、

    開始・・・10.5秒
    終了・・・11.2秒

    だった場合、

    整数部・・・11-10
    小数部・・・2-5

    となり、 10.5秒~11.2秒までの間は 1.-3

    となってしまう。要は繰り下がりも考慮しなければならない。

    コード例

    //Sleepに必要
    #include <Windows.h>
    
    //printf等
    #include <cstdio>
    
    //std::int64_tに必要
    #include <cstdint>
    
    //time.hでも可
    #include <ctime>
    
    int main()
    {
    
      struct timespec f,t; // from ~ to
    
      timespec_get(&f, TIME_UTC);// 計測開始時刻
    
      Sleep(3000); //処理
    
      timespec_get(&t, TIME_UTC);// 計測終了時刻
    
      //////////////////////////////////
      // to - from の計算
    
    long nsec; std::int64_t tsec;
    if (f.tv_nsec < t.tv_nsec) { nsec = t.tv_nsec - f.tv_nsec; tsec = t.tv_sec - f.tv_sec; } else {//繰り下がり必要 nsec = 1000000000 + t.tv_nsec - f.tv_nsec; tsec = t.tv_sec - f.tv_sec - 1; } ///////////////////////////////// // 表示 printf("経過時間:%lld.%ld\n", tsec, nsec); getchar(); }

    参考文献

    cppreference.com
    https://ja.cppreference.com/w/c/chrono/timespec_get

    碧色工房
    https://www.mm2d.net/main/prog/c/time-03.html

    Blenderの空(雲)を作るチュートリアルHow to Make Realistic Clouds in Blenderを試す(2)

    雲用のPlaneのマテリアル設定(2)[11:30~]

    続き。

    最初に、前回最後の図の一番上のMappingをRotation Z=180に設定し、Planeを[R][X]で回転し雲が画面いっぱいになるようにしておく。(動画10:30~参照)

    1.Planeを[Shift+D]でduplicateし(Plane.001)、[G][Z]でわずかに上にずらして配置する。さらに、このコピーは一時的に非表示にする

    2.コピーしたPlaneが選択された状態で、ノードエディタ側のマテリアル名の右側にある[2]のボタンを押し、マテリアルのリンクを切り、新たにマテリアル名を”Cloud.001″等にする

    3.Plane.001のRotation Zを0に戻す。

    4.そのほかのマテリアル設定を以下のように変更

    5.Planeを表示させる

    レンダリング[13:08~]

    レンダリングに関する設定を行う。

    Transparent BSDFとEmissionのMixでしかないので、Samplesが大きい必要は無い。

    Compositing

    Compositを以下のように設定。

    ただし、Cinematicは別チュートリアルになるので、別途取り組む。

    Cinematic Look Tutorial

    別記事へ

    Easy Cinematic Look Tutorial – Using Free Software (Blender)のチュートリアルを試す+保存とかNode Groupとか

    結果

    Compositなし

    Compositあり

    関連

    Easy Cinematic Look Tutorial – Using Free Software (Blender)のチュートリアルを試す+保存とかNode Groupとか

    Blenderの空(雲)を作るチュートリアルHow to Make Realistic Clouds in Blenderを試す(1)

    Blenderの空(雲)を作るチュートリアルHow to Make Realistic Clouds in Blenderを試す(2)

    Blenderの空(雲)を作るチュートリアルHow to Make Realistic Clouds in Blenderを試す(1)

    How to Make Realistic Clouds in Blender (100% PROCEDURAL!)

    組み込みテクスチャで雲のある空を作るチュートリアルです。

    カメラの設定[0:27~]

    1.最初のCubeを[X]で削除し、レンダラをCyclesに切り替える。

    2.カメラを[Alt+G] , [Alt+R] で位置と向きを初期化する。

    3.[R][X][90]とキーを打ちカメラをY正面に向け、Y座標を-10に移動する。

    4.[Num 0]でカメラ視点になる

    5.Render→Dimensions→Borderをチェックし、カメラ内部だけがレンダリングされるようにする

    6.ノードエディタを開き、Worldの編集に入る

    ノード編集[1:30~]

    1.ノードを追加する

    • [Shift+A]→[Texture]→Gradient Texture
    • [Shift+A]→[Vector]→Mapping
    • [Shift+A]→ [Input]→Texture Coordinate
    • [Shift+A]→ [Converter]→ColorRamp

    を追加。

    なお動画ではGradient Textureを追加した時点で[Ctrl + T]でMappingやTexture Coordinateが追加されるが、これは「Node Wrangler」というアドオンの機能なので、ここでUser Preferenceから有効にしておく。

    2.各ノードを以下のように設定

    3.ColorRampの↔ボタンをクリックして上下を入れ替える

    4.以下のように設定

    5.[Shift+A]→[Color]→Hue/Saturationを追加し、ColorRampとBackgroundの間に挟む。Saturation=0.950に設定

    雲用のPlaneを追加[4:13~]

    1.3D Viewへ戻り、[Shift+A]→[Mesh]→Plane を追加 (※3Dカーソルが0,0,0二あることを確認)

    2.[S]→[1]→[0]とキーを押しサイズを10に設定
    3.[Num 5]で平行投影にして[Num 7]でXのビューに切り替え
    4.[R]→[1]→[0]とキーを押し10Unit (10° ? )回転
    5.Planeの右側、カメラと反対側がほぼZ=0になるように上に移動

    雲用のPlaneのマテリアル設定[4:43~]

    1.PlaneのMaterialをnewし、名前をCloudsにする。さらにこのDiffuseは[X]で削除する。

    2.続いて以下を設置

    • [Shift+A]→[Texture]→Noise Textureを追加
    • [Ctrl + T]でMappingとTexture Coordinateを追加
    • Texture CoordinateのObject→Mappingにノード接続
    • [Ctrl + Shift]を押しながらNoise TextureをクリックしてViewerノードを追加
    • [Shift+A]→[Converter]→[ColorRamp]を追加し、Noise TextureのColor→ColorRampのColor→ViewerのColor→Material OutputのSurfaceという接続を作る

    3.ColorRampを以下のように設定

    4.以下を追加

    • [Shift+A]→[Shader]→Emission
    • [Shift+A]→[Shader]→Transparent BSDF
    • [Shift+A]→[Shader]→Mix Shader

    Transparent BSDFとEmissionをMixし、ColorRampの出力をFacへの入力とする。出力はMaterial Outputへつなげる

    5.Noise Textureのdetailを16.0に変更

    雲用のPlaneのマテリアル設定(2)[6:35~]

    さらにリアルにしていく。

    1.Mapping と Noise Texture をDuplicate、[Shift+A]→[Color]→MixRGBを追加し、以下のように接続

    2.Noise Texture と ColorRampをDuplicateし、以下のように接続

    3.[Shift+A]→[Color]→[MixRGB]を追加し、タイプをMultiplyに設定

    以下のように接続

    4.[Shift+A]→[Texture]→Gradient Textureを追加
    5.Mapping , ColorRampをDuplicateし、以下のように設定
    6.MultiplyをDuplicateし、以下のように接続

    ここまででひとまず完成で、このままPlaneを[R][X]で回転して好みの状態を作ることができる

    残り (11:30~ )

    時間的には残りわずかなんだけれど疲れたので残りは次回。

    関連

    Easy Cinematic Look Tutorial – Using Free Software (Blender)のチュートリアルを試す+保存とかNode Groupとか

    Blenderの空(雲)を作るチュートリアルHow to Make Realistic Clouds in Blenderを試す(1)

    Blenderの空(雲)を作るチュートリアルHow to Make Realistic Clouds in Blenderを試す(2)

    Easy Cinematic Look Tutorial – Using Free Software (Blender)のチュートリアルを試す+保存とかNode Groupとか

    これはCompositのチュートリアル。この人の雲のチュートリアルをやっていたら、レンダリング結果に独自のCompositを使っていたので先にこっちをやります。

    まずCompositingに切り替える

    ノードグループの作成

    何でもいいからノードを追加し、それを選択して[Ctrl+G]でノードグループを作成できる。また、この状態で[Tab]を押すとノードグループから出る。

    以下のようにノードを繋ぐ。

    ①[Shift+A]→[Filter]→[Blur]
    ②[Shift+A]→[Color]→[Mix] 設定をSoft Lightにする
    ③[Shift+A]→[Converter]→[RGB to BW]
    ④[Shift+A]→[Filter]→[Filter] 設定をSharpenにする
    ⑤ ②を[Shift+D]して設定をOverlayにする
    ⑥[Shift+A]→[Matte]→[Box Mask]
    ⑦ ②を[Shift+D]して設定を Addにする
    ⑧[Shift+A]→[Color]→[Invert]
    ⑨ ② を[Shift+D]して設定をMultiplyにする

    ※Box Maskのwidthは動画では1になっているのだがなんか足りないような気がするので2にしている。

    Soft LightとOverlayをGroup Inputに繋げたら、Inputsの名前を以下のように変更する

    個人的にBox Maskは画像の上下を切り取ってしまう機能なのでいらないと思う。見せ方として映画っぽくするのはもちろんありだけれど色調調整の範疇ではない。

    ノードグループの保存と読み込み

    1.ノードグループ単体で保存する機能はない(知らない)ので、まずノードグループがあるシーンを.blendファイルで普通に保存する。

    2.ノードグループを使用したいファイルを開き、[Shift+F1]でAppendする。

    Blender 2.69: River 川(アニメーション)を作るチュートリアルを試す

    Blender 2.69: River [Tutorial/English/HD]

    川を作るチュートリアル約10分です。

    川の作成 開始[2:35~]

    1.[x] →Delete でCubeを削除

    2.[Shift+A]→[Mesh]→Planeで面を追加

    3.Planeに対してOceanモディファイアを追加

    4.最初のフレームと最後のフレームにOceanモディファイアのTimeパラメータのキーフレームを追加する

    5.このままだとアニメーションの最初と最後の波の速度が変わってしまうので、Graphシートを開き、[A]で全てのキーフレームを選択、[T]→[Linear]でアニメーションの速度を線形にする

    6.OceanのRepeat X=6と設定し、縦長にする

    川の変形 [4:33~]

    1.[Num 7]でTopビューに変更後、[Z]キーでワイヤフレーム表示にする

    2.[Shift+A]→[Curve]→Bezierで曲線追加
    3.Editモードに入り、[A]で全選択→[S]→[Y] → [0]でY方向にスケールを0にする

    4. BezierCurveをEditでスケーリングなどを行い、川と同じ長さにする。さらにEditモードで[W]→Subdivideを 二回 する。

    5.川の方を選択し、Curveモディファイアを追加する。ObjectにBezierCurveを設定する。

    6.カーブをY軸で編集し、川を曲げる

    7.Ocean→[Wave]→Scale と Alignmentで川の流れを調整する。動画では
    ・Scale = 0.5
    ・Alignment=2.220

    8.カメラを適当な位置に配置する。この時、もし川の向こう側が表示されないようならCameraのClippingのEndを大きくする

    マテリアルの設定 (Cycles) [8:15]

    1.Skyを追加する

    2.Node Editorを開き、川本体のマテリアルを以下のように設定

    結果

    そのままだと暗いのでSun足したけれどそれでも暗い。

    以下アニメーション版。APNG。

    Blenderのチュートリアル Massive Particle Fluid Simulationを試す

    Massive Particle Fluid Simulation, Blender Tutorial (Cycles)

    Particleアニメーション、約10分のチュートリアルです。

    1.Particleが流れ込む先の準備 [0:13~]

    1.Blenderを起動し、レンダラをCyclesに変更する。

    2.Ctrl+Sで最初のCubeを拡大する

    3.Editモードへ行き、箱の上面を削除する。その後Objectモードへ戻る

    4.[Shift+D]でこのオブジェクトを複製し、場所は変えない。そしてその一方を、Editモードへ行き、視点側の二辺を削除する。

    5.二つの重なった長方形のうち、四方の辺を削除しなかった方を選択し、Collisionを有効にした上で表示とレンダリングをOffにする。

    この設定で、「物理シミュレーションに影響するが、表示はされない」ようになる。

    6.表示したままの方のオブジェクトを選択し、[Solidify]モディファイアを設定し、Thicknessを0.02に設定してApplyする。

    ライトの設定 [1:33~]

    ライトの種類を[Sun]、Sizeを[1.0]、Use NodeにしてStrengthを[7.0]に設定する。そしてライトの位置を移動し、ボックスを横から照らすようにする。

    シャワーヘッド(Particleが出てる用に見せる筒)[1:55~]

    1.[Shift+A]→[Mesh]→[Cylinder]で円筒を追加し、平たい円筒にして少し上の方に配置する。

    2.Editモードへ移行し、円筒の上下の面を選択する。
    3.この状態で、Mean Creaseを1.0に変更する。この設定はSubsurfをしたときにこのラインをなめらかにしないようにするためのもの。

    4.Editモードで、円筒の下の面を削除、上の面を[E]→[S]複製、縮小、再び[E][Z]でZ方向に押し出しを行う。

    あと必要であればObjectモードに戻って[G]で位置調整を行う

    5.Subdivision SurfaceモディファイアとSolidifyモディファイアを追加する。

    Particleの流出元[3:33]

    1.シャワーヘッドを上記の作り方で作っていれば、オブジェクト中心が流出口付近の中央にあるはずなので、そのまま[Shift+S]→[Cursor to Selected]で3Dカーソルの位置をそこへ移動する。

    2.[Shift+A]→[Mesh]→[Circle]で円を追加し、[S]で拡大、[F]で面を張る。面を張らないとParticleが出ない

    Particleの設定[3:55~]

    作成した円を選択し、Particle Systemを追加する。

    ここで一度[Alt+A]を押し、アニメーションを実行してParticleの流出を確認する。

    マテリアルの設定[5:20~]

    各オブジェクトのマテリアルを設定する

    1.流出先の床・壁

    2.背景

    3.シャワーヘッド

    Particleの設定(2)[6:00~]

    1.先ほど[Alt+A]したので生成されているはずのParticleをクリックし、Particleの設定から[Cache]→[Bake]を実行する

    2.[Shift+A]→[Mesh]→[UV Sphere]を追加し、[M]で別のレイヤーに移動する。

    3.Particle Systemの[Render]→[Object]に作成したSphereを設定する

    4.追加したSphereのマテリアルを設定する

    カメラの設定[7:48~]

    [Num 0]でカメラ視点に移り、[Shift+F]でフライモードに入る。ASDW+マウス移動でカメラ位置を決定する。

    レンダリングの設定

    レンダリングの設定は主に
    ・OutputのCompressionを0にする
    ・SamplingのSamplesを400にする
    ・(GPUがあるなら)[Render]→[Device]でGPU Computeを選択し
    ・(GPUがあるなら) PerformanceのTilesを500に設定

    結果

    以下、APNGなのでブラウザを選びます。 GPUレンダリング 、GeForce GTX 960という弱いグラボでも、このサイズで25分以内に終わったので意外と速いです。

    ICO球の作り方(2)

    前回ICO球の作り方をやったので、今回はそれをC++で実装する。

    https://suzulang.com/cpp-code-ico-q-1/

    ソースコード

    #pragma warning(disable:4996)
    
    
    #include <GL/glut.h>
    
    #include <fstream>
    #include <sstream>
    #include <vector>
    #include <algorithm>
    
    #include <unordered_map>
    #include <array>
    
    
    //! @brief 三次元の座標値 struct xyz { double x, y, z; xyz() {} xyz(const double X, const double Y, const double Z) : x(X), y(Y), z(Z) {} }; //! @brief 三つの頂点IDで表す三角形 struct tri { size_t a, b, c; tri() {} tri(const size_t A, const size_t B, const size_t C) : a(A), b(B), c(C) {} }; //////////////////////////////////////////////////////////////// //! @brief ハッシュ値を統合する ( 汎用的に使用できる関数 ) //! @param [in,out] seed in:既存のハッシュ値 out:元のseedとvから作成したハッシュ値を統合した値 //! @param [in] v 新たにハッシュ値を作成する値 template<typename T> void hash_combine(size_t& seed, T const& v) { //基本型に関するハッシュ生成は標準ライブラリが提供している std::hash<T> primitive_type_hash; //生成したハッシュを合成する。このコードはboostものを使用する seed ^= primitive_type_hash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } namespace ICOQ { //! @brief 二つの頂点IDで表すエッジ struct edge { size_t a, b; edge(const size_t A, const size_t B) :a(A), b(B) {} //! @brief 比較演算子。順序が逆でも同じと見なす bool operator==(const edge& e)const { return (e.a == a && e.b == b) || (e.a == b && e.b == a); } }; //! @brief エッジ用のハッシュ関数 struct EHash { public: size_t operator()(const edge& data)const { //クラスのメンバの値それぞれについてハッシュ生成して、それらを結合して一つのハッシュ値にする std::size_t seed = 0; if (data.a < data.b) { hash_combine(seed, data.a); hash_combine(seed, data.b); } else { hash_combine(seed, data.b); hash_combine(seed, data.a); } return seed; } }; //! @brief ICO球作成 class icoQ { public: //結果を格納する配列 std::vector<xyz> vlist; std::vector<tri> tlist; private: double R; //! @brief 頂点を正規化して原点からの距離Rにする //! @param [in,out] vtx 頂点座標 //! @param [in] R 半径 static void normalize(xyz& vtx,double R) { double x = vtx.x; double y = vtx.y; double z = vtx.z; double len = sqrt(x * x + y * y + z * z); vtx.x /= len; vtx.y /= len; vtx.z /= len; vtx.x *= R; vtx.y *= R; vtx.z *= R; } public: icoQ() { R = 1; } //! @brief 指定したエッジの中点(球上)の登録 void calc_middle(std::unordered_map<edge, size_t, EHash> * ehash, std::vector<xyz> * vtx, const size_t j, const size_t k) { double p[3] = { (*vtx)[j].x,(*vtx)[j].y,(*vtx)[j].z }; double q[3] = { (*vtx)[k].x,(*vtx)[k].y,(*vtx)[k].z }; xyz newV( (p[0] + q[0]) / 2, (p[1] + q[1]) / 2, (p[2] + q[2]) / 2 ); normalize(newV, R); vtx->push_back(newV); (*ehash)[edge(j, k)] = vtx->size() - 1; } //! @brief エッジの中間点を計算し、頂点をアップデートする //! @param [in,out] ehash エッジからエッジの中点のIDを取り出すためのハッシュ //! @param [in,out] vtx 頂点一覧。増えることはあっても順序は変わらない //! @param [in] tri_now 現在の三角形一覧 void update_middle_hash(std::unordered_map<edge, size_t, EHash> * ehash, std::vector<xyz> * vtx, const std::vector<tri> & tri_now) { ehash->clear(); // エッジの中間点となる頂点を作成し、ハッシュに登録する for (auto& t : tri_now) { std::unordered_map<edge, size_t, EHash>::iterator ei; // t.a -> t.b の中間点 ei = ehash->find(edge(t.a, t.b)); if (ei == std::end(*ehash)) { calc_middle(ehash, vtx, t.a, t.b); } // t.b -> t.c の中間点 ei = ehash->find(edge(t.b, t.c)); if (ei == std::end(*ehash)) { calc_middle(ehash, vtx, t.b, t.c); } // t.c -> t.a の中間点 ei = ehash->find(edge(t.c, t.a)); if (ei == std::end(*ehash)) { calc_middle(ehash, vtx, t.c, t.a); } } } //! @brief 初期状態の正二十面体を作成する void setSeeds( std::vector<xyz>& svtx, std::vector<tri>& stri){ svtx.resize(12); // create 12 vertices of a icosahedron // 正二十面体を構成する頂点を全て列挙する const double t = (1.0 + sqrt(5.0)) / 2.0; svtx[0] = xyz(-1, t, 0); svtx[1] = xyz(1, t, 0); svtx[2] = xyz(-1, -t, 0); svtx[3] = xyz(1, -t, 0); svtx[4] = xyz(0, -1, t); svtx[5] = xyz(0, 1, t); svtx[6] = xyz(0, -1, -t); svtx[7] = xyz(0, 1, -t); svtx[8] = xyz(t, 0, -1); svtx[9] = xyz(t, 0, 1); svtx[10] = xyz(-t, 0, -1); svtx[11] = xyz(-t, 0, 1); //すべての頂点座標を球の上に置く for (size_t i = 0; i < svtx.size(); i++) { normalize(svtx[i], R); } stri.resize(20); // create 20 triangles of the icosahedron // 正二十面体を構成する三角形を全て列挙する // 5 faces around point 0 stri[0] = tri(0, 11, 5); stri[1] = tri(0, 5, 1); stri[2] = tri(0, 1, 7); stri[3] = tri(0, 7, 10); stri[4] = tri(0, 10, 11); // 5 adjacent faces stri[5] = tri(1, 5, 9); stri[6] = tri(5, 11, 4); stri[7] = tri(11, 10, 2); stri[8] = tri(10, 7, 6); stri[9] = tri(7, 1, 8); // 5 faces around point 3 stri[10] = tri(3, 9, 4); stri[11] = tri(3, 4, 2); stri[12] = tri(3, 2, 6); stri[13] = tri(3, 6, 8); stri[14] = tri(3, 8, 9); // 5 adjacent faces stri[15] = tri(4, 9, 5); stri[16] = tri(2, 4, 11); stri[17] = tri(6, 2, 10); stri[18] = tri(8, 6, 7); stri[19] = tri(9, 8, 1); }
     
        //! @brief 球の作成
    
    void refine(const double r,const int level) { //半径の決定 R = r; // エッジの中間点を表すハッシュ std::unordered_map<edge, size_t, EHash> ehash; //初期状態の頂点・三角形群 std::vector<tri> tri_now; std::vector<xyz> vtx; setSeeds(vtx, tri_now); //初期形状の頂点情報の配列 //ICO球となる三角形 std::vector<tri> retQ; //「全ての三角形を4分割する」という作業をlevel回繰り返す for (size_t i = 0; i < level; i++) { //エッジの中点を求め、頂点一覧に追加し、そのハッシュを作成する update_middle_hash(&ehash, &vtx, tri_now); //今までの結果をクリア retQ.clear(); //全ての三角形を分割 for (auto& t : tri_now) { size_t a = t.a; size_t b = t.b; size_t c = t.c; size_t d = ehash[edge(a, b)]; size_t e = ehash[edge(b, c)]; size_t f = ehash[edge(c, a)]; //△t、つまり△abcを分割し新三角形を4つ作る /* a /\ / \ / \ d / \ f / \ / \ / \ / \ b ~~~~~~~e~~~~~~~ c */ retQ.push_back(tri(a, d, f)); //△adf retQ.push_back(tri(d, b, e)); //△dbe retQ.push_back(tri(d, e, f)); //△def retQ.push_back(tri(f, e, c)); //△fec } tri_now = retQ; } vlist = vtx; tlist = retQ; } }; }
     
    void display(void); void timer(int value) { glutPostRedisplay(); glutTimerFunc(20, timer, 0); } int width, height; void resize(int w, int h) { width = w; height = h; } ICOQ::icoQ icoq; int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow(argv[0]); glutDisplayFunc(display); glutReshapeFunc(resize); glutTimerFunc(20, timer, 0); glutReshapeFunc(resize); icoq.refine(1,4); glutMainLoop(); return 0; } void display(void) { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, width/(double)height, 0.1, 20); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslated(0, 0, -4); static int kk = 0; kk++; glRotated(kk, 1, 1.5, 1); glDisable(GL_LIGHTING); glColor3d(1, 1, 1); glLineWidth(2);
    //ICO球エッジ表示
     for (size_t i = 0; i < icoq.tlist.size(); i++) { xyz a = icoq.vlist[icoq.tlist[i].a]; xyz b = icoq.vlist[icoq.tlist[i].b]; xyz c = icoq.vlist[icoq.tlist[i].c]; glBegin(GL_LINE_LOOP); glVertex3d(a.x, a.y, a.z); glVertex3d(b.x, b.y, b.z); glVertex3d(c.x, c.y, c.z); glEnd(); } glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); GLfloat abl[4] = { 1,1,1,1 }; GLfloat zero[4] = { 0,0,0,0 }; glLightfv(GL_LIGHT0, GL_AMBIENT, abl); glLightfv(GL_LIGHT0, GL_DIFFUSE, zero); glLightfv(GL_LIGHT0, GL_SPECULAR, zero); glLightfv(GL_LIGHT0, GL_POSITION, zero); GLfloat ambr[4] = { 1,0,0,1 }; GLfloat ambb[4] = { 0,0,1,1 }; glMaterialfv(GL_FRONT, GL_AMBIENT, ambb); glMaterialfv(GL_BACK, GL_AMBIENT, ambr);
    //ICO球表示 for (size_t i = 0; i < icoq.tlist.size(); i++) { xyz a = icoq.vlist[icoq.tlist[i].a]; xyz b = icoq.vlist[icoq.tlist[i].b]; xyz c = icoq.vlist[icoq.tlist[i].c]; glBegin(GL_TRIANGLES); glVertex3d(a.x, a.y, a.z); glVertex3d(b.x, b.y, b.z); glVertex3d(c.x, c.y, c.z); glEnd(); }
     
    glFlush(); }

    参考文献

    Creating an icosphere mesh in code
    http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html

    Weblog on mebius.tokaichiba.jp
    http://ynomura.dip.jp/archives/2009/08/20.html