Gast
2007-02-17, 22:48:39
Hi Leute,
wir hatten ja vor einer Weile schonmal das Thema OpenGL unter .NET. Das Problem war ja, daß OpenGL unter Windows unmanaged Datentypen wie HWND, HDC und HGLRC verwendet, die mit managed Datentypen nicht so ganz kompatibel sind. Es gibt zwar bereits Bibliotheken wie CsGL oder Tao, die diese Problematik intern irgendwie regeln, aber ich wollte einfach mal herausfinden, ob man das auch selbst hinbekommen kann. Hier nun das positive Resultat :)
Die .NET System.Windows.Forms.Form-Klasse hat einen Member Handle, der offenbar dem Win32er Window-Handle (HWND) entspricht. Dieser Handle hat den Datentyp System.IntPtr, und die nun vielleicht naheliegende Idee, in gemischtem C++ Code einfach von IntPtr nach HWND zu konvertieren, funktioniert zwar nicht, wohl aber ist es möglich, von IntPtr nach int und von dort nach HWND zu konvertieren. Man kann z.B. in einer managed C++ Klasse, die ein OpenGL-Fenster kapseln soll und von Form abgeleitet ist, folgermaßen vorgehen:
#include <windows.h>
#include <gl.gl.h>
class OpenGLForm : public System::Windows::Forms::Form
{
public:
OpenGLForm()
{
// Konvertierung von IntPtr -> int
this->windowHandle = (int) this->Handle;
// und weiter nach HWND
HWND hWnd = (HWND) this->windowHandle;
// OpenGL-Kontext unmanaged erzeugen
HDC hDC = GetDC(hWnd);
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory (&pfd, sizeof (pfd));
pfd.nSize = sizeof (pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = colorBits;
pfd.cDepthBits = depthBits;
pfd.iLayerType = PFD_MAIN_PLANE;
GLuint PixelFormat = ChoosePixelFormat(hDC, &pfd);
SetPixelFormat(hDC,PixelFormat,&pfd);
HGLRC hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
// unmanaged Handles als int-Member speichern
this->deviceContext = (int) hDC;
this->renderingContext = (int) hRC;
}
void Dispose(Boolean disposing)
{
if (disposing && components)
{
components->Dispose();
}
__super::Dispose(disposing);
// GL Rendering Kontext freigeben
wglMakeCurrent(0, 0);
if (this->renderingContext != 0)
wglDeleteContext((HGLRC) this->renderingContext);
if (this->windowHandle != 0 && this->deviceContext != 0)
ReleaseDC((HWND) this->windowHandle, (HDC) this->deviceContext);
}
private:
int windowHandle, deviceContext, renderingContext;
};
Nachdem man also einen HWND gewinnen konnte, erzeugt man wie in unmanaged Code unter Verwendung der Datentypen HDC, HGLRC und PIXELFORMATDESCRIPTOR einen OpenGL Rendering Context. Der HDC und HGLRC werden später noch benötigt, z.B. für SwapBuffer() oder zum Freigeben des Kontexts am Programmende. Da sie unmanaged sind, können sie nicht als Member der Klasse gespeichert werden, dafür aber können sie in ints konvertiert und als solche gespeichert werden. Aus diesen können sie bei Bedarf durch Rückkonvertierung wiedergewonnen werden.
Die erzeuge OpenGLForm-Klasse ist zwar in C++ geschrieben, kann aber in allen anderen .NET-Sprachen (z.B. C#) verwendet werden. Alles was man jetzt noch braucht ist ein Wrapper, der die unmanaged OpenGL-Funktionen in managed Funktionen wrappt. Dies kann in der OpenGLForm-Klasse geschehen oder auch in einer seperaten Wrapper-Klasse.
wir hatten ja vor einer Weile schonmal das Thema OpenGL unter .NET. Das Problem war ja, daß OpenGL unter Windows unmanaged Datentypen wie HWND, HDC und HGLRC verwendet, die mit managed Datentypen nicht so ganz kompatibel sind. Es gibt zwar bereits Bibliotheken wie CsGL oder Tao, die diese Problematik intern irgendwie regeln, aber ich wollte einfach mal herausfinden, ob man das auch selbst hinbekommen kann. Hier nun das positive Resultat :)
Die .NET System.Windows.Forms.Form-Klasse hat einen Member Handle, der offenbar dem Win32er Window-Handle (HWND) entspricht. Dieser Handle hat den Datentyp System.IntPtr, und die nun vielleicht naheliegende Idee, in gemischtem C++ Code einfach von IntPtr nach HWND zu konvertieren, funktioniert zwar nicht, wohl aber ist es möglich, von IntPtr nach int und von dort nach HWND zu konvertieren. Man kann z.B. in einer managed C++ Klasse, die ein OpenGL-Fenster kapseln soll und von Form abgeleitet ist, folgermaßen vorgehen:
#include <windows.h>
#include <gl.gl.h>
class OpenGLForm : public System::Windows::Forms::Form
{
public:
OpenGLForm()
{
// Konvertierung von IntPtr -> int
this->windowHandle = (int) this->Handle;
// und weiter nach HWND
HWND hWnd = (HWND) this->windowHandle;
// OpenGL-Kontext unmanaged erzeugen
HDC hDC = GetDC(hWnd);
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory (&pfd, sizeof (pfd));
pfd.nSize = sizeof (pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = colorBits;
pfd.cDepthBits = depthBits;
pfd.iLayerType = PFD_MAIN_PLANE;
GLuint PixelFormat = ChoosePixelFormat(hDC, &pfd);
SetPixelFormat(hDC,PixelFormat,&pfd);
HGLRC hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
// unmanaged Handles als int-Member speichern
this->deviceContext = (int) hDC;
this->renderingContext = (int) hRC;
}
void Dispose(Boolean disposing)
{
if (disposing && components)
{
components->Dispose();
}
__super::Dispose(disposing);
// GL Rendering Kontext freigeben
wglMakeCurrent(0, 0);
if (this->renderingContext != 0)
wglDeleteContext((HGLRC) this->renderingContext);
if (this->windowHandle != 0 && this->deviceContext != 0)
ReleaseDC((HWND) this->windowHandle, (HDC) this->deviceContext);
}
private:
int windowHandle, deviceContext, renderingContext;
};
Nachdem man also einen HWND gewinnen konnte, erzeugt man wie in unmanaged Code unter Verwendung der Datentypen HDC, HGLRC und PIXELFORMATDESCRIPTOR einen OpenGL Rendering Context. Der HDC und HGLRC werden später noch benötigt, z.B. für SwapBuffer() oder zum Freigeben des Kontexts am Programmende. Da sie unmanaged sind, können sie nicht als Member der Klasse gespeichert werden, dafür aber können sie in ints konvertiert und als solche gespeichert werden. Aus diesen können sie bei Bedarf durch Rückkonvertierung wiedergewonnen werden.
Die erzeuge OpenGLForm-Klasse ist zwar in C++ geschrieben, kann aber in allen anderen .NET-Sprachen (z.B. C#) verwendet werden. Alles was man jetzt noch braucht ist ein Wrapper, der die unmanaged OpenGL-Funktionen in managed Funktionen wrappt. Dies kann in der OpenGLForm-Klasse geschehen oder auch in einer seperaten Wrapper-Klasse.