使用场景
需求:将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)
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_, sceneHeight_, 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 colorBits, int depthBits, 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, sizeof(pfd), &pfd);
// set the fixel format
if (!::SetPixelFormat(hdc, pixelFormat, &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 colorBits, int depthBits, int stencilBits)
{
// retrieve a handle to a display device context
hwnd_ = hwnd;
hdc_ = ::GetDC(hwnd);
// set pixel format
if (!setPixelFormat(hdc_, colorBits, depthBits, 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一样可以用