当前位置: 首页 > 工具软件 > GLView > 使用案例 >

定制cocos2d的GLView

韦绍晖
2023-12-01

使用场景

需求:将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_, 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一样可以用

另外,推荐一下一个wgl + OpenGL的例子:http://www.songho.ca/opengl/gl_mvc.html
 类似资料: