Windows API Code Packを追加して使えるCommonSaveFileDialogを使う。
VC++の[表示]→[その他のウィンドウ]→[パッケージマネージャーコンソール]を開き、以下のコマンドを入力
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"); } } } } }
投稿日は以下の方法で出力できる
<time><?php echo get_the_time('Y年n月j日'); ?></time>
記事が編集された日が更新日となる。
記事を書き、そのまま予約投稿した場合は、「予約投稿」ボタンを押した日が更新日となる。
更新日は以下の方法で出力できる
<time><?php echo the_modified_date('Y/m/d'); ?></time>
予約投稿ボタンを押した日=更新日なので、
予約投稿すると、
のような状態になる。ここで問題になるのは更新日のほうなので、get_mtimeで「更新日の方が古ければ、投稿日を表示」という分岐を行う。
http://www.keni-customize.net/update-date-573/
ちなみにこのget_mtime、ユーザ定義の関数なのにget_mtimeで検索するとすぐ出てくる。もう標準化しちゃえよ・・・
約八分、Tissueアドオンを使うので、Blender 2.79が必要。
Editモードで[w]→Subdivideで、 もともとあったCubeを7回再分割する。
[Shift+A]→[Ico Sphere]でICO球を追加し、Cubeの端の方へ移動する。
CubeのEditモードへ行き、 Cubeのすべての頂点を選択し、全ての頂点をVertex Groupへ追加する。
Cubeを選択し、Vertex Weight Proximityモディファイアを追加する
Weight Paintモードに切り替える
モディファイアを以下のように設定
ICO Sphereを非表示にする。
Decimateモディファイアを追加し、以下のように設定。ワイヤフレーム表示は[Z]キーで切り替えられる。
この状態で、上から各モディファイアをApplyする。
Tissue ToolsのDual Meshをクリックする。
Wireframeモディファイアを追加し以下のように設定。
その後Subdivision Surfaceモディファイアを追加する。
1.土台と背景用にそれぞれPlaneを追加し配置
2.Cyclesで、BackgroundのColorを黒にして環境光を消す
3.Point Lightを追加し、オブジェクトの中に配置
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] };
1)
https://ja.cppreference.com/w/c/chrono/timespec_getts
の指す timespec オブジェクトにタイムベースbase
における現在のカレンダー時刻を格納します。
2)timespec_get
の引数base
として使用するのに適した値に展開されます。
timespec tspec; timespec_get(&tspec, TIME_UTC); printf(" %lld.%ld\n", tspec.tv_sec, tspec.tv_nsec);
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
続き。
最初に、前回最後の図の一番上の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を表示させる
レンダリングに関する設定を行う。
Transparent BSDFとEmissionのMixでしかないので、Samplesが大きい必要は無い。
Compositを以下のように設定。
ただし、Cinematicは別チュートリアルになるので、別途取り組む。
別記事へ
Easy Cinematic Look Tutorial – Using Free Software (Blender)のチュートリアルを試す+保存とかNode Groupとか
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)
組み込みテクスチャで雲のある空を作るチュートリアルです。
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.ノードを追加する
を追加。
なお動画では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に設定
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になるように上に移動
1.PlaneのMaterialをnewし、名前をCloudsにする。さらにこのDiffuseは[X]で削除する。
2.続いて以下を設置
3.ColorRampを以下のように設定
4.以下を追加
Transparent BSDFとEmissionをMixし、ColorRampの出力をFacへの入力とする。出力はMaterial Outputへつなげる
5.Noise Textureのdetailを16.0に変更
さらにリアルにしていく。
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]で回転して好みの状態を作ることができる
時間的には残りわずかなんだけれど疲れたので残りは次回。
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)
これは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する。
川を作るチュートリアル約10分です。
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と設定し、縦長にする
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を大きくする
1.Skyを追加する
2.Node Editorを開き、川本体のマテリアルを以下のように設定
そのままだと暗いのでSun足したけれどそれでも暗い。
以下アニメーション版。APNG。
Particleアニメーション、約10分のチュートリアルです。
1.Blenderを起動し、レンダラをCyclesに変更する。
2.Ctrl+Sで最初のCubeを拡大する
3.Editモードへ行き、箱の上面を削除する。その後Objectモードへ戻る
4.[Shift+D]でこのオブジェクトを複製し、場所は変えない。そしてその一方を、Editモードへ行き、視点側の二辺を削除する。
5.二つの重なった長方形のうち、四方の辺を削除しなかった方を選択し、Collisionを有効にした上で表示とレンダリングをOffにする。
この設定で、「物理シミュレーションに影響するが、表示はされない」ようになる。
6.表示したままの方のオブジェクトを選択し、[Solidify]モディファイアを設定し、Thicknessを0.02に設定してApplyする。
ライトの種類を[Sun]、Sizeを[1.0]、Use NodeにしてStrengthを[7.0]に設定する。そしてライトの位置を移動し、ボックスを横から照らすようにする。
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モディファイアを追加する。
1.シャワーヘッドを上記の作り方で作っていれば、オブジェクト中心が流出口付近の中央にあるはずなので、そのまま[Shift+S]→[Cursor to Selected]で3Dカーソルの位置をそこへ移動する。
2.[Shift+A]→[Mesh]→[Circle]で円を追加し、[S]で拡大、[F]で面を張る。面を張らないとParticleが出ない。
作成した円を選択し、Particle Systemを追加する。
ここで一度[Alt+A]を押し、アニメーションを実行してParticleの流出を確認する。
各オブジェクトのマテリアルを設定する
1.流出先の床・壁
2.背景
3.シャワーヘッド
1.先ほど[Alt+A]したので生成されているはずのParticleをクリックし、Particleの設定から[Cache]→[Bake]を実行する
2.[Shift+A]→[Mesh]→[UV Sphere]を追加し、[M]で別のレイヤーに移動する。
3.Particle Systemの[Render]→[Object]に作成したSphereを設定する
4.追加したSphereのマテリアルを設定する
[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球の作り方をやったので、今回はそれを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