Archived

This topic is now archived and is closed to further replies.

MikeD

OpenGL Wrapper for OpenGL, can't user member function WndProc

Recommended Posts

Well I hope the entire Subject Header gets through on this one. I''m writing a wrapper for OpenGL (do I hear yawns at the back?) and I''ve come across one specific problem. When I try to register my window with the windows system I have to pass in the WndProc function for message handling. This function is in my OpenGL class and I can''t cast it to anything the Visual C++ SDK is interested in compiling. i.e. the error generally is error C2440: ''return'' : cannot convert from ''long (__stdcall COpenGL::*)(struct HWND__ *,unsigned int,unsigned int,long)'' to ''long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)'' i.e. it won''t accept a member function as the message handler. Is there a way around this or do I have to redesign? Cheers, Mike

Share this post


Link to post
Share on other sites
I am aware that I can get around this by making the WndProc function a static member, but that would involve abstracting the function to a higher level class that deals with an OpenGL window by passing the messages through, as my WndProc function currently touches member variables.

This raises one question I''m not quite sure about, is it possible to have multiple windowed OpenGL windows or does hardware acceleration just not work that way?

Forgive me, I''m an AI programmer and I know not what I do.

Mike

Share this post


Link to post
Share on other sites
MikeD is right, you can use a static member function for the callback. You then call a private member function on the correct window.

The trick then becomes figuring out which instance of the class belongs to this window. I've seen this done a number of ways...

One way is to use the SetWindowLong function to store a pointer to the object for each window. Then in your static member function, you get the HWND, and use GetWindowLong to retreive the pointer. Then you can call the correct private member like this...

pObject->PrivateWndProc().

The other cool way i've seen is to use an STL map or linked list to map HWNDs to object pointers. In your window creation function, store the HWND and this pointer in the map. Then you simply look up the HWND and retrieve the correct pointer.

Hope this works.

Chris Miller.

CosmicBee - Software With Buzz

[edited by - crizo on March 21, 2002 12:57:16 PM]

Share this post


Link to post
Share on other sites
My lead programmer at my last company (which fell 2 days ago) has an article in the pipeline for games programming gems 3 on an idea called autolists, where, using templates, a list is automatically stored with every instantiation of a class of the members of that class. So when you instantiate a window it would automatically have a list of the other windows as a static member. The static WndProc could access that list and, with each instantiation holding its own windows handle, you could use the static WndProc to traverse the list and find the suitable PrivateWndProc to call.

Either way, cheers crizo for a good direction to head in.

Mike

Share this post


Link to post
Share on other sites
I searched long and hard on how to use a non-static member function as my WindowProc as well, and I ended up going with what crizo was getting at.

Basically I pass the this pointer of the object that creates the window to my custom window creation function. In this function, right after CreateWindowEx I call SetProperty with the handle of the newly created window, a string for the property (e.g., "PrivateWindowProc") and the this pointer which was passed to the function.

My CWindow class has a static member function MainWindowProc (which is what I use in RegisterClass) which simply checks to see if any window that is sending it messages has a property called "PrivateWindowProc", and if so I do what crizo said:

return(pThis->PrivateWindowProc(hWnd,uMsg,wParam,lParam));

If the messaging window doesn''t have, or doesn''t yet have, a property for the this pointer then I just pass the message to DefWindowProc.

This way, the MainWindowProc passes all messages to my non-static member function, PrivateWindowProc, which can then access member data normally.

If you want to see exactly what I''m talking about let me know.

Care,
Chris Rasmus

Florida, USA
RTS Engine in Development
http://www.knology.net/~heaven
Jesus is LORD!

Share this post


Link to post
Share on other sites
You could have a static function that calls the WndProc function of the appropriate instance for you, using a static pointer to the current instance.

static OGLWrapperObj* OGLWrapperObj::currentObj;

static void OGLWrapperObj::CallFunc( void )
{
currentObj->WndProc();
}

Then you could simply make any instance the current instance...

void OGLWrapperObj::MakeCurrent( void )
{ currentObj = this; }

and call the function.



[edited by - smitty1276 on March 22, 2002 12:49:00 PM]

Share this post


Link to post
Share on other sites
Interesting, reading this thread has given me insight to pretty much the same problem I am experiencing.

I've been trying to create a "CWindowGL" class which would just create the actual Window and handle all the messages. However, I came across the WndProc problem. I've been trying to get my head around these replies, but it's difficult because I am quite new to Windows programming.

The one method of implementing a solution to the WndProc problem that intrests me is the one that Heaven posted. However, I am curious: What does the MainWndProc() function handle, and what does the PrivateWndProc() function handle? Can you maybe explain the relationship between these two in greater detail? Are you using MFC to make use of the SetProperty() func? Any help is appreciated, thanks!

PS:

Earlier today I was sort of able to get it working: I friended the WndProc function in my CWindowGL class, however my CPU usage jumped up to ~50% and nothing got drawn to the screen. I guess friending it isn't the optimal route... :D

[edited by - Wheaty on March 22, 2002 5:27:56 PM]

Share this post


Link to post
Share on other sites
The relationship between the static MainWndProc and the PrivateWndProc is as follows.
As you require a static (or non-member) function to pass into the WndProc pointer when you register the window, to handle messages, you have to have a MainWndProc to do the message handling. However you want each window to handle its own messages with its own member function that can access the member variables. So the MainWndProc must have access to all of the window instantiations so it can pass the message on to the correct window. So the MainWndProc handles passing the relavent message to the correct instantiation of the class of window.

If that makes sense.

Mike

Share this post


Link to post
Share on other sites
Ugh...

Well, I understand WHY this needs to be done, however I am confused over how to implement it. For example (not using GL stuff, just trying to get a regular window drawn -- I also chopped some member funcs out, just want to get this WndProc stuff straightend out):


        
class CWindowGL
{
protected:
HWND hWnd; // window handle

HDC hDC; // device context


private:
bool done; //on/off flag


public:
CWindowGL() { done = false; } // constructor


static LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
LRESULT PrivateWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
};


[edit: whoops, moved the PrivateWndProc() to public: ]

So what you're trying to say is that the MainWndProc() would determine WHICH object to use, and then call that object's PrivateWndProc function. So there would be no need to use a switch(message) {... } in that function, because its only purpose would be to determine which PrivateWndProc to call. I understand that -- I guess where I'm stuck is how to store the pointer data. I saw Heaven referring to a "SetProperty" function... I checked my MSDN documentation and it seems to be a MFC call.

[edited by - Wheaty on March 22, 2002 6:46:04 PM]

Share this post


Link to post
Share on other sites
Hmm, I decided to use smitty1276''s idea and it worked. Just one question though, is it normal for regular Windows app which paints "Hello World!" to the middle of a form to utilize 100% of the CPU?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I can see how this solves the problem of wrapping a window in a class, but what about deallocating the object? If the WndProc is inside the class the WM_QUIT message can''t call "delete this". You could have a static STL map of HWND''s->this'' and deallocate objects in the static WndProc, but how do you notify the remaining portion of your application? The part that called the object''s constructor? You could send an event or post a message, but this seems to be getting out of hand. Wrapping a window in an object ought to be easier than this.

Share this post


Link to post
Share on other sites
quote:
Original post by Wheaty
The one method of implementing a solution to the WndProc problem that intrests me is the one that Heaven posted. However, I am curious: What does the MainWndProc() function handle, and what does the PrivateWndProc() function handle? Can you maybe explain the relationship between these two in greater detail? Are you using MFC to make use of the SetProperty() func? Any help is appreciated, thanks!


Let me show you how I''ve got things set up, and then how I use it (WindowProc). Keep in mind that I''ve stripped a lot of stuff not important in the relating of the concept.

class CWindow
{
BOOL Register(WNDPROC);
BOOL Create(CGameApp *);
HWND Handle;
}

BOOL CWindow::Register(WNDPROC WndProc)
{
WNDCLASS wc;
...
wc.lpfnWndProc=(WNDPROC)WndProc;
...
}

BOOL CWindow::Create(CGameApp *pThis)
{
...
if (!(Handle=CreateWindowEx(blah)))
return(FALSE);
SetProp(Handle,"WINDOWPROC",(HANDLE)pThis);
...
}

class CWinApp:public CWindow,
public CKeyboard,
public CMouse
{
BOOL Ready;
MSG Msg;
static LRESULT CALLBACK MainWindowProc(HWND,UINT,WPARAM,LPARAM);
BOOL Startup(CGameApp *);
}

BOOL CWinApp::Startup(CGameApp *pThis)
{
...
if (!Register(MainWindowProc))
return(FALSE);

if (!Create(pThis))
return(FALSE);
...
}

static LRESULT CALLBACK CWinApp::MainWindowProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
CGameApp *pThis=(CGameApp *)GetProp(hWnd,"WINDOWPROC");

if (pThis)
return(pThis->WindowProc(hWnd,msg,wParam,lParam));

return(DefWindowProc(hWnd,msg,wParam,lParam));
}

class CGameApp:public CWinApp,
public COpenGL, // could be CDirectX if I wanted to implement D3D style rendering
public CMap,
public CObjects
{
CGameApp(void);
LRESULT CALLBACK WindowProc(HWND,UINT,WPARAM,LPARAM);
}

CGameApp::CGameApp(void)
{
...
if (!Startup(this))
Ready=FALSE;
else
Ready=TRUE;
...
}

LRESULT CALLBACK CGameApp::WindowProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch(msg)
{
case WM_...:
{
...
return(0);
}
...
}
return(DefWindowProc(hWnd,msg,wParam,lParam));
}

int WINAPI WinMain(blah)
{
CGameApp GameApp;

if (!GameApp.Ready)
return(0);

// do main loop here
return(GameApp.Msg.wParam);
}

That should spell it out quite nicely for you.

If you have any questions please ask!

Care,
Chris Rasmus

Florida, USA
RTS Engine in Development
http://www.knology.net/~heaven
Jesus is LORD!

Share this post


Link to post
Share on other sites
It worked!!

Now I don''t need that global callback func anymore!

Btw, one prob I encountered with the methodes described above
is that the window "looses" a couple (4 last time i checked) of msg''s sent to the window when it''s created.
That ment that WM_CREATE never reached my WindowProc() =(

Ie.

CLASSNAME::Create()
{
CreateWindowEx(.....);
// I added a breakpoint in the MainWindowProc func, and it
// breaked before the one on SetProp.
SetProp(....);
}

Share this post


Link to post
Share on other sites
What you are trying to do is to write your own MFC.

Now if you could just use the search, you''d get LOADS of threads that answered this very question, and presented elegant solutions.

The way you do it is this.

When you register class, allocate 4 extra bytes per-window. Set cbWndExtra to 4.

When you call CreateWindow, it takes lpParam. Set it to "this". In your static WndProc, handle WM_NCCREATE. WM_NCCREATE''s lParam is an LPCREATESTRUCT which contains lpCreateParams. You set lpCreateParams earlier to "this". Now call SetWindowLong(hwnd, GWL_USERDATA, lpCreateParams). Don''t return yet.

Now for all messages, including WM_NCCREATE, do the following.

CWindow *pWindow = (CWindow *) GetWindowLong(hwnd, GWL_USERDATA);
pWindow->PrivateWndProc(hwnd, msg, wParam, lParam);

Your PrivateWndProc will get all messages from the first to the last. You should add a check to make sure pWindow is not NULL here.

Finally, if you allocated CWindow pointer with new, in your PrivateWndProc handle WM_NCDESTROY and do a delete this; in it.

It would be nice to add answers to WNDCLASS and link error questions to FAQ.

Share this post


Link to post
Share on other sites
IndirectX, here's what I came up with from your description:


LRESULT CALLBACK CWindow::MainWndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg)
{
case WM_NCCREATE:
SetWindowLong(hwnd, GWL_USERDATA, lparam);
default:
CWindow *pWindow = (CWindow*)GetWindowLong(hwnd, GWL_USERDATA);
if (pWindow)
pWindow->PrivWndProc(hwnd, umsg, wparam, lparam);
break;
}

return DefWindowProc(hwnd, umsg, wparam, lparam);
}

LRESULT CALLBACK CWindow:: PrivWndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT paintStruct;
char string[] = "Hello World!";

switch (umsg)
{
case WM_CREATE:
return 0;
break;

case WM_CLOSE:
PostQuitMessage(0);
return 0;
break;

case WM_PAINT:
hDC = BeginPaint(hwnd, &paintStruct);
SetTextColor(hDC, COLORREF(0x00FF0000));
TextOut(hDC, 150, 150, string, sizeof(string)-1);
EndPaint(hwnd, &paintStruct);
return 0;
break;

case WM_NCDESTROY:
delete this;
return 0;
break;

default:
return 0;
break;
}

return DefWindowProc(hwnd, umsg, wparam, lparam);
}

Everything seems to work fine, however, when I quit the program, it still remains running in memory.

[edited by - Wheaty on March 24, 2002 3:40:18 PM]

Share this post


Link to post
Share on other sites
if (pWindow) RETURN pWindow->PrivWndProc(hwnd, umsg, wparam, lparam);
return what you get from the windowprocedure..

dunno if this helps yet



[edited by - davepermen on March 24, 2002 4:30:48 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by davepermen
if (pWindow) RETURN pWindow->PrivWndProc(hwnd, umsg, wparam, lparam);
return what you get from the windowprocedure..

dunno if this helps yet



[edited by - davepermen on March 24, 2002 4:30:48 PM]


Ah yes, actually it was a combination of returning the pWindow->PrivWndProc() call, and removing the "return 0;" from the "default:" action in PrivWndProc().

Here's the fixed code for anyone who's interested:


LRESULT CALLBACK CWindow:: PrivWndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT paintStruct;
char string[] = "Hello World!";

switch (umsg)
{
case WM_CREATE:
return 0;
break;

case WM_CLOSE:
PostQuitMessage(0);
return 0;
break;

case WM_PAINT:
hDC = BeginPaint(hwnd, &paintStruct);
SetTextColor(hDC, COLORREF(0x00FF0000));
TextOut(hDC, 150, 150, string, sizeof(string)-1);
EndPaint(hwnd, &paintStruct);
return 0;
break;

case WM_NCDESTROY:
delete this;
return 0;
break;

default:
break;
}

return DefWindowProc(hwnd, umsg, wparam, lparam);
}


LRESULT CALLBACK CWindow::MainWndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch (umsg)
{
case WM_NCCREATE:
SetWindowLong(hwnd, GWL_USERDATA, lparam);
default:
CWindow *pWindow = (CWindow*)GetWindowLong(hwnd, GWL_USERDATA);
if (pWindow)
return pWindow->PrivWndProc(hwnd, umsg, wparam, lparam);
break;
}

return DefWindowProc(hwnd, umsg, wparam, lparam);
}



[edited by - Wheaty on March 24, 2002 4:34:10 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by IndirectX
I suggest you move PostQuitMessage to WM_NCDESTROY handler. You also don''t need to use break after return in switches.

If I move the PostQuitMessage(0); call to WM_NCDESTROY, then it is impossible to close the window. Have to end it in the task manager.

Share this post


Link to post
Share on other sites
How did you get your code to work? "this" is the lpCreateParams of CREATESTRUCT. I meant the following:

SetWindowLong(hwnd, GWL_USERDATA, LPCREATESTRUCT(lparam)->lpCreateParams);

Am I missing something very obvious here?

Share this post


Link to post
Share on other sites
Well, it compiled with no errors.

If I put the line in that you just specified, I get this error:

error C2664: ''SetWindowLongA'' : cannot convert parameter 3 from ''LPVOID'' to ''LONG''

I suppose I have to cast a LONG to it or something.

Share this post


Link to post
Share on other sites
The proper way of closing the window is using DestroyWindow(hwnd) is WM_CLOSE and PostQuitMessage in WM_DESTROY, or WM_NCDESTROY.

If you click the close button or press Alt+F4, you get WM_CLOSE. However, to close a window from a program you call DestroyWindow(hwnd). Your method won''t work for DestroyWindow call.

Share this post


Link to post
Share on other sites

  • Forum Statistics

    • Total Topics
      627774
    • Total Posts
      2979012
  • Similar Content

    • By lonewolff
      Hi guys,
      With OpenGL not having a dedicated SDK, how were libraries like GLUT and the likes ever written?
      Could someone these days write an OpenGL library from scratch? How would you even go about this?
      Obviously this question stems from the fact that there is no OpenGL SDK.
      DirectX is a bit different as MS has the advantage of having the relationship with the vendors and having full access to OS source code and the entire works.
      If I were to attempt to write the most absolute basic lib to access OpenGL on the GPU, how would I go about this?
    • By DelicateTreeFrog
      Hello! As an exercise for delving into modern OpenGL, I'm creating a simple .obj renderer. I want to support things like varying degrees of specularity, geometry opacity, things like that, on a per-material basis. Different materials can also have different textures. Basic .obj necessities. I've done this in old school OpenGL, but modern OpenGL has its own thing going on, and I'd like to conform as closely to the standards as possible so as to keep the program running correctly, and I'm hoping to avoid picking up bad habits this early on.
      Reading around on the OpenGL Wiki, one tip in particular really stands out to me on this page:
      For something like a renderer for .obj files, this sort of thing seems almost ideal, but according to the wiki, it's a bad idea. Interesting to note!
      So, here's what the plan is so far as far as loading goes:
      Set up a type for materials so that materials can be created and destroyed. They will contain things like diffuse color, diffuse texture, geometry opacity, and so on, for each material in the .mtl file. Since .obj files are conveniently split up by material, I can load different groups of vertices/normals/UVs and triangles into different blocks of data for different models. When it comes to the rendering, I get a bit lost. I can either:
      Between drawing triangle groups, call glUseProgram to use a different shader for that particular geometry (so a unique shader just for the material that is shared by this triangle group). or
      Between drawing triangle groups, call glUniform a few times to adjust different parameters within the "master shader", such as specularity, diffuse color, and geometry opacity. In both cases, I still have to call glBindTexture between drawing triangle groups in order to bind the diffuse texture used by the material, so there doesn't seem to be a way around having the CPU do *something* during the rendering process instead of letting the GPU do everything all at once.
      The second option here seems less cluttered, however. There are less shaders to keep up with while one "master shader" handles it all. I don't have to duplicate any code or compile multiple shaders. Arguably, I could always have the shader program for each material be embedded in the material itself, and be auto-generated upon loading the material from the .mtl file. But this still leads to constantly calling glUseProgram, much more than is probably necessary in order to properly render the .obj. There seem to be a number of differing opinions on if it's okay to use hundreds of shaders or if it's best to just use tens of shaders.
      So, ultimately, what is the "right" way to do this? Does using a "master shader" (or a few variants of one) bog down the system compared to using hundreds of shader programs each dedicated to their own corresponding materials? Keeping in mind that the "master shaders" would have to track these additional uniforms and potentially have numerous branches of ifs, it may be possible that the ifs will lead to additional and unnecessary processing. But would that more expensive than constantly calling glUseProgram to switch shaders, or storing the shaders to begin with?
      With all these angles to consider, it's difficult to come to a conclusion. Both possible methods work, and both seem rather convenient for their own reasons, but which is the most performant? Please help this beginner/dummy understand. Thank you!
    • By JJCDeveloper
      I want to make professional java 3d game with server program and database,packet handling for multiplayer and client-server communicating,maps rendering,models,and stuffs Which aspect of java can I learn and where can I learn java Lwjgl OpenGL rendering Like minecraft and world of tanks
    • By AyeRonTarpas
      A friend of mine and I are making a 2D game engine as a learning experience and to hopefully build upon the experience in the long run.

      -What I'm using:
          C++;. Since im learning this language while in college and its one of the popular language to make games with why not.     Visual Studios; Im using a windows so yea.     SDL or GLFW; was thinking about SDL since i do some research on it where it is catching my interest but i hear SDL is a huge package compared to GLFW, so i may do GLFW to start with as learning since i may get overwhelmed with SDL.  
      -Questions
      Knowing what we want in the engine what should our main focus be in terms of learning. File managements, with headers, functions ect. How can i properly manage files with out confusing myself and my friend when sharing code. Alternative to Visual studios: My friend has a mac and cant properly use Vis studios, is there another alternative to it?  
    • By ferreiradaselva
      Both functions are available since 3.0, and I'm currently using `glMapBuffer()`, which works fine.
      But, I was wondering if anyone has experienced advantage in using `glMapBufferRange()`, which allows to specify the range of the mapped buffer. Could this be only a safety measure or does it improve performance?
      Note: I'm not asking about glBufferSubData()/glBufferData. Those two are irrelevant in this case.
  • Popular Now