色々とやりようはあるしそこらじゅうに転がってるんですがとりあえず自分が納得するものを作ります。
std::wstring towstring(const char* c) { std::wstring tmps; if (c == nullptr) return tmps; size_t sz = strlen(c); tmps.reserve(sz);//メモリを確保し、newが走りすぎないようにする const size_t CNT_MAX = 50; char tmpc[CNT_MAX]; wchar_t tmpw[CNT_MAX]; const char* p = c; assert(p); while (*p != '\0') { int L = mblen(p, CNT_MAX);//pが指し示すマルチバイト文字のバイト数を取得 if (L <= 0) break; strncpy(tmpc, p, L);//tmpcにその一文字をコピーする tmpc[L] = '\0';
//multi byte string to wide char string mbstowcs(tmpw, tmpc, CNT_MAX);//tmpcの終端を0にしてあるので人文字だけ変換する tmps += tmpw; p += L; } return tmps; } std::string tostring(const wchar_t* c) { std::string tmps; if (c == nullptr) return tmps; size_t sz = wcslen(c); tmps.reserve(sz*2);//メモリを確保し、newが走りすぎないようにする const size_t CNT_MAX = 50; char tmpc[CNT_MAX]; wchar_t tmpw[CNT_MAX]; const wchar_t* p = c; while (*p) {
//サロゲートペアなら2文字分、違うなら1文字分だけtmpwに確保 if (IS_HIGH_SURROGATE(*p) == true) { wcsncpy(tmpw, p, 2); tmpw[2] = L'\0'; p += 2; } else { wcsncpy(tmpw, p, 1); tmpw[1] = L'\0'; p += 1; } wcstombs(tmpc, tmpw, CNT_MAX);//tmpwの内容を変換してtmpcに代入 tmps += tmpc; } return tmps; }
使う時はsetlocaleが必要です。
setlocale(LC_ALL, "");//必要 const char *c = "こんにちは"; std::wstring s = towstring(c);
ここで、 IS_HIGH_SURROGATEはWindowsのマクロです。
IS_HIGH_SURROGATE macro (winnls.h) | Microsoft Docs
定義は、WinNls.hの中で、
#define IS_HIGH_SURROGATE(wch) (((wch) >= HIGH_SURROGATE_START) && ((wch) <= HIGH_SURROGATE_END))
#define IS_LOW_SURROGATE(wch) (((wch) >= LOW_SURROGATE_START) && ((wch) <= LOW_SURROGATE_END))
#define IS_SURROGATE_PAIR(hs, ls) (IS_HIGH_SURROGATE(hs) && IS_LOW_SURROGATE(ls))
こんな感じで定義されています。 なお、
#define HIGH_SURROGATE_START 0xd800 #define HIGH_SURROGATE_END 0xdbff #define LOW_SURROGATE_START 0xdc00 #define LOW_SURROGATE_END 0xdfff
stringが指す先のマルチバイト文字のバイト数を返す。
例えば、 'a' なら1 , 'あ'なら2
他所を参照(手抜き)
mblen | Programming Place Plus C言語編 標準ライブラリのリファレンス
スレッドセーフではない。スレッドセーフであってほしいときはmbrlenを使う
マルチバイト文字列をワイド文字列に変換する。mbs to wcs。
他所を参照
mbstowcs | Programming Place Plus C言語編 標準ライブラリのリファレンス
ワイド 文字列を マルチバイト 文字列に変換する。 wcs to mbs。
難しいことではないんですがUIが変わったせいで見つけるのが大変
[Render]→[RenderEngine]→Cyclesを設定
名前がNode EditorからShader Editorに変更されました。
デフォルトで"Principled BSDF"が適用されています。
[Shift+A]のショートカットキーは健在なので、ノードを追加出来ます。
デフォルトでResolutionが100%になっているのでマシンスペックの低い人は注意してください。
SamplingのFinal↔Previewを変更するのは[Render]→[Sampling]のアイコンをクリックします。
注意 バグなのか仕様なのかはわからないけれど"Preview"とか"Final"をクリックした瞬間変わらないで、マウスを選択肢の外に出して一覧が消えた後で変わります。
実行は今までと同じようにRenderメニューからRender Imageを選択します。
レンダリングを開始するとレンダリング用のウィンドウが出ます。このウィンドウを消すとレンダリングが中断出来ます。
結果の確認はRenderingタブから。
保存は[Shift+S]でできます。
わからなくなったら[F3]を押してSaveとかそれっぽい単語を打ち込むと機能が出てくるかもしれません。
Blender 2.8からレイヤー機能がなくなり、Collection機能を使うことになります。
画面右上にあるアイコンからもCollectionの表示を切り替えられます
ショートカットキーは [Ctrl+H]です。
全てのCollectionを表示するショートカットキーは[Alt+H]です。ただし、私がやったときはアウトライナーウィンドウにマウスが置かれていないと動きませんでした。仕様かバグかはよくわかりません。
調べたところ、旧UIに戻す方法はないそうです。
最も混乱するところは、マウスの左ボタンでオブジェクトを選択します。
そしてショートカットキーがだいぶ変わっています。新たな機能、なくなった機能もあり気持ちはわかりますが覚え直しです。
あと全体的にレイアウトというより思想がだいぶ変わってるので違いを挙げるのは大変です(多すぎる)
続く・・・
ツリー構造を作っていた時にできた副産物です。改善の余地はありますが楽です。
case WM_CREATE: { menu_t::SetDefaultMII(); MyMenu m; m.root().set( L"Menu",0);//メニューのルート。表示されない m.root().push(L"File",100);// push(タイトル, ID)の順に記述 m.root().latest().push(L"Load" ,110);//latestはこの階層で最後にpushされたアイテム m.root().latest().push(L"Save" ,120); m.root().latest().push(L"Export",130); m.root().latest().push(L"Import",140); m.root().latest().push(L"Recent",150); m.root().latest().latest().push(L"file1",151); m.root().latest().latest().push(L"file2",152); m.root().latest().latest().push(L"file3",153); m.root().push(L"Edit",200); m.root().latest().push(L"Add" ,210); m.root().latest().push(L"Delete",220); m.root().latest().push(L"Select",230); m.root().push(L"View",300); m.root().latest().push(L"Vertical" ,310); m.root().latest().push(L"Horizonal",320); m.winSetMenu(hwnd); } return 0; case WM_COMMAND://メニューがクリックされた時の処理 switch (LOWORD(wp)) { case 110: MessageBox(hwnd,L"Load",0,0); break; case 120: MessageBox(hwnd,L"Save",0,0); break; } break;
#include <vector> #include<windows.h> struct menu_t{ static MENUITEMINFO defaultMII; MENUITEMINFO mii; std::vector< menu_t > m_submenues; HMENU hmenu_this; menu_t(){} menu_t(wchar_t* title,UINT id,MENUITEMINFO _mii = defaultMII);
//このメニュー項目を設定 void set( wchar_t* title,UINT id,MENUITEMINFO _mii = defaultMII);
//このメニュー項目にサブメニューの項目を追加 void push(wchar_t* title,UINT id,MENUITEMINFO _mii = defaultMII);
//このメニュー項目のサブメニューの末尾を取得 menu_t& latest(); static void SetDefaultMII(); }; //メニュー管理クラス class MyMenu{ menu_t m_root; HMENU hMenuTop;
//メニュー登録のエントリポイント。内部でSetMenuesを呼び出す HMENU defineMenu();
//登録作業の本体。InsertMenuItemを呼び出している void SetMenues(HMENU hmenu , menu_t& menu); public: menu_t& root(); MyMenu();
//Win32apiのSetMenuを呼び出し、実際にメニューを登録する BOOL winSetMenu(HWND hwnd); };
#include "nuWinMenu.h" MENUITEMINFO menu_t::defaultMII; menu_t::menu_t(wchar_t* title,UINT id,MENUITEMINFO _mii){ set(title,id,_mii); } void menu_t::set( wchar_t* title,UINT id,MENUITEMINFO _mii){
//このメニュー項目のタイトルとIDを設定 mii = _mii; mii.fMask = MIIM_ID | MIIM_STRING; mii.wID = id; mii.dwTypeData = title; } void menu_t::push(wchar_t* title,UINT id,MENUITEMINFO _mii){
//このメニュー項目のサブメニューにアイテムを追加 m_submenues.emplace_back(title,id,_mii); } menu_t& menu_t::latest(){
//このメニュー項目に最後に追加されたサブメニュー項目を取得 return m_submenues.back(); } void menu_t::SetDefaultMII(){ memset(&defaultMII, 0, sizeof(MENUITEMINFO)); defaultMII.cbSize = sizeof(MENUITEMINFO); defaultMII.fMask = MIIM_ID | MIIM_STRING; } ///////////////////////////////////////////////////////// HMENU MyMenu::defineMenu(){ hMenuTop = CreateMenu();//メニュー作成 for(size_t i = 0; i < m_root.m_submenues.size();i++){
//トップレベルのメニュー項目を登録する SetMenues(hMenuTop,m_root.m_submenues[i]); } m_root.hmenu_this = hMenuTop; return hMenuTop; } void MyMenu::SetMenues(HMENU hmenu , menu_t& menu){ if(menu.m_submenues.size()!=0){ HMENU submenu = CreatePopupMenu();//ポップアップメニュー作成 menu.mii.hSubMenu = submenu; menu.mii.fMask |= MIIM_SUBMENU;//この項目にサブメニューがあることを指示 InsertMenuItem(hmenu, menu.mii.wID, FALSE, &menu.mii);
//サブメニューの各項目を登録 for(size_t i = 0; i < menu.m_submenues.size();i++){ SetMenues(submenu,menu.m_submenues[i]); } } else{ InsertMenuItem(hmenu, menu.mii.wID, FALSE, &menu.mii); } menu.hmenu_this = hmenu; } menu_t& MyMenu::root(){ return m_root; } MyMenu::MyMenu(){ hMenuTop = 0; } BOOL MyMenu::winSetMenu(HWND hwnd){ return SetMenu( hwnd,defineMenu() ); }
#include <Windows.h> #include <tchar.h> struct rgb_t { unsigned char r; unsigned char g; unsigned char b; rgb_t(unsigned char R, unsigned char G, unsigned char B) :b(B), g(G), r(R) {} static const rgb_t White; static const rgb_t Black; }; void createDIBsection24( HBITMAP* hBitmap, HDC* hMemDC, BITMAPINFO* bmpInfo, rgb_t** m_lpPixel, LONG width, LONG height); void deleteDIBsection24(HDC hMemDC, HBITMAP hBitmap);
#include "dibsection.h" void createDIBsection24( HBITMAP* hBitmap, HDC* hMemDC, BITMAPINFO* bmpInfo, rgb_t** m_lpPixel, LONG width, LONG height) { //DIBの情報を設定する bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo->bmiHeader.biWidth = width; bmpInfo->bmiHeader.biHeight = -height; //-を指定しないと上下逆になる bmpInfo->bmiHeader.biPlanes = 1; bmpInfo->bmiHeader.biBitCount = 24; bmpInfo->bmiHeader.biCompression = BI_RGB; HDC hdc = CreateDC(_T("DISPLAY"), 0, 0, 0); *hBitmap = CreateDIBSection(hdc, bmpInfo, DIB_RGB_COLORS, (void**)m_lpPixel, NULL, 0); *hMemDC = CreateCompatibleDC(hdc); DeleteDC(hdc); SelectObject(*hMemDC, *hBitmap); } void deleteDIBsection24(HDC hMemDC, HBITMAP hBitmap) { DeleteDC(hMemDC); DeleteObject(hBitmap); }
#include <Windows.h> void initGL(HGLRC *hRC,HDC hdc); bool DeInitGL(HGLRC hRC, HDC hdc);
ウィンドウに書き込む場合、PIXELFORMATDESCRIPTORのdwFlagsは:
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER
のようになるが、DIBに書き込む場合はPFD_DRAW_TO_WINDOWの代わりにPFD_DRAW_TO_BITMAPを使い、かつ、PFD_SUPPORT_GDIを使う。
#include "wingl.h" #include <Windows.h> void initGL(HGLRC *hRC, HDC hdc) { static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_BITMAP | PFD_SUPPORT_GDI | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, //Z-Buffer 0, //Stencil Buffer 0, PFD_MAIN_PLANE, 0, //Reserved 0, 0, 0 }; SetPixelFormat(hdc, ChoosePixelFormat(hdc, &pfd), &pfd); *hRC = wglCreateContext(hdc); //レンダリングコンテキストの作成 wglMakeCurrent(hdc, *hRC); //hRCを有効にする } bool DeInitGL(HGLRC hRC, HDC hdc) { //解放コード wglMakeCurrent(hdc, hRC); //元に戻してやる wglDeleteContext(hRC); //いらなくなったレンダリングコンテキストの破棄 return true; }
HBITMAP dib_hBitmap; HDC dib_hMemDC; BITMAPINFO dib_bmpInf rgb_t* dib_lpPixel;
HGLRC wgl_hRC;
switch (msg) { case WM_CREATE: { //DIB Sectionを作成(24Bit) createDIBsection24( &dib_hBitmap, &dib_hMemDC, &dib_bmpInfo, &dib_lpPixel, width, height); //OpenGL初期化 initGL(&wgl_hRC, dib_hMemDC); draw(); break; }
case WM_PAINT: { // 画像を表示 PAINTSTRUCT ps; HDC hDC = BeginPaint(hwnd, &ps); BitBlt(hDC, 0, 0, width, height, dib_hMemDC, 0, 0, SRCCOPY); EndPaint(hwnd, &ps); break; } case WM_LBUTTONDOWN: //マウスをクリックしたら画像を更新して描画要求を出す draw(); InvalidateRect(hwnd, nullptr, FALSE); break;
void draw() { static int r = 0; wglMakeCurrent(dib_hMemDC, wgl_hRC); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glRotated(r += 10, 0, 1, 0); glBegin(GL_QUADS); glColor3d(1, 0, 0); glVertex3d(-0.7, -0.7, 0); glColor3d(0, 1, 0); glVertex3d(-0.7, 0.7, 0); glColor3d(0, 0, 1); glVertex3d(0.7, 0.7, 0); glColor3d(1, 1, 1); glVertex3d(0.7, -0.7, 0); glEnd(); glFlush(); SwapBuffers(dib_hMemDC); wglMakeCurrent(dib_hMemDC, 0); }
Step 4 - Rendering and Exporting [ 9:26~ ]
SmokeのFlowになっているオブジェクトを選択し、[Object]-[Cycles Settings]-[Ray Visibility]-Cameraのチェックを外す。これをしないとレンダリング結果に表示される。
加えて、ドメインの[Border Collisions]をCollide Allに設定する。これを設定しないとSmokeがDomainの外に出てしまってアニメーションとして面白みがなくなる。
Step 5 - Post Production in After Effects [12:17~ ]
以降はBlenderではなくAfter Effectsに入るので省略する。
Blender CyclesでSmokeのチュートリアルを試す (1)
Blender CyclesでSmokeのチュートリアルを試す (2)
光源がレンダリングされないように、
[Object]-[Cycles Settings]-[Ray Visibility]-Cameraのチェックを外す
Blender CyclesでSmokeのチュートリアルを試す (1)
Blender CyclesでSmokeのチュートリアルを試す (2)
Cubeを選択し、Editモードへ行く。[W]→Subdivide Smoothを実行し、より自然な形状にする。
Temp. Diffを-1.0に設定する。環境との温度差が逆となり、煙が下に流れるようになる。
キーフレームにColorを指定することで、複雑な色のSmokeを作成出来る。
Blender CyclesでSmokeのチュートリアルを試す (1)
Blender CyclesでSmokeのチュートリアルを試す (2)
Blender CyclesでSmokeのチュートリアルを試す (3)
Blender CyclesでSmokeのチュートリアルを試す (4)
Smoke Simulation Tutorial - Blender Cycles
キューブを選択し、[Object]-[Quick Effects]-Quick Smokeを選択する。
Divisionsを上げると表示が遅くなる。Smoke Adaptive Domainをチェックすると、計算範囲を限定しシミュレーションの速度が上がる。
Smokeのsubdivision modifierを Smoke High Resolutionという。
また、WaveletというノイズのStrengthのパラメータを0にしておく。
Blender CyclesでSmokeのチュートリアルを試す (1)
Blender CyclesでSmokeのチュートリアルを試す (2)