Skip to main content

Featured

Build docker image from multiple build contexts

Build docker image from multiple build contexts Building a docker image requires specifying a source of truth to include in the image from a local directory or a remote git repository. In the previous version, the docker BuildKit allows users to specify the build context from a single source of truth only. However, the engineers may need to have the context from different locations based on the type of files. For instance, icons, images or other resources that are not included in the same package, including the resource from other docker images. Fortunately, the Docker Buildx toolkit supports multiple build context flag for Docker 1.4. Let's learn how to use this new feature. The following list is a shortcut for jumping into a specific topic handy. What version of Docker is this tutorial targeting? How to specify the version of Dockerfile frontend? Ho

[MFC]使用Picture Control來當做DrawScene

參考來源:
1.http://blog.sina.com.cn/s/blog_6ac675410100o0t6.html
2.http://www.codeguru.com/cpp/cpp/cpp_mfc/tutorials/article.php/c10975__2/Setting-Up-OpenGL-in-an-MFC-Control.htm
PS.內部參數皆先參考範例內資料。

這邊使用的為Dialog-based的FormView。因此在Dialog上拉出一個Picture Control即可。
ID可以隨便設定,但是Visible的屬性要設成false
根據作者的說法,有些狀況下,如果把Visible的屬性設成True的話,那麼在畫面重新繪製的時候,OpenGL繪製的畫面,會被Windows繪製PictureControl的畫面蓋掉。
目前測試手頭機器的確會有這樣的狀況。

需載入的header為以及。並引用opengl32.lib。

接下來要做的,就是新增用來繪製OpenGL用的Class。名稱也是一樣可以自由設定,這邊是使用COpenGLDrawer。
1.需要預先宣告的是Timer的指標以及相關需用來繪圖的成員。
 class COpenGLDrawer :    public CWnd  
 {  
 //================ Data member ==================  
     public:  
         UINT_PTR m_unpTimer;      //timer  
     private:  
 //Window Information - for draw scene  
         CWnd *m_hWnd;           //Handle of parent window  
         HDC m_hdc;              //Handle of DC for OpenGL using  
         HGLRC    m_hrc;         //Handle of RC for OpenGL using  
         int m_nPixelFormat;     //Pixel format  
         CRect m_rect;           //For getting window rect  
         CRect m_rectOldWindow;  //rect of previous displayed window  
         CRect m_rectOrgRect;    //rect of original displayed window  
 };  
相關成員也要記得先初始化,避免使用到錯誤的數值。

2.建立glCreate()。此函式是用來向系統註冊Class、建立OpenGL使用之視窗屬性以及記錄視窗的初始設定。
Header:
 class COpenGLDrawer :    public CWnd  
 {  
 ....  
 //============== Member Function =================  
     public:  
         COpenGLDrawer(void);  
         ~COpenGLDrawer(void);  
         void glCreate(CRect rect, CWnd *parent);  
 ....  
 };  

CPP:
 void COpenGLDrawer::glCreate(CRect rect, CWnd *parent)  
 {  
     CString cstrClassName = ::AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_OWNDC,  
                                                                                 NULL,  
                                                                                 (HBRUSH)::GetStockObject(BLACK_BRUSH),  
                                                                                 NULL);  
     CreateEx(0, cstrClassName, "OpenGL", WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN, rect, parent, 0);  
     //Set initail variable's value  
     this->m_rectOldWindow = rect;  
     this->m_rectOrgRect = rect;  
     this->m_hWnd = parent;  
 }  

3.由於OpenGL是使用Timer來進行畫面的繪製,因此就不適用於MFC所提供的繪製畫面的方法。因此需要跳過Windows繪圖的函式 - OnPaint()(WM_PAINT)。在OnPaint()內,使用ValidateRect()來讓Picture Control不被更新。
 void COpenGLDrawer::OnPaint()  
 {  
     //CPaintDC dc(this); // device context for painting  
     // TODO: Add your message handler code here  
     // Do not call CWnd::OnPaint() for painting messages  
     ValidateRect(NULL);  //Remove all of region of the client from the updatr region
 }  

4.建立產生OpenGL視窗的函式。裡面呼叫用來初始化OpenGL視窗的相關參數。這邊則改寫WM_CREATE之內容。
 int COpenGLDrawer::OnCreate(LPCREATESTRUCT lpCreateStruct)  
 {  
     if (CWnd::OnCreate(lpCreateStruct) == -1)  
         return -1;  
     // TODO: Add your specialized creation code here  
     this->glInitialize();  
     return 0;  
 }  

5.建立初始化OpenGL之函式。
Header:
 class COpenGLDrawer :    public CWnd  
 {  
 ....  
 //============== Member Function =================  
     public:  
         COpenGLDrawer(void);  
         ~COpenGLDrawer(void);  
         void glCreate(CRect rect, CWnd *parent);  
     protected:  
         void glInitialize(void);  
 };  

CPP:
 void COpenGLDrawer::glInitialize(void)  
 {  
 //Inital Setup  
     static PIXELFORMATDESCRIPTOR pfd =  
     {  
         sizeof(PIXELFORMATDESCRIPTOR),  
         1,  
         PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,  
         PFD_TYPE_RGBA,  
         32,                                //bit depth  
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  
         16,                                //z-buffer depth  
         0, 0, 0, 0, 0, 0, 0  
     };  
 //Get device context only once  
     this->m_hdc = GetDC()->m_hDC;  
 //Pixel format  
     this->m_nPixelFormat = ::ChoosePixelFormat(this->m_hdc, &pfd);  
     ::SetPixelFormat(this->m_hdc, this->m_nPixelFormat, &pfd);  
 //Create the OpenGL Rendering Context  
     this->m_hrc = ::wglCreateContext(this->m_hdc);  
     ::wglMakeCurrent(this->m_hdc, this->m_hrc);  
 //Basisc Setup:  
     //Set color to use when clearing the background  
     ::glClearColor(0.0f, 0.0f, 0.0f, 1.0f);  
     ::glClearDepth(1.0f);  
     //Turn on backface culling  
     ::glFrontFace(GL_CCW);  
     ::glCullFace(GL_BACK);  
     //Turn on depth testing  
     ::glEnable(GL_DEPTH_TEST);  
     ::glDepthFunc(GL_LEQUAL);  
     //Send draw request  
     this->OnDraw(NULL);  
 }  

6.建立OnDraw函式。此函式內處理圖形的移動以及旋轉,也就是鏡頭的控制。
Header:
 class COpenGLDrawer :    public CWnd  
 {  
 ....  
 // View information variables if camera - for mouse move  
         float m_fLastX;  
         float m_fLastY;  
         float m_fPosX;  
         float m_fPosY;  
         float m_fZoom;  
         float m_fRotX;  
         float m_fRotY;  
 //============== Member Function =================  
 ....  
     public:  
         afx_msg void OnDraw(CDC *pDC);  
 ....  
 };  

CPP:
 void COpenGLDrawer::OnDraw(CDC *pDC)  
 {  
   // TODO: Camera controls.  
     glLoadIdentity();  
     glTranslatef( 0.f, 0.f, -this->m_fZoom );  
     glTranslatef( this->m_fPosX, this->m_fPosY, 0.f);  
     glRotatef( this->m_fRotX, 1.f, 0.f, 0.f );  
     glRotatef( this->m_fRotY, 0.f, 1.f, 0.f);  
 }  

7.新增圖像更新用的OnTimer()(WM_TIMER)。
 void COpenGLDrawer::OnTimer(UINT_PTR nIDEvent)  
 {  
     // TODO: Add your message handler code here and/or call default  
     switch (nIDEvent)  
     {  
         case 1:          
         //Clear color and depth buffer bits  
             ::glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);  
         //Draw OpenGL scene  
             this->glDrawScene();  
         //Swap buffers  
             ::SwapBuffers(this->m_hdc);  
             break;  
         default:  
             break;  
     }    //End of switch (nIDEvent)  
     CWnd::OnTimer(nIDEvent);  
 }  

8.當視窗有需要自由變更大小時,則需改寫OnSize()(WM_SIZE)。
 void COpenGLDrawer::OnSize(UINT nType, int cx, int cy)  
 {  
     CWnd::OnSize(nType, cx, cy);  
     // TODO: Add your message handler code here  
     if ( 0>= cx || 0 >= cy || nType == SIZE_MINIMIZED ) {  
         return; };  
 //Resize  
     switch (nType)  
     {  
         case SIZE_MAXIMIZED:        // If widow resize token is "maximize"  
             //get the current window rect  
             GetWindowRect(m_rect);  
             //Move the window accordingly  
             MoveWindow(6, 6, (cx-14), (cy-14));  
             //Get the new window rect  
             GetWindowRect(this->m_rect);  
             //Store our old window as the new rect  
             this->m_rectOldWindow = this->m_rect;  
             break;  
         case SIZE_RESTORED:        //if window resize token is "restore"  
             //If the window is currently maximized  
             if ( this->m_bIsMaximized )  
             {  
                 //Get the current window rect  
                 GetWindowRect(this->m_rect);  
                 //Move the window accordingly ( to our stored old window)  
                 MoveWindow(this->m_rectOldWindow.left, (this->m_rectOldWindow.top-18), (this->m_rectOrgRect.Width()-4), (this->m_rectOrgRect.Height()-4));  
                 //Get the new window rect  
                 GetWindowRect(this->m_rect);  
                 //Store our old window as the new rect  
                 this->m_rectOldWindow = this->m_rect;  
             }    //End of if ( this->m_bIsMaximized )  
             break;  
     }    //End of switch (nType)  
 //Map the OpengGL coordinates  
     glViewport(0, 0, cx, cy);  
 //Projection view  
     glMatrixMode(GL_PROJECTION);  
     glLoadIdentity();  
 //Set our current view perspective  
     gluPerspective(35.0f, (float)cx / (float)cy, 0.01f, 2000.0f);  
 //Mode view  
     glMatrixMode(GL_MODELVIEW);  
 }  

當然也是要改寫Dialog的OnSize()。再由Dialog來呼叫OpenGL class來進行重新繪製。
 void CMainDlg::OnSize(UINT nType, int cx, int cy)  
 {  
     CDialog::OnSize(nType, cx, cy);  
     // TODO: Add your message handler code here  
     switch(nType)  
     {  
         case SIZE_RESTORED:  
             if ( this->m_glWindow.m_bIsMaximized ) {  
                 this->m_glWindow.OnSize(nType, cx, cy);  
                 this->m_glWindow.m_bIsMaximized = false;  
             }    //End of if ( this->m_glWindow.m_bIsMaximized )  
             break;  
         case SIZE_MAXIMIZED:  
             this->m_glWindow.OnSize(nType, cx, cy);  
             this->m_glWindow.m_bIsMaximized = true;  
             break;  
     }    //End of switch(ntype)  
 }  

9.當Timer設定完成後,即可定期的去做任何的更新動作。所以如有需要於畫面上繪製圖形,則需自行新增繪圖函式,並在OnTimer()內呼叫。此處宣告的繪圖函式為glDrawScene()。此處繪製一六面體。
Header:
 class COpenGLDrawer :    public CWnd  
 {  
 ....  
     protected:  
         void glInitialize(void);  
         void glDrawScene(void);  
 };  

CPP:
 void COpenGLDrawer::glDrawScene(void)  
 {  
 //Wireframe Mode  
     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);  
     glBegin(GL_QUADS);  
 //Top side  
     glVertex3f( 1.0f, 1.0f, 1.0f );  
     glVertex3f( 1.0f, 1.0f, -1.0f );  
     glVertex3f( -1.0f, 1.0f, -1.0f );  
     glVertex3f( -1.0f, 1.0f, 1.0f );  
 //Bottom side  
     glVertex3f( -1.0f, -1.0f, -1.0f );  
     glVertex3f( 1.0f, -1.0f, -1.0f );  
     glVertex3f( 1.0f, -1.0f, 1.0f );  
     glVertex3f( -1.0f, -1.0f, 1.0f );  
 //Front side  
     glVertex3f( 1.0f, 1.0f, 1.0f );  
     glVertex3f( -1.0f, 1.0f, 1.0f );  
     glVertex3f( -1.0f, -1.0f, 1.0f );  
     glVertex3f( 1.0f, -1.0f, 1.0f );  
 //Back side  
     glVertex3f( -1.0f, -1.0f, -1.0f );  
     glVertex3f( -1.0f, 1.0f, -1.0f );  
     glVertex3f( 1.0f, 1.0f, -1.0f );  
     glVertex3f( 1.0f, -1.0f, -1.0f );  
 //Left side  
     glVertex3f( -1.0f, -1.0f, -1.0f );  
     glVertex3f( -1.0f, -1.0f, 1.0f );  
     glVertex3f( -1.0f, 1.0f, 1.0f );  
     glVertex3f( -1.0f, 1.0f, -1.0f );  
 //Right side  
     glVertex3f( 1.0f, 1.0f, 1.0f );  
     glVertex3f( 1.0f, -1.0f, 1.0f );  
     glVertex3f( 1.0f, -1.0f, -1.0f );  
     glVertex3f( 1.0f, 1.0f, -1.0f );  
     glEnd();  
 }  

10.如有需控制物體的選轉、移動,則需改寫OnMouseMove()(WM_MOUSEMOVE)。
 void COpenGLDrawer::OnMouseMove(UINT nFlags, CPoint point)  
 {  
     // TODO: Add your message handler code here and/or call default  
     int diffX = (int)(point.x - this->m_fLastX);  
     int diffY = (int)(point.y - this->m_fLastY);  
     this->m_fLastX = (float)point.x;  
     this->m_fLastY = (float)point.y;  
 //Left mouse button  
     if ( nFlags & MK_LBUTTON )  
     {  
         this->m_fRotX += (float)0.5f * diffY;  
         if ( (this->m_fRotX > 360.0f) || (this->m_fRotX < -360.0f) )    {  
             this->m_fRotX = 0.f;    };  
         this->m_fRotY += (float)0.5f * diffX;  
         if ( (this->m_fRotY > 360.f) || (this->m_fRotY < -360.f) ) {  
             this->m_fRotY = 0.f; };  
 //Right mouse button  
     }else if ( nFlags & MK_RBUTTON ){  
         this->m_fZoom -= (float)0.1f * diffY;  
 //Middle mouse button  
     }else if ( nFlags & MK_MBUTTON ){  
         this->m_fPosX += (float)0.05f * diffX;  
         this->m_fPosY -= (float)0.05f * diffY;  
     }    //End of if ( nFlags & MK_LBUTTON )  
     this->OnDraw(NULL);  
     CWnd::OnMouseMove(nFlags, point);  
 }  

如果希望可以透過滑鼠滾輪來縮放大小,則一樣要改寫OnMouseWheel()(WM_MOUSEWHEEL)。但目前測試結果為WM_MOUSEWHEEL會先被Dialog所接收,所以這邊改寫的OnMouseWheel()並不會被message queue呼叫。
因此,也一併改寫Dialog的OnMouseWheel(),當收到message時,則呼叫COpenGLDrawer的OnMouseWheel()來達到縮放大小之結果。
COpenGLDrawer.cpp
 BOOL COpenGLDrawer::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)  
 {  
     // TODO: Add your message handler code here and/or call default  
     int diffX = (int)(pt.x - this->m_fLastX);  
     int diffY = (int)(pt.y - this->m_fLastY);  
     this->m_fLastX = (float)pt.x;  
     this->m_fLastY = (float)pt.y;  
     if ( zDelta == WHEEL_DELTA )                    //Rolls to forward - Zoom in  
     {  
         this->m_fZoom -= (float)0.05f * diffY;  
     }else if (zDelta == -WHEEL_DELTA) {        //Rolls to back - Zoom out  
         this->m_fZoom += (float)0.05f * diffY;  
     }    //End of if ( zDelta > 0 )   
     this->OnDraw(NULL);  
     return CWnd::OnMouseWheel(nFlags, zDelta, pt);  
 }  

CMainDlg.cpp
 BOOL CMainDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)  
 {  
     // TODO: Add your message handler code here and/or call default  
     this->m_glWindow.OnMouseWheel(nFlags, zDelta, pt);  
     return CDialog::OnMouseWheel(nFlags, zDelta, pt);  
 }  

不使用時,也是要釋放記憶體。
 COpenGLDrawer::~COpenGLDrawer(void)  
 {  
     wglMakeCurrent(NULL, NULL);  
     wglDeleteContext(this->m_hrc);
     DeleteDC(this->m_hdc);
 }  

到上述步驟完成後,OpenGL繪圖用的Class就已經完成了。接下來就要到主要的Dialog來使用此class。

11.建立OpenGL物件,以及設定參數。
Header:
 protected:  
     COpenGLDrawer m_glWindow;  

CPP:
 BOOL CMainDlg::OnInitDialog()  
 {  
     CDialog::OnInitDialog();  
     // Add "About..." menu item to system menu.  
     // IDM_ABOUTBOX must be in the system command range.  
     ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);  
     ASSERT(IDM_ABOUTBOX < 0xF000);  
     CMenu* pSysMenu = GetSystemMenu(FALSE);  
     if (pSysMenu != NULL)  
     {  
         CString strAboutMenu;  
         strAboutMenu.LoadString(IDS_ABOUTBOX);  
         if (!strAboutMenu.IsEmpty())  
         {  
             pSysMenu->AppendMenu(MF_SEPARATOR);  
             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);  
         }  
     }  
     // Set the icon for this dialog. The framework does this automatically  
     // when the application's main window is not a dialog  
     SetIcon(m_hIcon, TRUE);            // Set big icon  
     SetIcon(m_hIcon, FALSE);        // Set small icon  

     // TODO: Add extra initialization here  
     CRect rect;  
 //Get size and position of the picture control  
     GetDlgItem(IDC_PIC_DRAWDC)->GetWindowRect(rect);  
 //Convert screen coordinates to client coordinate  
     ScreenToClient(rect);  
 //Create OpenGL Control window  
     this->m_glWindow.glCreate(rect, this);  
 //Setup the OpenGL Window's timer to render  
     this->m_glWindow.m_unpTimer = this->m_glWindow.SetTimer(1, 1, 0);  
     return TRUE; // return TRUE unless you set the focus to a control  
 }  

上述完成後,就可以在Picture Control裡繪製圖形了,至於效能部分,就待之後之測試再進行說明。

PS.如有要將FormView當做繪圖區,則只要在進行ScreenToClient()時,塞入FormView的rect即可。

Comments

Popular Posts