ViewPorts

BlitzMax Forums/BlitzMax Programming/ViewPorts

TaskMaster(Posted 2009) [#1]
Since BlitzMax's built in viewports fail to work on all hardware, I use Zeke's DrawImageRect to draw only a portion of an image at the edge of a viewport. But, what if you want to draw an image that has been rotated (45 degrees for example)?

Let's say you set a view port at x,y=100,100 and a width,height of 200,200.

Then you wanted to rotate a square image 45 degrees (so it is sitting like a diamond), but you draw it near the edge of the viewport and one of the corners needs to not be drawn?

What is the best way to do this?

I thought about making a copy of the image, calculating the area that needs to be cut off and converting those pixels to transparent, but that is way too slow to do in real time.

Does anybody know a way to do something like this?


_JIM(Posted 2009) [#2]
If you're comfortable with gl commands you can do this easily by calculating your own clipping.

All you have to do is calculate the intersection between a line and a rotated rectangle:



You should calculate those green dots (including UV coordinates) and insert vertices there. Then, just draw the new poly.

In order to make this speedy, you should first test every image's bounding box against the viewport borders.

Here's a little type that I wrote that should help you with the calculations:

Type TTransform2D
	Field Matrix:Double[2, 2]
	Field Rotation:Float = 0
	Field ScaleX:Float = 1
	Field ScaleY:Float = 1
	
	Method Init(sx:Float = 1, sy:Float = 1, rot:Float = 0)
		Self.ScaleX = sx
		Self.ScaleY = sy
		Self.Rotation = rot
		Self.Update()
	EndMethod

	Method Update()
		Matrix[0, 0] = Cos(Rotation)
		Matrix[1, 0] = - Sin(Rotation)
		Matrix[0, 1] = Sin(Rotation)
		Matrix[1, 1] = Cos(Rotation)
	End Method

	Method Transform(x:Float var, y:Float var)
		Local xx:Float
		Local yy:Float
		
		x = x * ScaleX
		y = y * ScaleY
		
		xx = x * Matrix[0, 0] + y * Matrix[1, 0]
		yy = x * Matrix[0, 1] + y * Matrix[1, 1]
		
		x = xx
		y = yy
	End Method
	
	Method ITransform(x:Float var, y:Float var)
		Local xx:Float
		Local yy:Float
		
		Self.Rotation = -Self.Rotation
		Self.Update()
		
		xx = x * Matrix[0, 0] + y * Matrix[1, 0]
		yy = x * Matrix[0, 1] + y * Matrix[1, 1]
		
		Self.Rotation = -Self.Rotation
		Self.Update()
		
		xx = xx / ScaleX
		yy = yy / ScaleY
		
		x = xx
		y = yy
	End Method
End Type

Function LineIntersect(l1:Float ptr, l2:Float ptr, x_out:Float var, y_out:Float var)
	Local D1:Float = (l1[0] * l1[3] - l1[1] * l1[2])
	Local D2:Float = (l2[0] * l2[3] - l2[1] * l2[2])
	Local D3:Float = (l1[0] - l1[2]) * (l2[1] - l2[3]) - (l1[1] - l1[3]) * (l2[0] - l2[2])
	x_out = (D1 * (l2[0] - l2[2]) - D2 * (l1[0] - l1[2])) / D3
	y_out = (D1 * (l2[1] - l2[3]) - D2 * (l1[1] - l1[3])) / D3
End Function



And a little example:

Graphics 800, 600

Global image_width:Int = 300
Global image_height:Int = 200

'declare transform helper
Local tr:TTransform2D = New TTransform2D

'corners
Local cx:Float[4]
Local cy:Float[4]

While Not (KeyHit(KEY_ESCAPE) Or AppTerminate())
	
	tr.Init(1, 1, MilliSecs() / 20.0)
	
	'clockwise
	cx[0] = - image_width / 2;cy[0] = - image_height / 2
	cx[1] = image_width / 2;cy[1] = - image_height / 2
	cx[2] = image_width / 2;cy[2] = image_height / 2
	cx[3] = - image_width / 2;cy[3] = image_height / 2
	
	'transform points
	tr.Transform(cx[0], cy[0])
	tr.Transform(cx[1], cy[1])
	tr.Transform(cx[2], cy[2])
	tr.Transform(cx[3], cy[3])
	
	'draw stuff
	Cls
	SetOrigin 400, 300
	DrawLine(cx[0], cy[0], cx[1], cy[1])
	DrawLine(cx[1], cy[1], cx[2], cy[2])
	DrawLine(cx[2], cy[2], cx[3], cy[3])
	DrawLine(cx[3], cy[3], cx[0], cy[0])
	
	DrawText(cx[0] + " , " + cy[0], cx[0], cy[0])
	DrawText(cx[1] + " , " + cy[1], cx[1], cy[1])
	DrawText(cx[2] + " , " + cy[2], cx[2], cy[2])
	DrawText(cx[3] + " , " + cy[3], cx[3], cy[3])
	
	'SetOrigin 0, 0
	
	Local l1:Float[] = [0.0, 0.0, 800.0, 600.0]
	Local l2:Float[] = [0.0, 600.0, 800.0, 0.0]
	
	Local px:Float, py:Float
	
	'update lines
	l1 = [100.0, -300.0, 100.0, 300.0] 'viewport border
	DrawLine l1[0], l1[1], l1[2], l1[3]	'draw it
	
	SetColor 255, 0, 0
	
	l2 = [cx[0], cy[0], cx[1], cy[1] ]
	LineIntersect(l1, l2, px, py) ;DrawOval(px - 4, py - 4, 8, 8)
	
	l2 = [cx[1], cy[1], cx[2], cy[2] ]
	LineIntersect(l1, l2, px, py) ;DrawOval(px - 4, py - 4, 8, 8)
	
	l2 = [cx[2], cy[2], cx[3], cy[3] ]
	LineIntersect(l1, l2, px, py) ;DrawOval(px - 4, py - 4, 8, 8)
	
	l2 = [cx[3], cy[3], cx[0], cy[0] ]
	LineIntersect(l1, l2, px, py) ;DrawOval(px - 4, py - 4, 8, 8)
	
	SetColor 255, 255, 255
	
	Flip
Wend


You could probably tidy this up a bit and get something better.

Hope this helps ;)

EDIT: Updated the code to include intersection as well. You can then transform the intersection points into the image's space using

tr.ITransform(px, py)


EDIT2:

There's also easier but less fun methods like:

Draw viewport 1, grab pixmap1, cls, draw viewport 2, grab pixmap2, cls. Draw pixmap1. Draw pixmap2.

Or use FBOs (even less compatibility than viewports :) )


TaskMaster(Posted 2009) [#3]
Thanks a lot. I appreciate this.


_JIM(Posted 2009) [#4]
No problem. If you need any more help, I'd be happy to provide it (if I can :) )


ImaginaryHuman(Posted 2009) [#5]
Where is the information about viewports not working properly on all hardware?


Fry Crayola(Posted 2009) [#6]
I'm not sure where the info is, but the problem is that DirectX 7 doesn't support scissor testing, but makes viewports using clip panes.

Some cards only support 2 clip panes, a viewport requires 4, so two edges of the viewport get ignored.


beanage(Posted 2009) [#7]
Is that the same with opengl?


TaskMaster(Posted 2009) [#8]
Viewports not working has been discussed many times in these forums. I do not know the specifics, but since it is hit and miss, I found that it is just easier to not use them.

So, using _JIM's code, you can find the area that should be drawn (or not drawn). Without using/creating a pixmap, how would you go about only drawing that portion of the image? Creating and then drawing a pixmap every frame is not really an option, as it is pretty slow to do.

With drawing a portion of a non-rotated image, you can just adjust the u,v coordinates of the image to draw a portion of it, but this is still square. Is it possible to adjust the u,v system to draw a 5-sided poly (or more if it is clipped on two or more sides). Are there more coordinates than that, or is there a hidden array of vertices that can be modified?

I really do like the example code and appreciate the effort it take to do it, but all that work isn't really necessary. If somebody could just point me in the right direction, what command to use, or how to modify the drawing area of a blitzmax TImage into a poly shape. Maybe a openGL and/or directx function that could be searched on google. :)

Edit: By the way, I just ran _JIM's code, very cool!!!


TaskMaster(Posted 2009) [#9]
I figured out how to draw the image as a polygon using OpenGL.

Now I am working on how to draw it using DirectX.

To do it in OpenGL, you have to edit TGLImageFrame.Draw to draw as a GL_POLYGON instead of a GL_QUAD. Then yo just need to create the correct vertices and texture coords.

Now I am starting on the DirectX version. It loks like I will just need ot extend the xyzuv[24] array to include more vertices, as it already draws as a triangle strip.

Looks like the xyzuv[24] array is 4 sets of 6 variables. i think they are:
xyzuv[0]=x
xyzuv[1]=y
xyzuv[2]=z
xyzuv[3]=color
xyzuv[4]=u
xyzuv[5]=v

Then it just repeats 4 times, so to make a poly, you just need to add another set for each additional vertices. Adding them in Clockwise order.

Anybody know what xyzvu[3] is used for?

Edit: Figured it out. It is a color for the vertex.

Should be easy from here on out.


Fry Crayola(Posted 2009) [#10]
@Joseph

No, OpenGL supports scissor tests, so viewports ought to work fine.


ImaginaryHuman(Posted 2009) [#11]
Yah on OpenGL I haven't seen/heard of any issue about viewport scissor tests. This seems to be only a directx problem.


TaskMaster(Posted 2009) [#12]
Then you run into a similar problem if you use only OpenGL. The machines that don't support DirectX clipping are probably a subset of the machines that do not have OpenGL support. Machines that suffer form this are usually old hardware and/or old drivers.


_JIM(Posted 2009) [#13]
I just thought of an interesting side effect of my method: your viewports could have any polygonal shape... honeycomb vieports, angled splitscreen... this could be cool :)

Glad to see you're making progress! I can't wait to see this working.


ImaginaryHuman(Posted 2009) [#14]
Can you use the stencil buffer instead?


TaskMaster(Posted 2009) [#15]
Exaclty _JIM. Basically, you would be able to draw any shape you want. You could have view ports that are rotated, have more than 4 sides, etc...

I am just starting to get into this sort of stuff, ImaginaryHuman, I have no idea how to use a stencil buffer. :(