Dynamicly drawing to a texture

BlitzMax Forums/OpenGL Module/Dynamicly drawing to a texture

Jim Teeuwen(Posted 2006) [#1]
hello peeps.
Im having a bit of trouble when drawing to a texture during runtime.

I figured out I need to draw my stuff to the backbuffer first, then copy it to an existing OpenGL texture. This is no problem in itself, but the problem arises during the drawing to the backbuffer. There is no actual Flip()/swapbuffer command issued during the texture drawing, and the actual drawing of the scene in the main loop occurs later on in the code, so there is a glCear()/Cls() issued later in the code. But for some reason, the stuff I draw still apears on the screen, but just for a split second it's visible before being replaced by the actual scene.

Why is this happening?

here is the routine I use for drawing to the texture.

	Method UpdateTexture()

		If( Lines.Length = 0 ) Then
			Return;
		End If

		'// are we in Render or Select mode?
		'// (Select mode is used by Entity.__getPicked() routine)
		Local rm:Int;
		glGetIntegerv( GL_RENDER_MODE, Varptr rm );
		If( rm <> GL_RENDER ) Then Return;

	'//	If( AsciiTables = Null ) Then
	'//		GenerateAsciiTables();
	'//	End If

		If( (Texture[0] = Null) Or ((Texture[0].Width <> (Size.x * 2)) Or (Texture[0].Height <> (Size.y * 2))) ) Then
			Local img:TImage = CreateImage( Size.x * 2, Size.y * 2 );
			Texture[0] = GLTexture.FromImage( img );
		End If


		SetAlpha( 1 );
		TileImage( Texture[1].Image );

		Local x:Float = 2.0;
		Local y:Float = 2.0;
		Local maxlines:Int = Texture[0].Height / TextHeight( "M" );
		Local startline:Int = 0;
		
		If( maxlines < Lines.Length - 1 ) Then
			startline = (Lines.Length - 1) - maxlines;
		End If

		For Local l:TBLine = EachIn Lines
			If( l.Position >=  startline ) Then
				For Local w:TBWord = EachIn l.Words
					For Local t:TBToken = EachIn w.Tokens
						t.Draw( x, y );
						x :+ TextWidth( Chr(t.Value) );
					Next
					x :+ TextWidth(" ");
				Next
				y :+ TextHeight( " " );
				x = 2.0;
			End If
		Next
		
		glCopyTexSubImage2D ( ..
			Texture[0].Texture.ID, ..								'// target
			0, ..													'// level 
			0, 0, ..												'// xoffset, yoffset
			0, Stage.Instance.StageHeight - Texture[0].Height, ..	'// x , y
			Texture[0].Width, Texture[0].Height..					'// width, height
		);

		oldText = Text;
	End Method



the t.Draw( x, y ); routine is simple:

	Method Draw( lx:Float = -1, ly:Float = -1 )
		If( lx = -1 ) Then lx = x;
		If( ly = -1 ) Then ly = y;

		If( IsSelected ) Then
			SetColor( 255-Word.Textbox.ForeColor[0], 255-Word.Textbox.ForeColor[1], 255-Word.Textbox.ForeColor[2] );
		Else
			SetColor( Word.Textbox.ForeColor[0], Word.Textbox.ForeColor[1], Word.Textbox.ForeColor[2] );
		End If
		
		DrawText( Chr(Value), lx, ly );
	End Method


This is not the only instance where I can actually see the backbuffer contents on my screen without it having been 'flipped'. Other parts of the code where I attempted drawing to a texture had a similar result.

Note that I have tried various different things to make it go away. Like insert a CLS() at the end of the drawing routine. This does stop the drawn contents from showing on my screen, but instead it shows a completely black background for a split second.

Either a backbuffer does not exist and everything is just drawn to the frontbuffer directly, or for some reason, either Max2D or OpenGL insists on swapping the front/back buffer without issuing an explicit Flip/SwapBuffer


ImaginaryHuman(Posted 2006) [#2]
OpenGl will not flip the screen if you do not tell it to.

If you can see stuff being drawn you do not have a backbuffer.

Or you can put glDrawBuffer(GL_BACK) just to be sure. Having said that, on some systems it's idea of which is back and which is front is all screwed up.

It should default to the backbuffer if there is one. I suggest testing to be sure that it may be drawing to the front buffer. Also call:

Local a:Int
glGetIntegerv(GL_DOUBLEBUFFER,Varptr(a))
print a

(it might not be doublebuffer, maybe DOUBLE_BUFFERED. .. but it should tell you if there is one.


Jim Teeuwen(Posted 2006) [#3]
thanks for the info.

Unfortunatly it seems I am using a doublebuffer and drawing to the backbuffer so that rules out this possibility. :|

ah well. time to dig further i guess


Drey(Posted 2006) [#4]
As i'm researching this stuff, isn't there an aux buffers to render to and extract form?


Jim Teeuwen(Posted 2006) [#5]
There are a number of extra buffers, but the they don't make any difference in my case. I found out that, eventhough not intended to, my draw-to-texture code was running during the main render loop in some cases. this caused the flickering of the screen. It's solved now.

Now all I need is to find a proper way to render Text to a texture and still have it look OK. Atm either the texture turns out to be to small for the object im putting it on, or it looks really horrible.


New drawing code
Method Invalidate( r:Rectangle = Null )
	Super.Invalidate( r );

	If( (Text = oldText) Or (Tokens.Length = 0) ) Then
		Return;
	End If

	'// are we in Render or Select mode?
	'// (Select mode is used by Entity.__getPicked() routine)
	Local rm:Int;
	glGetIntegerv( GL_RENDER_MODE, Varptr rm );
	If( rm <> GL_RENDER ) Then Return;

	If( (Texture[0] = Null) Or ((Texture[0].Width <> (Size.x * 2)) Or (Texture[0].Height <> (Size.y * 2))) ) Then
		Local img:TImage = CreateImage( (Size.x * 2), (Size.y * 2) );
		Texture[0] = GLTexture.FromImage( img );
	End If

	Local tw:Int = Texture[0].Width, th:Int = Texture[0].Height;
	GLTexture.AdjustTexSize( tw, th );

	glViewPort( 0, 0, tw, th );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	SetAlpha( 1 );
	SetBlend( ALPHABLEND );
	TileImage( Texture[1].Image );

	If( MultiLine ) Then
		Local x:Float = 2.0, y:Float = th;
		Local ncw:Int = tw / TextWidth( "M" );
		Local nch:Int = th / TextHeight( "M" );
		Local nx:Int = 0, ny:Int = 0, count:Int = 0;
		Local startline:Int = 0;

		If( ActiveControl = Self ) Then
			If( MouseZ() <> 0 ) Then
				If( OldMouseZ < MouseZ() ) Then
					ScrollOffset :- 1;
				Else If( OldMouseZ > MouseZ() ) Then
					ScrollOffset :+ 1;
				End If
			End If
		End If
	
		If( Linecount > nch ) Then
			startline = (Linecount - nch);
			startline :+ ScrollOffset;
	
			If( startline < 0 ) Then
				startline = 0;
				ScrollOffset :+ 1;
			End If
	
			If( startline > Linecount - nch ) Then
				startline = Linecount - nch;
				ScrollOffset :- 1;
			End If
		End If

		For Local t:TBToken = EachIn Tokens
			If( count >= startline + nch ) Then
				Exit;
			End If

			If( count >= startline ) Then
				Select( Chr(t.Value) )
				 Case "~n", "~r", "~r~n";
				 Default;
					t.Draw( x, y );
					x :+ t.Rect.w; nx :+ 1;
					If( nx >= ncw ) Then
						x = 0; nx = 0;
						y :- t.Rect.h; ny :+ 1;
					End If
				End Select
			End If

			Select( Chr(t.Value) )
			 Case "~n", "~r", "~r~n";
				If( count >= startline And count <= (startline + nch) ) Then
					x = 0; nx = 0;
					y :- TextHeight("M"); ny :+ 1;
				End If
				count :+ 1;
			End Select
		Next

	Else
		Local x:Float = 2.0, y:Float = 2.0;

		For Local t:TBToken = EachIn Tokens
			Select( Chr(t.Value) )
			 Case "~n", "~r", "~r~n";
			 Default;
				t.Draw( x, y );
				x :+ t.Rect.w;
			End Select
		Next
	End If

	glBindTexture( GL_TEXTURE_2D, Texture[0].Texture.ID );
	glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, tw, th, 0);
	glViewport( 0, 0, Stage.Instance.Width, Stage.Instance.Height );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glBindTexture( GL_TEXTURE_2D, 0 );

	oldText = Text;

End Method


The resulting texture is not of the proper size to fit over the entire control (sprite) though. I have to adjust the texure size oin the above code to fit a Power of 2 Size, otherwise the glCopyTexImage2D() function fails. this causes some problems when Im trying to render the characters to it in the proper location.
To make the texture and characters allign properly, I am resetting the viewport to the texture's size before the drawing starts. This method is taken from NeHe's Blur tutorial (#36).

It doesnt work quite as intended though. The characters are very blurry. and the texture does not scale properly with the quad. the lines should stretch from the top of the screen, to the blue bar in the center. and from the left to the very right of the screen.



This is what the texture looks like just after it has been drawn to the screen and before it is copied to the actual OpenGL texture. It is still somewhat blurry, but the direction of the characters, the horizontal size and the scaling of the lines is correct.




*(Posted 2006) [#6]
What it looks like here is you are losing horizontal lines when the texture is squashed onto the area its supposed to go into, have you tried doubling the height of the font if thats possible before placing the texture.


Duck(Posted 2006) [#7]
Hi,

There is an OpenGL extension WGL_ARB_PBUFFER that does render to texture without using the backbuffer.

Here's a link to a sample in C++ that does this:

http://www.paulsprojects.net/opengl/rtotex/rtotex.html


Jim Teeuwen(Posted 2006) [#8]
cool thanks :)

edit: well that was shortlived :p
My card doesn't support any of the extensions required for Pbuffer's :p Seems my GF FX 5700 is getting old :D