Este snippet muestra una clase de ventana completa, incluyendo el problemático wndProc, que es es algo complicado incluir en la clase debido a que se tiene que pasar como parametro lpfnWndProc al WNDCLASS. Esto es debido a que no se puede pasar un puntero a una función miembro de una clase a menos de que sea estática o que el puntero vaya a ser usado en el contexto de un objeto de una clase, que sigue sin arreglar el problema. La solución que me ha parecido mas viable es la que se muestra en este hilo de gamedev.net, en la que se usa una función miembro estática que sirve de dummy para la verdadera función interna de la clase, comunicandose entre ambas vias el area de datos de usuario HWND de 32-bit, por donde se envía una referencia del objeto CVentana.
Para ver mas detalladamente el problema en la página The Function Pointer Tutorials esta que son y como usar los punteros a funciones en c++, este es un artículo de msdn como crear un puntero a función miembro y en linuxquality hay otro.
ventana.h
-
#ifndef _CVENTANA_C_
-
#define _CVENTANA_C_
-
-
#include <windows.h>
-
#include <gl\gl.h>
-
#include <gl\glu.h>
-
#include <gl\glaux.h>
-
-
-
-
#define INACTIVO 0 //Quiere decir que el objeto CVentana no tiene una ventana asociada, esta "vacio"
-
#define ACTIVO 1 //El objeto tiene una ventana funcional asociada
-
#define ESTADO_ERROR 2 //Durante la creacion o destrucccion del objeto se presentó un error
-
-
class CVentana
-
{
-
protected:
-
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
-
LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
-
private:
-
int estado;
-
public:
-
WNDCLASS wnd;
-
HWND hwnd;
-
MSG msg;
-
HINSTANCE hinstance;
-
HDC hdc;
-
HGLRC hrc;
-
bool fullscreen;
-
int alto;
-
int ancho;
-
-
//Constructor, inicializa el estado del objeto
-
CVentana() {
-
estado=INACTIVO;
-
};
-
-
int Crear_Ventana(int _ancho,int _alto,int pos_x,int pos_y,bool _fullscreen,int profundidad_color);
-
-
//Cambia el tamaño de la ventna de gl
-
void Cambiar_Tamano_GL(GLsizei ancho, GLsizei alto);
-
-
//Inicializa gl, se usa en la creacion de la ventana
-
bool Inicializar_GL(GLvoid);
-
-
//Funcion que libera los recursos de la ventana
-
void Destruir_Ventana(void) ;
-
-
//Ciclo de observacion de mensajes, el único mensaje especial que toma es WM_QUIT,de encontrarlo retorna
-
//true, si no, manda el mensaje al winproc()
-
bool Ciclo_Mensaje(void);
-
-
//Acceso al estado del objeto
-
int Obtener_Estado(void){return estado;};
-
-
//Limpia el objeto en caso de ESTADO_ERROR,y lo devuelve a INACTIVO
-
//OJO, no es segura la liberacion de memoria
-
void Limpiar_Objeto(void);
-
-
};
-
-
#endif
-
-
/*El codigo típico para abrir una ventana y que corra, etc es algo como
-
#pragma comment(lib, "opengl32.lib")
-
#pragma comment(lib, "glu32.lib")
-
#pragma comment(lib, "glaux.lib")
-
int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
-
{
-
CVentana myVentana;
-
if(myVentana.Crear_Ventana(640,480,200,200,false,32) == ESTADO_ERROR)
-
{
-
MessageBox(NULL,"Error en la creacion de la ventana","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
}
-
else
-
{
-
glClearColor(0.5f,0.5f,0.5f,0.5f);
-
while(myVentana.Ciclo_Mensaje()==false)
-
{
-
glLoadIdentity();
-
gluLookAt(0.0f,0.0f,19.0f, 0.0f,0.0f,0.0f, 0.0f,1.0f,0.0f);
-
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
SwapBuffers(myVentana.hdc);
-
}
-
}
-
}
-
*/
ventana.cpp
-
#include "ventana.h"
-
-
-
-
LRESULT CALLBACK CVentana::StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
-
{
-
CVentana* pParent;
-
-
// Get pointer to window
-
if(uMsg == WM_CREATE)
-
{
-
pParent = (CVentana*)((LPCREATESTRUCT)lParam)->lpCreateParams;
-
SetWindowLongPtr(hWnd,GWL_USERDATA,(LONG_PTR)pParent);
-
}
-
else
-
{
-
pParent = (CVentana*)GetWindowLongPtr(hWnd,GWL_USERDATA);
-
if(!pParent) return DefWindowProc(hWnd,uMsg,wParam,lParam);
-
}
-
-
pParent->hwnd = hWnd;
-
return pParent->WndProc(uMsg,wParam,lParam);
-
}
-
-
LRESULT CVentana::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
-
{
-
switch (uMsg) // Evalua los mensajes entrantes
-
{
-
case WM_SYSCOMMAND: // Intercepta comandos del sistema
-
{
-
switch (wParam) // Verifica el mensaje
-
{
-
case SC_SCREENSAVE: // screensaver intentando aparecer
-
case SC_MONITORPOWER: // Monitor entrando a modo bajo consumo energia
-
return 0; // evitar que ocurra
-
}
-
break; // salir
-
}
-
-
case WM_CLOSE: // Un mensaje de salida
-
{
-
PostQuitMessage(0); // Enviando un mensaje de salida
-
// destruir_ventana();
-
return 0;
-
}
-
}
-
-
return DefWindowProc(hwnd,uMsg,wParam,lParam); //Interprete por defecto de windows;
-
}
-
-
int CVentana::Crear_Ventana(int _ancho,int _alto,int pos_x,int pos_y,bool _fullscreen,int profundidad_color)
-
{
-
if(estado == INACTIVO)
-
{
-
DWORD dwExStyle;
-
DWORD dwStyle;
-
RECT window_rect;
-
GLuint PixelFormat;
-
-
fullscreen=_fullscreen;
-
ancho=_ancho; //Se copia el alto y el ancho para mantenerlo como variables globales
-
alto=_alto; //alto
-
-
window_rect.left=(long)0;
-
window_rect.right=(long)ancho; //Se inicializa el rect_angulo de la ventana
-
window_rect.top=(long)0;
-
window_rect.bottom=(long)alto;
-
-
-
hinstance = GetModuleHandle(NULL);
-
-
wnd.style =CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
-
wnd.lpfnWndProc = (WNDPROC) StaticWndProc; //El principal reto de la clase, incluir el wndProc dentro de la clase
-
wnd.cbClsExtra =0;
-
wnd.cbWndExtra =0;
-
wnd.hInstance =hinstance;
-
wnd.hIcon =LoadIcon(NULL,IDI_WINLOGO);
-
wnd.hCursor =LoadCursor(NULL,IDC_ARROW);
-
wnd.hbrBackground =NULL;
-
wnd.lpszMenuName =NULL;
-
wnd.lpszClassName ="pick";
-
-
if(!RegisterClass(&wnd)) //Se registra la clase y se verifican ESTADO_ERRORes
-
{
-
MessageBox(NULL,"Falla al registrar la clase de la ventana.","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
-
if(fullscreen) //se verifica el parametro fullscreen y se ajustan los parametros
-
{
-
DEVMODE dmScreenSettings;
-
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
-
dmScreenSettings.dmSize=sizeof(dmScreenSettings);
-
dmScreenSettings.dmPelsWidth = ancho;
-
dmScreenSettings.dmPelsHeight = alto;
-
dmScreenSettings.dmBitsPerPel = profundidad_color;
-
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
-
-
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)//se pide el cambio a fullscreen y se verifica ESTADO_ERRORes
-
{
-
if (MessageBox(NULL,"El modo No es soportado por la tarjeta | opengl, pasar a modo window?","Ventana gl",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
-
{
-
fullscreen=FALSE;
-
}
-
else
-
{
-
MessageBox(NULL,"El programa se cerrará.","ESTADO_ERROR",MB_OK|MB_ICONSTOP); //no se pudo cambiar el modo grafico a pantalla completa
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
}
-
}
-
if(fullscreen)//si el parametro fullscreen sigue siendo true
-
{
-
dwExStyle=WS_EX_APPWINDOW;
-
dwStyle=WS_POPUP;
-
pos_x=0;
-
pos_y=0;
-
ShowCursor(FALSE);
-
}
-
else // si no se solicita una ventana en modo normal
-
{
-
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
-
dwStyle=WS_OVERLAPPEDWINDOW;
-
}
-
AdjustWindowRectEx(&window_rect, dwStyle, FALSE, dwExStyle); //se ajusta el tamaño y estilo del borde
-
-
if (!(hwnd=CreateWindowEx( dwExStyle, //Funcion de creacion de la ventana con verificacion de ESTADO_ERROR
-
"pick",
-
"pick",
-
dwStyle |
-
WS_CLIPSIBLINGS |
-
WS_CLIPCHILDREN,
-
pos_x, pos_y,
-
window_rect.right-window_rect.left,
-
window_rect.bottom-window_rect.top,
-
NULL,
-
NULL,
-
hinstance,
-
this))) //Este NULL cambiarlo por this AHORA
-
{
-
Destruir_Ventana(); //Si fallo se destruye la ventana y se muestra mensaje de ESTADO_ERROR
-
MessageBox(NULL,"ESTADO_ERROR de creacion de la ventana.","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
-
static PIXELFORMATDESCRIPTOR pfd= //se eligen los parametros del pixel format descriptor
-
{
-
sizeof(PIXELFORMATDESCRIPTOR),
-
1,
-
PFD_DRAW_TO_WINDOW |
-
PFD_SUPPORT_OPENGL | //creacion de una ventana doble buffer, rgba, con el
-
PFD_DOUBLEBUFFER, //parametro profundidad_color que recibe la funcion
-
PFD_TYPE_RGBA, //se solicita compatible con gl,etc,etc
-
profundidad_color,
-
0, 0, 0, 0, 0, 0,
-
0,
-
0,
-
0,
-
0, 0, 0, 0,
-
16, //profundidad del buffer stencil
-
0,
-
0,
-
PFD_MAIN_PLANE,
-
0,
-
0, 0, 0
-
};
-
-
if (!(hdc=GetDC(hwnd))) //se solicita un dc para la ventana
-
{
-
this->Destruir_Ventana(); //si falla mensaje de ESTADO_ERROR y destruye la ventana
-
MessageBox(NULL,"No se pudo crear un contexto gl.","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
-
if (!(PixelFormat=ChoosePixelFormat(hdc,&pfd))) //se solicitan los parametros para el dc con el pdf
-
{
-
this->Destruir_Ventana(); //si falla mensaje de ESTADO_ERROR y destruye la ventana
-
MessageBox(NULL,"No se pudo encontrar el formato de pixeles.","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
-
if(!SetPixelFormat(hdc,PixelFormat,&pfd)) //se establecen los parametros para el dc
-
{
-
this->Destruir_Ventana(); //si falla mensaje de ESTADO_ERROR y destruye la ventana
-
MessageBox(NULL,"No se pudo establecer el formato de pixeles.","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
-
if (!(hrc=wglCreateContext(hdc))) //creacion del contexto de Opengl contenido en el hglrc hrc
-
{
-
this->Destruir_Ventana(); //si falla mensaje de ESTADO_ERROR y destruye la ventana
-
MessageBox(NULL,"No se pudo establecer un contexto gl.","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
-
if(!wglMakeCurrent(hdc,hrc)) //Se trata de activar el contexto gl hrc
-
{
-
this->Destruir_Ventana(); //si falla mensaje de ESTADO_ERROR y destruye la ventana
-
MessageBox(NULL,"No se pudo activar el contexto gl.","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
-
ShowWindow(hwnd,SW_SHOW); //se muestra la ventana ya creada
-
SetForegroundWindow(hwnd);
-
SetFocus(hwnd); //se pide foco del usuario sobre la ventana
-
-
estado=ACTIVO; // Se concluyo exitosamente la creacion de la ventana
-
-
Cambiar_Tamano_GL(ancho,alto); //se ajusta el hrc de gl a la ventana
-
-
if (!Inicializar_GL()) // Inicializacion de opengl
-
{
-
this->Destruir_Ventana(); //si falla mensaje de ESTADO_ERROR y destruye la ventana
-
MessageBox(NULL,"Inicializacion fallida.","ESTADO_ERROR",MB_OK|MB_ICONEXCLAMATION);
-
estado=ESTADO_ERROR;
-
return estado;
-
}
-
}
-
return estado;
-
-
}
-
-
-
-
void CVentana::Cambiar_Tamano_GL(GLsizei ancho, GLsizei alto) //funcion que cambia el viewport de gl segun ancho,alto
-
{
-
if(estado == ACTIVO)
-
{
-
if (alto==0)
-
{
-
alto=1;
-
}
-
glViewport(0,0,ancho,alto);
-
glMatrixMode(GL_PROJECTION);
-
glLoadIdentity();
-
gluPerspective(45.0f,(GLfloat)ancho/(GLfloat)alto,0.1f,100.0f);
-
glMatrixMode(GL_MODELVIEW);
-
glLoadIdentity();
-
}
-
}
-
-
bool CVentana::Inicializar_GL(GLvoid) //Funcion de inicializacion de Opengl interna de la libreria, para propositos mas especificos, cambiar los parametros por fuera de esta funcion
-
{
-
-
glShadeModel(GL_SMOOTH);
-
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
-
glClearDepth(1.0f);
-
glEnable(GL_DEPTH_TEST);
-
glDepthFunc(GL_LEQUAL);
-
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
-
return TRUE;
-
-
}
-
-
-
-
//Funcion de Destruccion de ventana y liberacion de recursos
-
void CVentana::Destruir_Ventana(void)
-
{
-
if(estado== ACTIVO)
-
{
-
if (fullscreen) // Esta la ventana en modo fullscreen
-
{
-
ChangeDisplaySettings(NULL,0); // Se regresa al modo normal de Windows
-
ShowCursor(TRUE); // Se muestra el puntero del mouse
-
}
-
-
if (hrc) // Si se establecio un contexto de render
-
{
-
if (!wglMakeCurrent(NULL,NULL)) // Se hace "actual"
-
{
-
MessageBox(NULL,"Liberacion de rc y dc fallo.","SHUTDOWN ESTADO_ERROR",MB_OK | MB_ICONINFORMATION);
-
estado=ESTADO_ERROR;
-
}
-