SetViewPort Problem

BlitzMax Forums/BlitzMax Programming/SetViewPort Problem

Hi!

Today I've tested my upcoming game with various systems, and on some old Geforce 2-GraphicCards the SetViewPort-Command from BlitzMax will be totaly ignored.

I've found some info on this board, that some gfx-card doesn't support the BlitzMax SetViewPort command.

Can you think of a workaround so I can clip images (in realtime) so that it works on all graphic cards? The BlitzMax DrawImageRect command doesn't work here, because it changes the uv-coords / scales the image.

not sure how you're using the viewport command. if its just to clip images that are off screen then just use black images drawing on the borders? if its some sort of inset (like an animated hud or something) the only thing I can think of at the moment is doing a grab pixels:
- draw your hud off screen somewhere on the backbuffer.
- then do a grabImage of the size you want.
- then just draw that image where you want to on the screen.

hmm, this is what I was testing in this thread:

http://www.blitzbasic.com/Community/posts.php?topic=62781

but noone found a PC where it didn't work. Any chance you can test my exe in that thread on the Geforce PCs and list the exact model numbers of the cards?

One of my problems was fixed with a Ian Duff's code (drawing part of rect) but for the other problem I could only use viewport because it involved drawing text onto an image but having the text clipped to a viewport. The only way I could see around this was to predraw the text on the image, and then use Ian Duff's code to draw part of the image only...

Meanwhile, are these two bits of code any good to you:

' -----------------------------------------------------------------------------
' cctg_drawimagerect by TonyG (slow?)
' -----------------------------------------------------------------------------
Function cctg_DrawImageRect(image:TImage,x:Int,y:Int,xs:Int,ys:Int,width:Int,height:Int)
    DrawImage LoadImage(PixmapWindow(LockImage(image),xs,ys,width,height)),x,y
End Function

' -----------------------------------------------------------------------------
' ccDrawImageArea by Ian Duff (faster?)
' -----------------------------------------------------------------------------
' Note that this code works fine in DirectX or OpenGL on PCs - it autodetects (JB).
Function ccDrawImageArea(image:TImage, x#, y#, rx#, ry#, rw#, rh#, frame=0)
  Local origin_x#, origin_y# ; GetOrigin (origin_x, origin_y)
  Local tw = Pow2Size(image.width)
  Local th = Pow2Size(image.height)
  Local rw1#  = rx + rw
  Local rh1#  = ry + rh
  Local x0# = -image.handle_x, x1# = x0 + rw
  Local y0# = -image.handle_y, y1# = y0 + rh
  
  If rw1 > image.width
    x1 = x0 + rw + image.width - rw1
    rw1 = image.width
  EndIf
   
  If rh1 > image.height
    y1 = y0 + rh + image.height - rh1
    rh1 = image.height
  EndIf
?Win32
  If TD3D7ImageFrame(image.frame(frame))
    Local frame:TD3D7ImageFrame = TD3D7ImageFrame(image.frame(frame))
    
    frame.setUV(rx / tw, ry / th, rw1 / tw, rh1 / th)
    frame.Draw x0, y0, x1, y1, x + origin_x, y + origin_y
    frame.setUV(0, 0, image.width / Float(tw), image.height / Float(th))
  Else
?
    Local frame:TGLImageFrame = TGLImageFrame (image.frame(frame))
                
    frame.u0 = rx / tw
    frame.v0 = ry / th
    frame.u1 = rw1 / tw
    frame.v1 = rh1 / th
    
    frame.Draw x0, y0, x1, y1, x + origin_x, y + origin_y
    
    frame.u0 = 0
    frame.v0 = 0
    frame.u1 = image.width / Float(tw)
    frame.v1 = image.height / Float(th)
?Win32
  EndIf
?
  
  Function Pow2Size(n)
    Local ry = 1
    
    While ry < n
      ry :* 2
    Wend
    
    Return ry
  End Function
End Function


The GraphicCard is: nVidia GeForce2MX400

Isn't it a little bit slow to grab the image each frame?

I don't even understand what those two bits of code are doing, I just use them. So they are grabbing an image each frame and putting out a smaller rectangle yes? Well if that's all that can be done...

The pixmap.window is slow and was supposed to be for a one-off. The SetUV code should be fast enough though.

thanks for clearing that up, that's what I thought which is why my comments say Slow? and Faster?

The code doesn't compile: "Duplicate identifier 'frame'" ??

Blimey, then put on Superstrict and make sure each variable is defined.
For your ease...
SuperStrict
Function ccDrawImageArea(image:TImage, x#, y#, rx#, ry#, rw#, rh#, frame:Int=0)
  Local origin_x#, origin_y# ; GetOrigin (origin_x, origin_y)
  Local tw:Int = Pow2Size(image.width)
  Local th:Int = Pow2Size(image.height)
  Local rw1#  = rx + rw
  Local rh1#  = ry + rh
  Local x0# = -image.handle_x, x1# = x0 + rw
  Local y0# = -image.handle_y, y1# = y0 + rh
  
  If rw1 > image.width
    x1 = x0 + rw + image.width - rw1
    rw1 = image.width
  EndIf
   
  If rh1 > image.height
    y1 = y0 + rh + image.height - rh1
    rh1 = image.height
  EndIf
?Win32
  If TD3D7ImageFrame(image.frame(frame))
    Local frame:TD3D7ImageFrame = TD3D7ImageFrame(image.frame(frame))
    
    frame.setUV(rx / tw, ry / th, rw1 / tw, rh1 / th)
    frame.Draw x0, y0, x1, y1, x + origin_x, y + origin_y
    frame.setUV(0, 0, image.width / Float(tw), image.height / Float(th))
  Else
?
    Local frame:TGLImageFrame = TGLImageFrame (image.frame(frame))
                
    frame.u0 = rx / tw
    frame.v0 = ry / th
    frame.u1 = rw1 / tw
    frame.v1 = rh1 / th
    
    frame.Draw x0, y0, x1, y1, x + origin_x, y + origin_y
    
    frame.u0 = 0
    frame.v0 = 0
    frame.u1 = image.width / Float(tw)
    frame.v1 = image.height / Float(th)
?Win32
  EndIf
?
  
  Function Pow2Size:Int(n:Int)
    Local ry:Int = 1
    
    While ry:Int < n
      ry :* 2
    Wend
    
    Return ry
  End Function
End Function
Graphics 640,480
Local image:TImage=LoadImage("max.png")
ccdrawimagearea(image,100,100,80,80,200,200)
Flip
WaitKey()

Let me know when your tea needs stirring.

Thanks, seems to work fine.

Because I have A LOT OF SetViewport / DrawImage Commands in my game source I tought of a possibilty To overwrite those two Blitzfunctions, but I have some problems with the math how To calculate the news coords.

The source should look something like this
SuperStrict

Graphics 640,480,0

'Viewport coords
Global vx:Float, vy:Float, vw:Float, vh:Float

Local t:TImage = LoadImage ("gfx/mainmenu.png")

While(1)
Cls
SetViewport 200,200,100,100
DrawImage t,20,20
Flip
Wend

Function SetViewport( x:Float, y:Float, width:Float, height:Float ) 
	vx = x
	vy = y
	vw = width
	vh = height
End Function

Function DrawImage(image:TImage, x:Float, y:Float, frame:Int=0)
	
        'Calculate new coords, I don't know how to calculate them

	Local rx:Float = vx - x
	Local ry:Float = vy - y
	Local rw:Float = vw
	Local rh:Float = vh

        'Recalculate x/y coord

       x = ???
       y = ???
		
	ccDrawImageArea(image,x,y,rx,ry,rw,rh,frame)
		
End Function



I know that other commands (like DrawRect etc...) won't use the viewport, but that doesn't matter for my case...

I can attest to the viewport problem--one of my testing boxes has it. It only seems to happen with DirectX. I'm using a GeForce3 something or other; the real kicker is that before I reinstalled Win2K on this machine, I wasn't having a problem, so it's almost certainly a driver or DirectX issue.

maverick69: oops sorry, yeah it just needs Strict before it and nothing else.

Tom Darby. Hmm that's a bummer that it's just a driver issue...or actually maybe that's good cos it means it can be fixed...

@Maverick, Make your viewport and object and add 'myviewport.x' and 'myviewport.y' to your image x/y?

I have written a little test app, and it seems to work fine. Only one small mistake. If the Viewport starts at (100,100) and I draw the image at (120,120) the lase pixel row and colum (above and left from the image) will be repeated.

Here's the code. You can hold down "Q" to swap between BlitzMax SetViewport and the overwritten SetViewport-Command

SuperStrict

Graphics 640,480,0

'Viewport coords
Global vx:Float, vy:Float, vw:Float, vh:Float

Local t:TImage = LoadImage ("gfx/mainmenu.png")
Local a:Int = 0
While(Not KeyDown(KEY_ESCAPE))
	a:+1
	Cls
	If KeyDown(KEY_Q)
		SetViewportC 200+(Sin(a)*100),200+(Cos(a)*100),100,100
		DrawImageC t,210,210
	Else
		SetViewport 200+(Sin(a)*100),200+(Cos(a)*100),100,100
		DrawImage t,210,210
		SetViewport 0,0,640,480
	End If
	Flip
Wend

Function SetViewportC( x:Float, y:Float, width:Float, height:Float ) 
	vx = x
	vy = y
	vw = width
	vh = height
End Function

Function DrawImageC(image:TImage, x:Float, y:Float, frame:Int=0)
	
        'Calculate new coords, I don't know how to calculate them

	Local rx:Float = vx
	Local ry:Float = vy
	Local rw:Float = vw
	Local rh:Float = vh
	
	rx:-x
	ry:-y

	x = vx 
	y = vy 
	


		
	ccDrawImageArea(image,x,y,rx,ry,rw,rh,frame)
		
End Function

Function ccDrawImageArea(image:TImage, x#, y#, rx#, ry#, rw#, rh#, frame:Int=0)
  Local origin_x#, origin_y# ; GetOrigin (origin_x, origin_y)
  Local tw:Int = Pow2Size(image.width)
  Local th:Int = Pow2Size(image.height)
  Local rw1#  = rx + rw
  Local rh1#  = ry + rh
  Local x0# = -image.handle_x, x1# = x0 + rw
  Local y0# = -image.handle_y, y1# = y0 + rh
  
  If rw1 > image.width
    x1 = x0 + rw + image.width - rw1
    rw1 = image.width
  EndIf
   
  If rh1 > image.height
    y1 = y0 + rh + image.height - rh1
    rh1 = image.height
  EndIf
?Win32
  If TD3D7ImageFrame(image.frame(frame))
    Local frame:TD3D7ImageFrame = TD3D7ImageFrame(image.frame(frame))
    
    frame.setUV(rx / tw, ry / th, rw1 / tw, rh1 / th)
    frame.Draw x0, y0, x1, y1, x + origin_x, y + origin_y
    frame.setUV(0, 0, image.width / Float(tw), image.height / Float(th))
  Else
?
    Local frame:TGLImageFrame = TGLImageFrame (image.frame(frame))
                
    frame.u0 = rx / tw
    frame.v0 = ry / th
    frame.u1 = rw1 / tw
    frame.v1 = rh1 / th
    
    frame.Draw x0, y0, x1, y1, x + origin_x, y + origin_y
    
    frame.u0 = 0
    frame.v0 = 0
    frame.u1 = image.width / Float(tw)
    frame.v1 = image.height / Float(th)
?Win32
  EndIf
?
  
  Function Pow2Size:Int(n:Int)
    Local ry:Int = 1
    
    While ry:Int < n
      ry :* 2
    Wend
    
    Return ry
  End Function
End Function


Hmmm, not sure I see a problem.
P.S. You might want to use codebox rather than code for long code posts.

You could look at using the stencil buffer.

Do you know where I can find some docu or examples using the stencil buffer and BlitzMax?

Just for other users with the same problem. If you use the following code you can use SetViewPort/DrawImage as you are used to use them, but it will work on all graphic cards:

SuperStrict

Graphics 640,480,0

'Global Viewport coords
Global vx:Float, vy:Float, vw:Float, vh:Float

Local t:TImage = LoadImage ("gfx/mainmenu.png")
Local a:Int = 0
While(Not KeyDown(KEY_ESCAPE))
	a:+1
	Cls
	SetViewport2 200+(Sin(a)*100),200+(Cos(a)*100),100,100
	DrawImage2 t,-300,210	
	Flip
Wend

Function SetViewport2( x:Float, y:Float, width:Float, height:Float ) 
	vx = x
	vy = y
	vw = width
	vh = height
	SetViewport(x,y,width,height)	
End Function

Function DrawImage2(image:TImage, x:Float, y:Float, frame:Int=0)
	Local rx:Float = vx
	Local ry:Float = vy
	Local rw:Float = vw
	Local rh:Float = vh
	 
	rx:-x
	ry:-y

	x = vx 		
	y = vy
	
	If rx<0 Then rw:+rx ; x:-rx ; rx=0
	If ry<0 Then rh:+ry ; y:-ry ; ry=0	
	If rw<0 Then rw=0
	If rh<0 Then rh=0

	ccDrawImageArea(image,x,y,rx,ry,rw,rh,frame)
End Function

Function ccDrawImageArea(image:TImage, x#, y#, rx#, ry#, rw#, rh#, frame:Int=0)
  Local origin_x#, origin_y# ; GetOrigin (origin_x, origin_y)
  Local tw:Int = Pow2Size(image.width)
  Local th:Int = Pow2Size(image.height)
  Local rw1#  = rx + rw
  Local rh1#  = ry + rh
  Local x0# = -image.handle_x, x1# = x0 + rw
  Local y0# = -image.handle_y, y1# = y0 + rh
  
  If rw1 > image.width
    x1 = x0 + rw + image.width - rw1
    rw1 = image.width
  EndIf
   
  If rh1 > image.height
    y1 = y0 + rh + image.height - rh1
    rh1 = image.height
  EndIf
?Win32
  If TD3D7ImageFrame(image.frame(frame))
    Local frame:TD3D7ImageFrame = TD3D7ImageFrame(image.frame(frame))
    
    frame.setUV(rx / tw, ry / th, rw1 / tw, rh1 / th)
    frame.Draw x0, y0, x1, y1, x + origin_x, y + origin_y
    frame.setUV(0, 0, image.width / Float(tw), image.height / Float(th))
  Else
?
    Local frame:TGLImageFrame = TGLImageFrame (image.frame(frame))
                
    frame.u0 = rx / tw
    frame.v0 = ry / th
    frame.u1 = rw1 / tw
    frame.v1 = rh1 / th
    
    frame.Draw x0, y0, x1, y1, x + origin_x, y + origin_y
    
    frame.u0 = 0
    frame.v0 = 0
    frame.u1 = image.width / Float(tw)
    frame.v1 = image.height / Float(th)
?Win32
  EndIf
?
  
  Function Pow2Size:Int(n:Int)
    Local ry:Int = 1
    
    While ry:Int < n
      ry :* 2
    Wend
    
    Return ry
  End Function
End Function


thanks. ANy idea if the code works with drawtext and rectangles/lines etc?

No, it doesn't. It also doesn't work if you use SetRotation() before the drawing command or if the ImageHandle isn't (0,0).

But the problem with the image handle could be corrected easily, with something like this I think (warning, not tested)

rw:+image.handle_x
rh:+image.handle_y


ok thanks.