sn1fflez Posted July 30, 2010 Posted July 30, 2010 (edited) I am working with OpenGl (Tao Framework) on vb.net. Now, I know that I've implemented opengl correctly but I'm not actually sure where the problem lies with my code. Here is the best explanation of the problem I can come up with. Given: The application I'm working with works perfectly in VB6 but in vb.net I've had to make changes to the UI portion of the code. Most pertinent to the problem at hand is that calls to the paint method of a picturebox can no longer be called as easily. I've had to replace calls to the paint method such as frmBackground.PctAnimate_Paint(Nothing, New System.Windows.Forms.PaintEventArgs(Nothing, Nothing)) with frmbackground.pctanimate.refresh. The application I'm working on has a main form called frmbackground and it has a picturebox control called pctanimate on which I have attached the opengl graphics context (sorry if my terminology is incorrect). When I rightclick on pctanimate, a menu pops up from which I can select to open(show and focus on) a form called frmgraphics in which I can change some graphics "properties" in pctanimate. When frmgraphics is displayed on the screen, frmbackground is disabled. There are three buttons in frmgraphics: apply, done, and cancel. One last thing you should know: The default color of pctanimate is black. But the graphics that should always be shown are a white grid on a black background. Problem: (In frmgraphics) Whenever I hit apply I lose the graphics on the screen and when I hit done (frmgraphics.close is called after calling frmbackground.enable = true) the graphics come back but the portion of the screen on which the frmgraphics was visible has not been painted. So when I hit done there is a black rectangle where frmgraphics used to be. There is no obvious problem with my paint event because when I hit apply (in frmgraphics) I lose the graphics but if I then move frmgraphics to another position on the screen pctanimate is correctly painted with the correct graphics(ie a grid) objects (ie no black rectangles result). How can I correct this problem? Sample code: This is called first as initiated by pressing the apply button on the frmgraphics form. Private Sub buttonApply_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles ApplyButton.Click Dim k, i, j, KTemp As Integer Dim IAnimateModeT As Integer Dim DD As Double Dim Str1 As Double Dim Str2 As String Dim DDC(3) As Double frmBackground.PctAnimate.BackColor = Picture1.BackColor 'frmBackground.PctAnimate_Paint(Nothing, New System.Windows.Forms.PaintEventArgs(Nothing, Nothing)) frmBackground.PctAnimate.Refresh() ScreenColor = SelectedColor GridColor = GridColor1 For i = 1 To 3 AXColor(i) = AxisColor(i) Next i If Check1.CheckState = 1 Then IGridShow = 1 Else IGridShow = 0 End If If Check2.CheckState = 1 Then IAxesShow = 1 Else IAxesShow = 0 End If GridSpace = Combo1.SelectedIndex + 1 GridThickness = Combo2.SelectedIndex + 1 AxesLength = Combo3.SelectedIndex + 1 AxesThickness = Combo4.SelectedIndex + 1 'Camera DDC(1) = CDbl(Text1.Text) - CDbl(Text4.Text) DDC(2) = CDbl(Text2.Text) - CDbl(Text5.Text) DDC(3) = CDbl(Text3.Text) - CDbl(Text6.Text) k = 0 For i = 1 To 3 If System.Math.Abs(DDC(i)) < 0.0001 Then k = k + 1 If k = 2 Then Mss(1) = "Wrong selection of the camera parameters." Mss(2) = CStr(MsgBoxStyle.OkOnly) Mss(3) = "Sams: Data Error" Call ErrMess() Exit Sub End If Next i CamLx = CDbl(Text1.Text) CamLy = CDbl(Text2.Text) CamLz = CDbl(Text3.Text) CamPx = CDbl(Text4.Text) CamPy = CDbl(Text5.Text) CamPz = CDbl(Text6.Text) CamUpx = CDbl(Text7.Text) CamUpy = CDbl(Text8.Text) CamUpz = CDbl(Text9.Text) DD = (CamUpx ^ 2 + CamUpy ^ 2 + CamUpz ^ 2) ^ 0.5 CamUpx = CamUpx / DD CamUpy = CamUpy / DD CamUpz = CamUpz / DD CameraFovy = CDbl(Text11.Text) ICameraFollow = Combo5.SelectedIndex IBodyLook = Combo6.SelectedIndex ICameraTranslate = Check3.CheckState ICameraRotate = Check4.CheckState If ICameraFollow = 0 Then ICameraTranslate = 0 ICameraRotate = 0 End If IPreviousCameraGraham = ICameraGraham 'Animation AnimationSpeed = (10 - Slider1.Value) * 5 FrameSteps = Int(CDbl(Text10.Text) End Sub The next function to be called is frmbackground_paint Private Sub frmBackground_Paint(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint If Err.Number <> 0 Then MessageBox.Show("Error Number: " & Err.Number.ToString & ". Error Source: " & Err.Source.ToString & ". This is error in paint 1") If VB6.PixelsToTwipsX(Me.Width) <= 0.6 * VB6.PixelsToTwipsX(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width) Then _ Me.Width = VB6.TwipsToPixelsX(0.6 * VB6.PixelsToTwipsX(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width)) If VB6.PixelsToTwipsY(Me.Height) <= 0.6 * VB6.PixelsToTwipsY(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height) Then _ Me.Height = VB6.TwipsToPixelsY(0.6 * VB6.PixelsToTwipsY(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height)) If IUTL = 0 And NOC(6) = 0 Then With PctAnimate .Width = Me.Width .Height = VB6.TwipsToPixelsY(VB6.PixelsToTwipsY(StatusBar1.Top) - VB6.PixelsToTwipsY(Toolbar1.Height)) .Left = 0 .Top = Toolbar1.Height End With Else With SamsTree1 .Width = VB6.TwipsToPixelsX(0.177 * VB6.PixelsToTwipsX(Me.Width)) .Height = VB6.TwipsToPixelsY(VB6.PixelsToTwipsY(StatusBar1.Top) - VB6.PixelsToTwipsY(Toolbar1.Height)) .Left = VB6.TwipsToPixelsX(0.82 * VB6.PixelsToTwipsX(Me.Width)) .Top = Toolbar1.Height '.Size = New System.Drawing.Size(100, 100) '.Location = New System.Drawing.Point(200, 20) '.Anchor = AnchorStyles.Right Or AnchorStyles.Top Or AnchorStyles.Bottom End With With PctAnimate '.Width = 0.81 * Me.Width .Width = VB6.TwipsToPixelsX(VB6.PixelsToTwipsX(Me.Width) - VB6.PixelsToTwipsX(SamsTree1.Width)) .Height = VB6.TwipsToPixelsY(VB6.PixelsToTwipsY(StatusBar1.Top) - VB6.PixelsToTwipsY(Toolbar1.Height)) .Left = 0 .Top = Toolbar1.Height End With End If If Err.Number <> 0 Then MessageBox.Show("Error Number: " & Err.Number.ToString & ". Error Source: " & Err.Source.ToString & ". This is error in paint 2") End Sub The debugger loops in frmbackground_paint (i'm guessing because I'm trying to step through every time visual studio tries to display the form as it debugs). After stepping out of debugging pctanimate now is all black and I have lost the graphics (frmgraphics is not closed by clicking apply). If I where to move the form frmgraphics, however, the graphics (ie the grid) re-appears. The sequence of subroutines that fire when I step through the program after clicking done on frmgraphics is: First: Private Sub ButtonDone_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles ButtonDone.Click frmBackground.Enabled = True Me.Close() End Sub then Private Sub frmGraphics_FormClosed(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed Call IBACKShow() End Sub then Sub IBACKShow() If IUTL = 5 Then frmBackground.Enabled = False Exit Sub End If frmBackground.Enabled = True End Sub I then end up back at frmbackground_paint and looping there until I step out of debug mode. After exiting debug mode the form frmgraphics is no longer visible but there is a black rectangle in the graphics grid where the form used to be. Opening any other form and moving it slightly will cause the pctanimate to repaint itself and as a result draw the complete grid without any problems. I believe the problem occurs because I have not understood fully the sequence of paint events that are initiated by windows and how vb.net handles them. Any ideas as to where the problem may lie would be greatly appreciated. Thank you for your time. Edited July 31, 2010 by snarfblam Quote
Leaders snarfblam Posted July 31, 2010 Leaders Posted July 31, 2010 I'm having a little bit of trouble understanding exactly what your code is doing. How you deal with the paint-related events depends on how you are drawing. Are you rendering a static image every time you need to redraw the control? Or are you continuously rendering (i.e. on a loop or timer). It sounds like the former. Unless you are running a rendering loop, you should be redrawing your pctanimate control every time its paint event is raised. You haven't posted the code for the handler, but it sounds like the problem may be there. It looks like you are resizing controls in your frmBackground's Paint event. Generally, the only thing you should be doing in the Paint event handler is drawing to the form. Resizing controls every time the form is painted sounds like a bad idea to me. Quote [sIGPIC]e[/sIGPIC]
sn1fflez Posted August 2, 2010 Author Posted August 2, 2010 Hey snarfblam, Thanks for taking the time out to reply. I'm sorry I haven't responded sooner. So the actual paint handler used in the program is this: Public Sub PctAnimate_Paint( ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.PaintEventArgs) Handles PctAnimate.Paint Dim IB, ISSS As Integer Dim IKK As Integer Dim m(15) As Single If IUTL <> 0 Then PctAnimate.Visible = False Exit Sub End If If IC(1) = 2 Then ISSS = 12 Gl.glLoadIdentity() Gl.glClearColor( CInt(&HFF And ScreenColor) / 255, ((&HFF00 And ScreenColor) \ 256) / 255, ((&HFF0000 And ScreenColor) \ 65536) / 255, 0) Gl.glClear(Gl.GL_COLOR_BUFFER_BIT Or Gl.GL_DEPTH_BUFFER_BIT) If IAnimate = 0 Then Call MoveCamera(0, 0) End If If NOC(6) > 0 And IUTL = 0 Then 'Initial Configuration For IB = 1 To NOC(6) If IFLR(IB, 8) = 1 Then Call InitialDisplay(IB) IFLR(IB, 8) = 0 End If Next If IAnimate = 0 Then Timer1.Enabled = False For IB = 1 To NOC(6) 'If AI(IB, ISSS) > 0 And AI(IB, IGR6) = 0 Then Call DrawObject(IB, 0) If AI(IB, ISSS) > 0 And AI(IB, IGR6) = 0 Then IKK = IFLR(IB, 7) + NOC(6) Gl.glNewList(IKK, Gl.GL_COMPILE) Gl.glPushMatrix() Call DrawObjectF(IB, 0) Gl.glPopMatrix() Gl.glEndList() Gl.glPushMatrix() Gl.glCallList((IKK)) Gl.glPopMatrix() End If Next IB IFrameL = 0 End If 'Animation of the system If IAnimate = 1 Then Call MoveCamera(ICameraFollow, IBodyLook) For IB = 1 To NOC(6) If AI(IB, ISSS) > 0 And AI(IB, IGR6) = 0 Then Call DrawObjectF(IB, IFrameA) Next IB IFrameL = IFrameA End If 'Pause If IAnimate = 2 Then Timer1.Enabled = False Call MoveCamera(ICameraFollow, IBodyLook) For IB = 1 To NOC(6) 'If AI(IB, ISSS) > 0 And AI(IB, IGR6) = 0 Then Call DrawObject(IB, IFrameL) If AI(IB, ISSS) > 0 And AI(IB, IGR6) = 0 Then Call DrawObjectF(IB, IFrameL) Next IB End If If IAnimate > 0 And IFrameL > 0 Then If NFrame > 0 And IAnimateMode = 0 Then StatusBar1.Panels(1).Text = "Time = " & CStr (AnA(IFrameA, 1)) If NFrame > 0 And IAnimateMode = 1 Then StatusBar1.Panels(1).Text = "Time = " & CStr (ANF(IModePrint, 1)) StatusBar1.Panels(2).Text = "Frame = " & CStr (IFrameA) Else StatusBar1.Panels(1).Text = "Time = 0" StatusBar1.Panels(2).Text = "Frame = 0" End If End If Gl.glFlush() Gdi.SwapBuffers(hDC) '200: End Sub So the program is either rendering a static image or continually rendering on a timer depending on the value of IAnimate. The timer code is as follows: Private Sub Timer1_Tick( ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles Timer1.Tick If IAnimate = 1 Then IFrameA = IFrameA + FrameSteps If IFrameA > NFrame Then IFrameA = 1 'Call PctAnimate_Paint(PctAnimate, New System.Windows.Forms.PaintEventArgs(Nothing, Nothing)) PctAnimate.Refresh() End If End Sub I feel as though the paint handler may be called one too many times or the buffering isn't done correctly or perhaps I should move the above code into the onpaint(or onpaintbackground) method. Should I be setting the controlstyle of pctanimate to .AllPaintingInWmPaint? I've been reading around the net to find out if maybe there is some sort of doublebuffering set into the forms by default. What do you feel is the most likely problem? If you have any other questions about the code (I know the variable and method names aren't that helpful - sorry about that) I would be more than happy to answer. Thanks for your help so far : ) sn1fflez Quote
Leaders snarfblam Posted August 3, 2010 Leaders Posted August 3, 2010 Sorry, I can't really make a lot of sense of your PctAnimate_Paint code. I'm not going to pick on your coding style, but I can't really do much with that. I know plenty about GDI+ painting, but not so much about OpenGL or what's going on inside your app, so if the code's not clear it might as well be Chinese. Quote [sIGPIC]e[/sIGPIC]
sn1fflez Posted August 3, 2010 Author Posted August 3, 2010 That's okay. Thank you for trying. I really do need to work on making my code more friendly to other people. Perhaps you could answer some questions for me: in your opinion would it be better for me to create a control that inherits picturebox, override the onpaint method with the above code, and change the style to wnAllPaintingInWmPaint? Quote
Leaders snarfblam Posted August 4, 2010 Leaders Posted August 4, 2010 AllPaintingInWmPaint is a good plan if you really are doing all painting in the Paint events/methods. It gives WinForms a better idea of how you are painting (i.e. that you are adhering strictly to the standard invalidation/paint cycle) so it can handle its own side of the painting a little better. However, the biggest issue this seems to address are flickering and double-buffering. From MSDN, AllPaintingInWmPaint: If true, the control ignores the window message WM_ERASEBKGND to reduce flicker. This style should only be applied if the UserPaint bit is set to true. [/Quote] As for what to inherit, I used to always inherit PictureBox, Panel, or UserControl for all my controls. I guess it gave me a sense that since I'm starting with a complete control, there will be a smaller chance of gaps or omissions on my end. That, and PictureBox was the class to use when you needed a control with it's own window and handle in VB6. Since, I've discovered that if you are implementing 100% of the painting yourself and you don't need any functionality that another control provides, there is no real reason not to simply inherit the Control class. Even so, while inheriting PictureBox doesn't buy you anything, it doesn't cost you much either. I still think the problem lies in your paint event. The fact that it is a bit convoluted and unclear only supports the idea. I think that if you break the logic down (i.e. use more methods or even classes, and use enumerations instead of magic numbers), the problem just might present itself. Quote [sIGPIC]e[/sIGPIC]
sn1fflez Posted August 4, 2010 Author Posted August 4, 2010 Thank you so much for the response Snarfblam. I will take your advice and rewrite the code for the paint handler, It seems to be a better plan of attack than guess and check :). Quote
sn1fflez Posted August 16, 2010 Author Posted August 16, 2010 Hey SnarfBlam, I finally finished rewriting my problem code. I've also found out that it is most likely a problem arising from the switch from vb6 to vb.net. Apparently the the autoredraw property was key to the permanence of my graphics. I cannot find the autoredraw property in vb.net and am searching for a viable alternative. I've found some people who say that the best alternative to autoredraw is to save the contents of the picturebox and then set it as the image of the picturebox every time the control is painted. There may be two problems with this approach: I don't know if saving a picture each time the control is painted will slow down animations too much, and I don't know what would be the fastest way to save an image (or how to save an image). I was hoping you would be able to give me some advice. I know you're knowledgeable in GDI and you might have encountered this problem before. I have included a link to the download of the sample project I've come up with. The rar file also includes the tao framework install that is needed to run the project. The code is basically composed of the calls necessary to draw something with opengl in vb.net. These calls are all fine but as soon as you run the project the problem will become apparent. Clicking on the button at the upper left of the background form brings up another form. Try moving this form and you'll see the graphics come in and then leave if you click any of the buttons in the second form. One last thing to remember in deciding how to advise me is that I cannot move away from using either opengl or the picturebox control (actually I just need to be able to use the image property of the picturebox). Thank you so much for your time and I really appreciate your advice from before. http://rapidshare.com/files/413337094/GraphicsFlicker3.rar Quote
Leaders snarfblam Posted August 17, 2010 Leaders Posted August 17, 2010 First, on AutoRedraw: Apparently the the autoredraw property was key to the permanence of my graphics. All AutoRedraw really amounts to is double-buffering. The real issue is simply that your control isn't being redrawn when it should. AutoRedraw is a very easy way of making this happen, but the standard painting cycle, if properly implemented, should also correctly paint your form. Now, I ran your code, tinkered with it, and the problem seems a bit unusual. What is actually happening is the invalid region (i.e. the area that needs to be drawn) is not being redrawn with your image, but everything else is. That seems completely backwards, and the result is that every time the control is painted, the area that should have been updated is empty, everything else looks fine. If the entire control should be updated, the entire control will be left blank. If a small area should be updated, you will notice an artifact of blankness there. This is either happening because something is clearing the invalid region after you update it, or because the invalid region is somehow being clipped (the wrong way) when you update. One possibility that occurs to me is that Windows is trying to optimize the painting in a way that is working against you. I don't think this is likely. One solution that worked for me is to render in an idle loop. This is a pretty standard thing when a Windows app that mixes hardware accelerated rendering with an otherwise standard UI. Basically, you run the render code once each time the Applicition.Idle event is raised. It's easy and sidesteps the whole problem. I tried that and it worked very well. Quote [sIGPIC]e[/sIGPIC]
Leaders snarfblam Posted August 17, 2010 Leaders Posted August 17, 2010 First, on AutoRedraw: Apparently the the autoredraw property was key to the permanence of my graphics. All AutoRedraw really amounts to is double-buffering. The real issue is simply that your control isn't being redrawn when it should. AutoRedraw is a very easy way of making this happen, but the standard painting cycle, if properly implemented, should also correctly paint your form. Now, I ran your code, tinkered with it, and the problem seems a bit unusual. What is actually happening is the invalid region (i.e. the area that needs to be drawn) is not being redrawn with your image, but everything else is. That seems completely backwards, and the result is that every time the control is painted, the area that should have been updated is empty, everything else looks fine. If the entire control should be updated, the entire control will be left blank. If a small area should be updated, you will notice an artifact of blankness there. This is either happening because something is clearing the invalid region after you update it, or because the invalid region is somehow being clipped (the wrong way) when you update. One possibility that occurs to me is that Windows is trying to optimize the painting in a way that is working against you. I don't think this is likely. TL;DR: I'm at a bit of a loss. One solution that worked for me is to render in an idle loop. This is a pretty standard thing when a Windows app that mixes hardware accelerated rendering with an otherwise standard UI. Basically, you run the render code once each time the Applicition.Idle event is raised. It's easy and sidesteps the whole problem. I tried that and it worked very well. Public Sub New() ' This call is required by the Windows Form Designer. InitializeComponent() frmSelectGraphics.Hide() ' Add any initialization after the InitializeComponent() call. Call OpenGL00() [color="Blue"] AddHandler Application.Idle, AddressOf DoDrawStuff[/color] [color="Green"] 'This handler must be removed when the form is closed! [/color] End Sub [color="Blue"] Private Sub DoDrawStuff(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BttnGraphics.Click DrawStuff() End Sub Sub DrawStuff()[/Color] Gl.glLoadIdentity() Gl.glClearColor(CInt(&HFF And screencolor) / 255, ((&HFF00 And screencolor) \ 256) / 255, ((&HFF0000 And screencolor) \ 65536) / 255, 0) 'Gl.glClear(Gl.GL_COLOR_BUFFER_BIT Or Gl.GL_DEPTH_BUFFER_BIT) Call CameraPrespective() Glu.gluLookAt(-4, -2, 2.5, 0, 0, 0, 0, 0, 1) Gl.glNewList(1, Gl.GL_COMPILE) Gl.glPushMatrix() Glu.gluDisk(QuadObj, 2, 3, 32, 64) Gl.glPopMatrix() Gl.glEndList() Gl.glPushMatrix() Gl.glCallList(1) Gl.glPopMatrix() Gl.glFlush() Gdi.SwapBuffersFast(hDC) [color="Blue"] End Sub[/color] Quote [sIGPIC]e[/sIGPIC]
sn1fflez Posted August 17, 2010 Author Posted August 17, 2010 (edited) Thanks for the help!!! from the looks of it, this seems to be the perfect solution(I'm more than okay with sidestepping an issue whenever possible). Again thank you so much, I'll let you know how this works out. -Sn1fflez Edited August 17, 2010 by sn1fflez Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.