古いVisual Studioのダウンロードが必要だったので調べた。
左側のツリーを展開して青いボタンを押すとダウンロードできる。
https://docs.microsoft.com/ja-jp/visualstudio/productinfo/
glfwでは複数のウィンドウを作れる。以下はそのサンプルコード。
ただし別に別スレッドで動いているというものでもない。メッセージループは一つ。
イベント処理時は、コールバック関数がウィンドウへのポインタを第一引数に渡すので、それを比較して呼び出し元を区別できるが、違う関数を呼び出しても問題ない(はずだ)し普通はそうすべきだと思う。
#include <cstdio> #include <Windows.h> #include <gl/GL.h> #include <GLFW/glfw3.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"glfw3.lib") // ウィンドウを格納する変数 GLFWwindow* window1; GLFWwindow* window2;//キー入力を処理するコールバック関数 void key_callback(GLFWwindow* pwin, int key, int scancode, int action, int mods) { if ( action != GLFW_PRESS) return; //ウィンドウの判別をポインタで行う if (window1 == pwin) { printf("window 1 "); } if (window2 == pwin) { printf("window 2 "); } //キーイベント if ( key == GLFW_KEY_UP) { printf("key up\n"); } else if ( key == GLFW_KEY_DOWN ) { printf("key down\n"); } else if ( key == GLFW_KEY_LEFT ) { printf("key left\n"); } else if ( key == GLFW_KEY_RIGHT ) { printf("key right\n"); } else { printf("\n"); } } // マウスクリックを処理するコールバック関数 void mouse_button_callback(GLFWwindow* pwin, int button, int action, int mods) { if (action != GLFW_PRESS) { return; } //ウィンドウの判別をポインタで行う if (window1 == pwin) { printf("window 1 "); } if (window2 == pwin) { printf("window 2 "); } //マウスイベント if (button == GLFW_MOUSE_BUTTON_LEFT) { printf("L - down\n"); } else if (button == GLFW_MOUSE_BUTTON_RIGHT) { printf("R - down\n"); } else if (button == GLFW_MOUSE_BUTTON_MIDDLE) { printf("M - down\n"); } else { printf("\n"); } }void render1(int width,int height);//window 1 用描画関数 void render2(int width, int height);//window 2 用描画関数int main() { if (glfwInit() == GL_FALSE) { return 1; } window1 = glfwCreateWindow(400,400,"window 1",NULL,NULL); window2 = glfwCreateWindow(400,400,"window 2",NULL,NULL); if (window1 == nullptr) { glfwTerminate(); return 1; } if (window2 == nullptr) { glfwTerminate(); return 1; } //////////////////////////////////////////// // コールバック関数の設定 glfwSetKeyCallback(window1, key_callback); glfwSetKeyCallback(window2, key_callback); glfwSetMouseButtonCallback(window1, mouse_button_callback); glfwSetMouseButtonCallback(window2, mouse_button_callback); // //////////////////////////////////////////// while (glfwWindowShouldClose(window1) == GL_FALSE) { int width, height; //////////////////////////////////////////////////////// glfwGetFramebufferSize(window1, &width, &height); glfwMakeContextCurrent(window1); render1(width,height); glfwSwapBuffers(window1); //////////////////////////////////////////////////////// glfwGetFramebufferSize(window2, &width, &height); glfwMakeContextCurrent(window2); render2(width, height); glfwSwapBuffers(window2); //////////////////////////////////////////////////////// // イベント取得 glfwWaitEvents(); } glfwTerminate(); }void render1(int width, int height) { glViewport(0, 0, width, height); glOrtho(-1, 1, -1, 1, -1, 1); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_QUADS); glColor3d(1, 0, 0); glVertex2d(-0.7, -0.7); glColor3d(0, 1, 0); glVertex2d(-0.7, 0.7); glColor3d(0, 0, 1); glVertex2d(0.7, 0.7); glColor3d(1, 1, 1); glVertex2d(0.7, -0.7); glEnd(); } void render2(int width, int height) { glViewport(0, 0, width, height); glOrtho(-1, 1, -1, 1, -1, 1); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_QUADS); glColor3d(0, 1, 1); glVertex2d(-0.7, -0.7); glColor3d(1, 0, 1); glVertex2d(-0.7, 0.7); glColor3d(1, 1, 0); glVertex2d(0.7, 0.7); glColor3d(0, 0, 0); glVertex2d(0.7, -0.7); glEnd(); }
ドキュメントトップ
イベントの扱いについて。
キーの定数一覧:
#include <cstdio> #include <Windows.h> #include <gl/GL.h> #include <GLFW/glfw3.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"glfw3.lib") // 参考文献 //////////////////////////////////////// // https://www.glfw.org/docs/latest/input_guide.html
//キー入力を処理するコールバック関数 void key_callback(GLFWwindow* pwin, int key, int scancode, int action, int mods) { // https://www.glfw.org/docs/latest/group__keys.html#ga99aacc875b6b27a072552631e13775c7 // action ... GLFW_PRESS , GLFW_REPEAT , GLFW_RELEASE //特殊キー if (key == GLFW_KEY_UP && action == GLFW_PRESS) { printf("key up\n"); } if (key == GLFW_KEY_DOWN && action == GLFW_PRESS) { printf("key down\n"); } if (key == GLFW_KEY_LEFT && action == GLFW_PRESS) { printf("key left\n"); } if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS) { printf("key right\n"); } if (key >= GLFW_KEY_A && key <= GLFW_KEY_Z) { if (action == GLFW_PRESS) { const char* key_name = glfwGetKeyName(key, 0); printf("key - %s\n", key_name); } } }// マウスクリックを処理するコールバック関数 void mouse_button_callback(GLFWwindow* pwin, int button, int action, int mods) { if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) { printf("L - down\n"); } if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) { printf("R - down\n"); } if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) { printf("M - down\n"); } }// マウスホイールを処理するコールバック関数 void mouse_wheel_callback(GLFWwindow* window, double xoffset, double yoffset) { if (yoffset < 0) { printf("wheel down \n"); } if (yoffset > 0) { printf("wheel up \n"); } }int main() { if (glfwInit() == GL_FALSE) { return 1; } GLFWwindow* window = glfwCreateWindow( 400, 400, "window title", NULL, NULL ); if (window == nullptr) { glfwTerminate(); return 1; } //////////////////////////////////////////// // コールバック関数の設定 glfwSetKeyCallback(window, key_callback); glfwSetMouseButtonCallback(window, mouse_button_callback); glfwSetScrollCallback(window, mouse_wheel_callback); // //////////////////////////////////////////// glfwMakeContextCurrent(window); while (glfwWindowShouldClose(window) == GL_FALSE) { int width, height; glfwGetFramebufferSize(window, &width, &height); ///////////////////// // 描画 glViewport(0, 0, width, height); glOrtho(-1, 1, -1, 1, -1, 1); glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT); // ///////////////////// glfwSwapBuffers(window); // イベント取得 glfwWaitEvents(); } glfwTerminate(); }
最近ではglutよりglfwという風潮がある。
そこでglfwでウィンドウを表示して簡単な描画までを行う。
https://www.glfw.org/download.html
#include <cstdlib> #include <iostream>
#include <Windows.h> #include <gl/GL.h> #include <GLFW/glfw3.h> #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"glfw3.lib") int main() { //////////////////////////////////////////////////////////////////////////////// // GLFW の初期化 if (glfwInit() == GL_FALSE) { // 初期化に失敗したら終了 return 1; } //////////////////////////////////////////////////////////////////////////////// // ウィンドウを作成 GLFWwindow* window = glfwCreateWindow( 400, //width 400, //height "window title",//title NULL, //monitor NULL //share ); //////////////////////////////////////////////////////////////////////////////// // ウィンドウを作成できなければ終了 if (window == nullptr) { glfwTerminate(); return 1; } glfwMakeContextCurrent(window); while (glfwWindowShouldClose(window) == GL_FALSE) { int width, height; glfwGetFramebufferSize(window, &width, &height);
///////////////////// // 描画 glViewport(0, 0, width, height); glOrtho(-1, 1, -1, 1, -1, 1); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_QUADS); glColor3d(1, 0, 0); glVertex2d(-0.7, -0.7); glColor3d(0, 1, 0); glVertex2d(-0.7, 0.7); glColor3d(0, 0, 1); glVertex2d( 0.7, 0.7); glColor3d(1, 1, 1); glVertex2d(0.7, -0.7); glEnd(); // /////////////////////glfwSwapBuffers(window); // イベント取得 glfwWaitEvents(); } glfwTerminate(); }
当然だが、File → Export → Stanford(.ply)でply出力できる。
しかしその時、出力されたPLYファイルを見ると、頂点が重複している。
重複頂点を削除するオプションがどうも見つからなかったので、別の方法を考える。最も確実な方法は、おそらくMeshLab等の有名どころを使って重複頂点削除コマンドを実行すること。
MeshLab → [Filters] → [Cleaning and Repairing] → [Remove Duplicate Vertices]
あるいは、plyは比較的わかりやすいフォーマットなので出力プログラムを自力で書いてもいいかもしれない。
import bpy msh = bpy.context.active_object fname = 'C:/test/' + msh.name + '.ply' with open(fname, 'w') as myf: print( "ply" ,file=myf ) print( "format ascii 1.0" ,file=myf ) print( "element vertex",len(msh.data.vertices) ,file=myf ) print( "property float x" ,file=myf ) print( "property float y" ,file=myf ) print( "property float z" ,file=myf ) print( "property float nx" ,file=myf ) print( "property float ny" ,file=myf ) print( "property float nz" ,file=myf ) print( "element face",len(msh.data.polygons) ,file=myf ) print( "property list uchar int vertex_index" ,file=myf ) print( "end_header" ,file=myf ) for vt in msh.data.vertices: print(vt.co[0],vt.co[1],vt.co[2], " " ,end="",file=myf ) #頂点座標出力 print(vt.normal[0],vt.normal[1],vt.normal[2],file=myf ) #頂点法線出力 for f in msh.data.polygons: print( len(f.vertices) ," ", end="",file=myf ) #各面の頂点数出力 for v in f.vertices: print( v ," ", end="" ,file=myf ) #各面を構成する頂点の頂点番号を出力 print( "" ,file=myf )
MeshLabで頂点数の確認をする。Verticesが減っているのがわかる


以下のように、結果がやや異なる。頂点数の違いは重複と見なす距離の差だと思う(想像)。四角形以上のNgonの扱いも異なっている面法線の再計算の有無あたりかとも思うが不明。
まず以下のスクリプトをおもむろに実行する
import bpy ############################################## # ボタンを定義 class ConeButton(bpy.types.Operator): bl_idname = "szl.button" bl_label = "Add a CONE" def execute(self, context): bpy.ops.mesh.primitive_cone_add() return{'FINISHED'} ############################################## # パネルの項目を定義 class ConeUI(bpy.types.Panel): bl_label = "panel title" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "AddConePanel" def draw(self, context): self.layout.operator("szl.button") ############################################## classes = ( ConeUI, ConeButton ) ############################################## # アドオン有効化時に呼び出される def register(): for c in classes: bpy.utils.register_class(c) # アドオン無効化時に呼び出される def unregister(): for c in classes: bpy.utils.unregister_class(c)if __name__ == "__main__": register()
すると、パネルに新しい項目が追加され、ボタンを押すとConeが生成される。
これは上記スクリプトの最後の register() 関数が実行されたため。
しかしBlenderを再起動すれば消えてしまう。そこでまず以下のように変更を加える。ファイル名は add-cone-addon.py とでもしておく。
下記、bl_infoの部分があるとアドオンとして認識される。
また、registerはBlenderがアドオンを読み込んだときに自動で呼び出されるので、一番下の呼び出し部分を削除する。
import bpy ##############################################bl_info = { "name" : "sample: add-on-name", "author" : "yodori soratori", "version" : (1, 0), "blender" : (2, 81, 0), "location" : "location", "description": "sample", "warning" : "warning message", # 空だと!アイコンが出ない "support" : "TESTING", # または OFFICIAL または COMMUNITY "wiki_url" : "", "tracker_url": "", "category" : "Object" #アドオンのカテゴリ }############################################## # ボタンを定義 class ConeButton(bpy.types.Operator): bl_idname = "szl.button" bl_label = "Add a CONE" def execute(self, context): bpy.ops.mesh.primitive_cone_add() return{'FINISHED'} ############################################## # class ConeUI(bpy.types.Panel): bl_label = "panel title" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "AddConePanel" def draw(self, context): self.layout.operator("szl.button") ############################################## classes = ( ConeUI, ConeButton ) ############################################## # アドオン有効化時に呼び出される def register(): for c in classes: bpy.utils.register_class(c) # アドオン無効化時に呼び出される def unregister(): for c in classes: bpy.utils.unregister_class(c)#if __name__ == "__main__": # register()
そして、Preference → Add-ons でアドオンとしてインストールする

https://dskjal.com/blender/ui-script.html
エッジを二本選択し、その交点を求めたい。
How can I add vertices to intersection of two edges?
https://blender.stackexchange.com/questions/2976/how-can-i-add-vertices-to-intersection-of-two-edges
のPythonスクリプトを実行すると、二辺の交差点に頂点を一つ追加できる。(Blender 2.79 , 2.8 両対応)
しかし頂点が一つ置かれるだけだと不便なので、各エッジをsubdivideする形で追加するように変更した。
コードは以下:
import bmesh import bpy from mathutils import geometry # get cross point of 2 edges def calc_cross_coordinate(edge2): if len(edge2) == 2: [[v1, v2], [v3, v4]] = [[v.co for v in e.verts] for e in edge2] iv = geometry.intersect_line_line(v1, v2, v3, v4) iv = (iv[0] + iv[1]) / 2 return iv # subdivide 2 edges and move the points to cross point def subdivide_selects(edge2, moveto): obj = bpy.context.object me = obj.data bm = bmesh.from_edit_mesh(me) ret = bmesh.ops.subdivide_edges(bm, edges=edge2, use_grid_fill=True, cuts=1) bmesh.update_edit_mesh(me) for i in ret['geom_split']: if type(i) == bmesh.types.BMVert: i.co=moveto def cross_subdivide(): obj = bpy.context.object me = obj.data bm = bmesh.from_edit_mesh(me) edge2 = [e for e in bm.edges if e.select] if len(edge2) == 2: pos = calc_cross_coordinate(edge2) subdivide_selects(edge2,pos) cross_subdivide()
今回の処理を行う関数。
エッジが二本選択されているとき、二本のエッジをsubdivideし、新規頂点を交点に作成する。
二本のエッジを引数にとり、geometry.intersect_line_lineで交差している座標を求める。
三次元の線分の場合、現実には誤差の関係で絶対に交差しないので、交点は二つ求まる。その中間点を交点(iv)としている。
bmesh.ops.subdivide_edgesでオブジェクトをsubdivideしている。
戻り値の['geom_split']に作成されたデータが入っているので、データ型がBMVertのものだけを取り出し、座標を交点で上書きする
glutBitmapCharacterは珍しい関数では無いけれどなぜかどのサンプルもzを殺している。個人的には以下のようにx,y,zの指定をちゃんとやった方が圧倒的に使いやすい。
//! @brief 文字列を表示 //! @param [in] x 三次元の座標(X) //! @param [in] y 三次元の座標(Y) //! @param [in] z 三次元の座標(Z) //! @param [in] str 文字列(英語のみ) void render_string(float x, float y, float z, const char* str) { glRasterPos3f(x, y, z); const char* c = str; while (*c) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *c++); } }

単に現在の状態を表示するだけならコンソールでいいし、見栄えが重要ならそもそもglutなんて使わないし、何より、文字を左上に出すならちゃんとglLoadIdentityしてから世界座標系で指定する必要がある。手間を省けてない。

//! @brief 文字列を表示 //! @param [in] x 三次元の座標(X) //! @param [in] y 三次元の座標(Y) //! @param [in] z 三次元の座標(Z) //! @param [in] str 文字列(英語のみ) void render_string(float x, float y, float z, const char* str) { glRasterPos3f(x, y, z); const char* c = str; while (*c) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *c++); } } int width, height; //回転オブジェクト定義 nu::mrotate camr; //表示 void disp(void) { glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, width, height); glPushMatrix(); //原点を視線方向に0.5ずらす glTranslated(0, 0, -0.5); //回転行列を適用 double mat[16]; glMultMatrixd(camr.getmatrix(mat)); //一辺0.7のキューブを描画 cube(0.7); glColor3d(1, 1, 1); double ws = -0.7 / 2; double we = 0.7 / 2; render_string(ws, ws, ws, "min"); render_string(we, we, we, "max"); glPopMatrix(); glLoadIdentity(); render_string(-1, 0.9, 0, "sample"); glFlush(); }
ずっと不便き極まりなかったことが今更だがわかったのでここに残しておきたい。というか、世の人は多分今の方が使いやすいか、簡単に戻し型がわかってしまうのだろう。だから私の使う検索ワードではヒットしないのである。
ある日の更新を境に、Firefoxホームに表示されたgoogleやamazonをクリックすると検索になってしまい、サイトに飛ばなくなった。
記憶が確かなら元々はよくアクセスするサイトが自動で出てきていたのに、虫眼鏡マークのボタンに上書きされてしまった格好だったと思う。


@がついていると検索ワード入力になるのかとか思って色々試したが戻らなかった。
@amazonと@googleは検索エンジン扱いになっている。つまりリンクでは無い。だからトップサイトとして登録し、それとは全く別に、(必要であれば)検索エンジンとしてのアイコンを無効にする。
としたのち、以下でapacheを再起動
以下でいいはずなのだが、
なぜか動いてくれないので、以下を実行する
「ドキュメント」の左上の「新しいドキュメント」をクリック
「コンテンツ」に本文を書いて「保存」をクリック。「プレビュー」で表示を確認できる。
HTMLタグを直に打てる
サイトのトップページの設定がデフォルトではHomeになっているので、システム設定で site_start の項目を検索し、トップページにしたい記事のIDを設定する