使用场景
需求:将cocos2d 的输出窗口设置为一个子窗口,父窗口其它部分仍然有控件
cocos2d功能强大,但是却不好整合进各种gdi和MFC程序中,有些实现是改cocos2d的项目源代码,这样的话,后续升级版本麻烦些,本文提供的方法可以不修改cocos2d源码
实现步骤
1.从cocos2d::GLView派生类WGLView,需要实现 end,swapBuffers,isOpenGLReady,setIMEKeyboardState,windowShouldClose,getWin32Window 这几个虚函数
2.windows 下的 OpenGL初始化流程有这么几项:
(1).外部提供一个HWND,需要窗口类注册时具有 CS_OWNDC标志
(2).获取HWND的HDC,使用SetPixelFormat来调整窗口像素格式,选择的格式需要具有PFD_SUPPORT_OPENGL,PFD_DRAW_TO_WINDOW,PFD_TYPE_RGBA,PFD_DOUBLEBUFFER这几个标志
(3).使用wglCreateContext创建OpenGL环境上下文
//如果准备在其它线程内使用OpenGL,下面转入该线程执行
(4).wglMakeCurrent使用创建好的上下文
(5).初始化glew (必须在 wglMakeCurrent调用后执行,否则glewInit() 会返回错误 1)
(6).Director::setOpenGLView 设置 GLView(必须在glew初始化后执行,否则内部会报空指针错误,因为对应的glew函数指针尚未初始化)
(7).一些后续的初始化
代码(适用于cocos2d 3.13)
Application
static bool glew_dynamic_binding() { const char *gl_extensions = (const char*)glGetString(GL_EXTENSIONS); // If the current opengl driver doesn't have framebuffers methods,check if an extension exists if (glGenFramebuffers == nullptr) { log("OpenGL: glGenFramebuffers is nullptr,try to detect an extension"); if (strstr(gl_extensions,"ARB_framebuffer_object")) { log("OpenGL: ARB_framebuffer_object is supported"); glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)wglGetProcAddress("glIsRenderbuffer"); glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)wglGetProcAddress("glBindRenderbuffer"); glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)wglGetProcAddress("glDeleteRenderbuffers"); glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)wglGetProcAddress("glGenRenderbuffers"); glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)wglGetProcAddress("glRenderbufferStorage"); glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)wglGetProcAddress("glGetRenderbufferParameteriv"); glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)wglGetProcAddress("glIsFramebuffer"); glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"); glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffers"); glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers"); glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)wglGetProcAddress("glCheckFramebufferStatus"); glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)wglGetProcAddress("glFramebufferTexture1D"); glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D"); glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)wglGetProcAddress("glFramebufferTexture3D"); glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbuffer"); glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)wglGetProcAddress("glGetFramebufferAttachmentParameteriv"); glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)wglGetProcAddress("glGenerateMipmap"); } else if (strstr(gl_extensions,"EXT_framebuffer_object")) { log("OpenGL: EXT_framebuffer_object is supported"); glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)wglGetProcAddress("glIsRenderbufferEXT"); glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)wglGetProcAddress("glBindRenderbufferEXT"); glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)wglGetProcAddress("glDeleteRenderbuffersEXT"); glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)wglGetProcAddress("glGenRenderbuffersEXT"); glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)wglGetProcAddress("glRenderbufferStorageEXT"); glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)wglGetProcAddress("glGetRenderbufferParameterivEXT"); glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)wglGetProcAddress("glIsFramebufferEXT"); glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebufferEXT"); glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffersEXT"); glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffersEXT"); glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)wglGetProcAddress("glCheckFramebufferStatusEXT"); glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)wglGetProcAddress("glFramebufferTexture1DEXT"); glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2DEXT"); glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)wglGetProcAddress("glFramebufferTexture3DEXT"); glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbufferEXT"); glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)wglGetProcAddress("glGetFramebufferAttachmentParameterivEXT"); glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)wglGetProcAddress("glGenerateMipmapEXT"); } else { log("OpenGL: No framebuffers extension is supported"); log("OpenGL: Any call to Fbo will crash!"); return false; } } return true; } static bool initGlew() { GLenum GlewInitResult = glewInit(); if (GLEW_OK != GlewInitResult) return false; //must be called after calling wglMakeCurrent if (glew_dynamic_binding() == false) return false; return true; } class AppDelegate : public cocos2d::Application { float sceneWidth_,sceneHeight_,frameRate_; HWND hwnd_; cocos2d::GLView *glview_; public: AppDelegate(float sceneWidth,float sceneHeight,float frameRate,HWND hwndRender) : sceneWidth_(sceneWidth),sceneHeight_(sceneHeight),frameRate_(frameRate),hwnd_(hwndRender),glview_(nullptr) { extern cocos2d::GLView* CreateWGLView(HWND); glview_ = CreateWGLView(hwnd_); } virtual bool applicationDidFinishLaunching() { // execute in engine thread glview_->setFrameSize(sceneWidth_,sceneHeight_); glview_->setDesignResolutionSize(sceneWidth_,ResolutionPolicy::NO_BORDER); extern void ActiveWGLView(cocos2d::GLView *); ActiveWGLView(glview_); initGlew(); auto director = Director::getInstance(); director->setOpenGLView(glview_); director->setContentScaleFactor(1.0); director->setDisplayStats(false); director->setAnimationInterval(1.0 / frameRate_); return true; } virtual void applicationDidEnterBackground() { Director::getInstance()->stopAnimation(); } virtual void applicationWillEnterForeground() { Director::getInstance()->startAnimation(); } };GLView
static int findPixelFormat(HDC hdc,int colorBits,int depthBits,int stencilBits) { int currMode; // pixel format mode ID int bestMode = 0; // return value,best pixel format int currscore = 0; // points of current mode int bestscore = 0; // points of best candidate PIXELFORMATDESCRIPTOR pfd; // search the available formats for the best mode bestMode = 0; bestscore = 0; for (currMode = 1; ::DescribePixelFormat(hdc,currMode,sizeof(pfd),&pfd) > 0; ++currMode) { // ignore if cannot support opengl if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL)) continue; // ignore if cannot render into a window if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW)) continue; // ignore if cannot support rgba mode if ((pfd.iPixelType != PFD_TYPE_RGBA) || (pfd.dwFlags & PFD_NEED_PALETTE)) continue; // ignore if not double buffer if (!(pfd.dwFlags & PFD_DOUBLEBUFFER)) continue; // try to find best candidate currscore = 0; // colour bits if (pfd.cColorBits >= colorBits) ++currscore; if (pfd.cColorBits == colorBits) ++currscore; // depth bits if (pfd.cDepthBits >= depthBits) ++currscore; if (pfd.cDepthBits == depthBits) ++currscore; // stencil bits if (pfd.cStencilBits >= stencilBits) ++currscore; if (pfd.cStencilBits == stencilBits) ++currscore; // alpha bits if (pfd.cAlphaBits > 0) ++currscore; // check if it is best mode so far if (currscore > bestscore) { bestscore = currscore; bestMode = currMode; } } return bestMode; } static bool setPixelFormat(HDC hdc,int stencilBits) { PIXELFORMATDESCRIPTOR pfd; // find out the best matched pixel format int pixelFormat = findPixelFormat(hdc,colorBits,depthBits,stencilBits); if (pixelFormat == 0) return false; // set members of PIXELFORMATDESCRIPTOR with given mode ID ::DescribePixelFormat(hdc,pixelFormat,&pfd); // set the fixel format if (!::SetPixelFormat(hdc,&pfd)) return false; return true; } class WGLView : public cocos2d::GLView { HWND hwnd_; HDC hdc_; HGLRC hglrc_; public: WGLView() : hwnd_(0),hdc_(0),hglrc_(0) {} virtual void end() override { closeContext(); } virtual void swapBuffers() override { ::SwapBuffers(hdc_); } virtual bool isOpenGLReady() override { return hwnd_ != 0; } virtual void setIMEKeyboardState(bool open) override {} virtual bool windowShouldClose() override { return true; }; virtual HWND getWin32Window() override { return hwnd_; } bool createContext(HWND hwnd,int stencilBits) { // retrieve a handle to a display device context hwnd_ = hwnd; hdc_ = ::GetDC(hwnd); // set pixel format if (!setPixelFormat(hdc_,stencilBits)) { ::ReleaseDC(hwnd,hdc_); return false; } // create a new OpenGL rendering context hglrc_ = ::wglCreateContext(hdc_); ::ReleaseDC(hwnd,hdc_); return true; } void activeContext() { ::wglMakeCurrent(hdc_,hglrc_); } void closeContext() { if (!hdc_ || !hglrc_) return; // delete DC and RC ::wglMakeCurrent(0,0); ::wglDeleteContext(hglrc_); ::ReleaseDC(hwnd_,hdc_); hdc_ = 0; hglrc_ = 0; hwnd_ = 0; } }; void ActiveWGLView(cocos2d::GLView *view) { WGLView *v = dynamic_cast<WGLView *>(view); if (v) v->activeContext(); } cocos2d::GLView* CreateWGLView(HWND wndExist) { WGLView *view = new WGLView(); if (!view->createContext(wndExist,32,24,8)) { delete view; return nullptr; } return view; }函数 initGlew 从cocos2d-2.2.6拷贝的,3.13一样可以用
另外,推荐一下一个wgl + OpenGL的例子:http://www.songho.ca/opengl/gl_mvc.html