Creating Ring

BlitzMax Forums/OpenGL Module/Creating Ring

Gavin Beard(Posted 2007) [#1]
Hi all,

I;ve been trying to create a ring of textured quads but to be honest dont seem to be able to do it correctly (mostly down to my maths skills) i cant quiet write the code to make it rotate and join properly at th eend.

and example of what i was looking to do is :



same as always i'm not asking anyone to write the code for me but a gentle nudge in right direction :-)

thanks


ImaginaryHuman(Posted 2007) [#2]
It wont look quite as circular as you have drawn it since the edges of the quads will be straight not curved.

You need to decide whether you want to use a GL_QUAD_STRIP or several GL_QUADS. It's more efficient to do the strip and then you can be sure that the points match up exactly with no gaps.

You need to start out by calculating the first two points *outside* of your loop. Then for each loop iteration you need to calculate the next two points and add those onto the quad strip. You can do 1 less than the number of iterations to add all the quads and then at the end do the last 2 points outside of the loop using the coordinates for the first two points.

Something like this (not tested and you need to incorporate the correct texture coordinates for each quad. It would be a good idea to have all the textures needed within one Image otherwise you'll have to switch textures after each quad which means you have to glEnd() before you can switch (ie no strips). Also note texture coords are in the range 0 to 1.0, not pixels.
Cls
Local Angle:Float=0
Local Quads:Int=16
Local AngelStep:Float=360.0/Quads
Local FirstX:Float=100*Cos(Angle)
Local FirstY:Float=100*Sin(Angle)
Local SecondX:Float=120*Cos(Angle)
Local SecondY:Float=120*Sin(Angle)
glBegin (GL_QUAD_STRIP)
   glTexCoord2f(textopleftx,textoplefty)
   glColor4ub($255,$255,$255,$255)   'could do this for each vertex
   glVertex2f(FirstX,FirstY)
   glTexCoord2f(bottomleftx,bottomlefty)
   glVertex2f(SecondX,SecondY)
   For Local Quad:Int=0 to Quads-2
      Angle:+AngleStep
      Local NextX1:Float=100*Cos(Angle)
      Local NextY1:Float=100*Sin(Angle)
      Local NextX2:Float=100*Cos(Angle)
      Local NextY2:Float=100*Sin(Angle)
      glTexCoord2f(bottomrightx,bottomrighty)
      glVertex2f(NextX1,NextX2)
      glTexCoord2f(toprightx,toprighty)
      glVertex2f(NextX2,NextY2)
   Next
   glTexCoord2f(bottomrightx,bottomrighty)
   glVertex2f(FirstX,FirstY)
   glTexCoord2f(toprightx,toprighty)
   glVertex2f(SecondX,SecondY)
glEnd()
Flip()

This isn't quite all that you'll need. You have to have an image containing a horizontal strip of images, one for each segment. You also have to calculate the correct texture coords for each pair of vertices. As the loop proceeds you have to increment the coordinates in X by 1.0/Quads so that you step across the image and so that where the previous image left off the new quad begins, as remember the first 2 vertices are shared with the previous 2. I haven't written that part into the code but it is needed.

Alternatively use GL_QUADS and just do a separate quad for each segment and then you can make the texture coordinates whatever you want within the image, perhaps pulling from an array or based on a calculation. Depends how you want to use the image. Just remember the texture's size is 0..1 x 0..1 so you have to divide it up.


Gavin Beard(Posted 2007) [#3]
thats awsome, many thanks, this should help no end, i'll prob try the quad strip and individual quads and see if big impact on performace. shouldnt think there would be tho


Gavin Beard(Posted 2007) [#4]
hey ya,

gone to have a look at integrating this code into some of my code with modifications, only bit that seems to trouble me is

Local NextX1:Float=100*Cos(Angle)
Local NextY1:Float=100*Sin(Angle)
Local NextX2:Float=100*Cos(Angle)
Local NextY2:Float=100*Sin(Angle)
glTexCoord2f(bottomrightx,bottomrighty)
glVertex2f(NextX1,NextX2)
glTexCoord2f(toprightx,toprighty)
glVertex2f(NextX2,NextY2)

surely nextx1,nexty1 should not be the same as nextx2,nexty2 as in above code?

here is sample code i have put together:

SuperStrict
SetGraphicsDriver GLGraphicsDriver()
GLGraphics 640,480

glViewport(0,0,640,480);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

gluPerspective(45.0,640.0/480,0.1,100.0);
glMatrixMode(GL_MODELVIEW);				
glLoadIdentity();	

Global Angle:Float=0
Global Quads:Int=16
Global AngleStep:Float=360.0/Quads

Repeat
	angle=0;
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	glLoadIdentity()
	glTranslatef(0,0.0,-6.0);
	glBegin(GL_QUAD_STRIP);
		glColor3f(0.0,0.0,1.0);
		glVertex2f(0,0)
		glVertex2f(0,1)
		For Local x:Int=0 To 15
		Angle:+AngleStep
		    Local NextX1:Float=2*Cos(Angle)
     		Local NextY1:Float=2*Sin(Angle)
      		Local NextX2:Float=2*Cos(Angle)
      		Local NextY2:Float=2*Sin(Angle)
'DebugStop()
			glVertex2f(nextx1,nexty1);
			glVertex2f(nexty2,nexty2);
		Next
		glEnd();


Flip()
Until KeyDown(key_escape)


but as u see from runing the code its quiet a strange shape it creates


klepto2(Posted 2007) [#5]
SuperStrict
SetGraphicsDriver GLGraphicsDriver()
GLGraphics 800,600

glViewport(0,0,800,600);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

gluPerspective(45.0,800.0/600,0.1,100.0);
glMatrixMode(GL_MODELVIEW);				
glLoadIdentity();	

Local Ring:TRing = TRing.Create(0 , 0 , 1 , .5 , 18)


Repeat
'	angle=0;
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	glLoadIdentity()
	glTranslatef(0,0.0,-6.0);
	 
	glBegin(GL_QUADS);
	glColor3f(0.0 , 0.0 , 1.0) ;
	Ring.Draw()
	glEnd();


Flip()
Until KeyDown(key_escape)

Type TQuad
	Field Points:Float[8]
	
	Function Create:TQuad(x1:Float , y1:Float , x2:Float , y2:Float , x3:Float , y3:Float , x4:Float , y4:Float)
		Local Q:TQuad = New TQuad
		
		Q.Points = [x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4]
		
		Return Q
	End Function
	
	Method Draw()
		DrawPoly(Points)
	End Method
	
	Method Render()
		glvertex2f(Points[0], Points[1])
		glvertex2f(Points[2], Points[3])
		glvertex2f(Points[4], Points[5])
		glvertex2f(Points[6] , Points[7])
	End Method
	
End Type

Type TRing
	Field Segments:TList = New TList
	
	Function Create:TRing(X:Float , Y:Float, inner:Float,thickness:Float,seg:Int = 32)
		Local R:TRing = New TRing
		Local Stepper:Float = 360.0/seg
		Local Angle:Float = 0
		Repeat 
			Local x1:Float
			Local x2:Float
			Local x3:Float
			Local x4:Float
			
			Local y1:Float
			Local y2:Float
			Local y3:Float
			Local y4:Float
			
			x1 = X + Sin(Angle) * inner
			y1 = Y - Cos(Angle) * inner
			
			x4 = X + Sin(Angle) * (inner + thickness) 
			y4 = Y - Cos(Angle) * (inner + thickness) 
			
			angle:+ Stepper
			
			x2 = X + Sin(Angle) * inner
			y2 = Y - Cos(Angle) * inner
			
			x3 = X + Sin(Angle) * (inner + thickness) 
			y3 = Y - Cos(Angle) * (inner + thickness) 
			
			R.Segments.Addlast(TQuad.Create(x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4) )

		Until Angle >= 360
		
		Return R
	End Function
	
	Method Draw()
		For Local Q:TQuad = EachIn segments
			glPolygonMode(GL_FRONT,GL_LINE)
			Q.Render()
			glPolygonMode(GL_FRONT,GL_FILL)
		Next
	End Method
End Type


something small I have put together from oyur above code.


ImaginaryHuman(Posted 2007) [#6]
Hi Gavin,

You left out a couple of bits. The first two vertices have to be calculated with Cos and Sin just like all the others. Also the second line of the inner loop that adds the two next vertices, you had nexty2,nexty2 instead of nextx2,nexty2.
The following works:

SuperStrict
SetGraphicsDriver GLGraphicsDriver()
GLGraphics 640,480

glViewport(0,0,640,480)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()

gluPerspective(45.0,640.0/480,0.1,100.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()

Global Angle:Float=0
Global Quads:Int=16
Global AngleStep:Float=360.0/Quads

Repeat
        Angle=0
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()
        glTranslatef(0,0.0,-6.0)
        glBegin(GL_QUAD_STRIP)
                glColor4f(0.0,0.0,1.0,1.0)
                glVertex2f(2*Cos(Angle),2*Sin(Angle))
                glVertex2f(2.5*Cos(Angle),2.5*Sin(Angle))
                For Local x:Int=0 To 15
                    Angle:+AngleStep
                    glVertex2f(2*Cos(Angle),2*Sin(Angle))
                    glVertex2f(2.5*Cos(Angle),2.5*Sin(Angle))
                Next
         glEnd()
		Flip()
Until KeyDown(key_escape)

I've assumed that your inner radius is 2 and your outer radius is 2.5, but you can adjust that. If you do, make sure you adjust it for the first two vertices also.

The next thing you need to do is texture it. For this you need a few things. You have to have a texture uploaded (see glTexImage2D). You have to enable texturing with glEnable(GL_TEXTURE_2D). You have to bind (select) the current texture with glBindTexture(). Then you have to specify texture coordinates *before* each vertex that you define. As mentioned previously, you'll have to think about whether you plan to share sets of vertices in a strip or have separate GL_QUADS. With the strip the next pair of corners are shared with the previous pair so your texture image has to be a horizontal strip and as you go through the loop you have to increment the texture coordinates to move across the image to the next segment. I recommend putting all images needed into a single composite image like as an anim strip otherwise you'll have to glBindImage() for every quad you draw, which is an overhead.


Gavin Beard(Posted 2007) [#7]
Thank you, u r a saviour :D
gonna have a look at texturing a bit later, if i get any issues i'll post :)


ImaginaryHuman(Posted 2007) [#8]
Try using Local tex:Int=GLTexFromPixmap(mypixmap)

to get the texture handle for a texture that you load with LoadPixmap(url,false).

Then you just have to switch on texturing, bind the texture, and define the texture coords without having to think about the complicated stuff of creating a texture.


Gavin Beard(Posted 2007) [#9]
Hey all,

tried the above codes supplied which i have got working and textured, the problem i have is with the textures. the textures come out...well kinda distorted:



code:

SuperStrict
SetGraphicsDriver GLGraphicsDriver()
GLGraphics 640,480

glViewport(0,0,640,480)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()

gluPerspective(45.0,640.0/480,0.1,100.0)
glMatrixMode(GL_MODELVIEW)
glEnable(GL_TEXTURE_2D)
glLoadIdentity()

Global Angle:Float=0
Global Quads:Int=16
Global AngleStep:Float=360.0/Quads
Global DiscAngle:Float=0
Global tileTexture:TPixmap=LoadPixmap("1.png");Global TileTex%;Global txPos:Float;

TileTex = GLTexFromPixmap(tileTexture,True)

Repeat
        Angle=0
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()
        glTranslatef(0,0.0,-5.0)
		glRotatef(-60,1,0,0)
		glBindTexture(GL_TEXTURE_2D,TileTex)
		glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR)
		glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT)
		glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT)
		'glRotatef(DiscAngle,0,0,1)
        glBegin(GL_QUAD_STRIP)
				glTexCoord2f(0.0,0.0);
                glVertex2f(1.5*Cos(Angle),1.5*Sin(Angle))
				glTexCoord2f(0,1);
                glVertex2f(2.5*Cos(Angle),2.5*Sin(Angle))
				txPos=1;
                For Local x:Int=0 To Quads-1
                    Angle:+AngleStep
					glTexCoord2f(0,txPos);
                    glVertex2f(1.5*Cos(Angle),1.5*Sin(Angle))
					glTexCoord2f(1,txPos);
                    glVertex2f(2.5*Cos(Angle),2.5*Sin(Angle))
					If txPos = 1 Then
						txPos = 0
					Else
						txPos=1
					EndIf
                Next
         glEnd()
		 If KeyDown(KEY_R)
			DiscAngle:+2
		EndIf

		Flip()
		
Until KeyDown(key_escape)



also get same issue with:


SuperStrict
SetGraphicsDriver GLGraphicsDriver()
GLGraphics 800,600
Global tileTexture:TPixmap=LoadPixmap("1.png");Global TileTex%=GLTexFromPixmap(tileTexture);Global txPos:Float;
glViewport(0,0,800,600);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

gluPerspective(45.0,800.0/600,0.1,100.0);
glMatrixMode(GL_MODELVIEW);		
glEnable(GL_TEXTURE_2D)		
glLoadIdentity();	

Local Ring:TRing = TRing.Create(0 , 0 , 1 , 0.5 , 18)


Repeat
'	angle=0;
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
	glLoadIdentity()
	glTranslatef(0,0,-4);
	glBindTexture(GL_TEXTURE_2D,TileTex)
	glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR)
	glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT)
	glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT)
	glBegin(GL_QUADS);
		Ring.Draw()
	glEnd();


Flip()
Until KeyDown(key_escape)

Type TQuad
	Field Points:Float[8]
	
	Function Create:TQuad(x1:Float , y1:Float , x2:Float , y2:Float , x3:Float , y3:Float , x4:Float , y4:Float)
		Local Q:TQuad = New TQuaD
		Q.Points = [x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4]
		
		Return Q
	End Function
	
	Method Draw()
		DrawPoly(Points)
	End Method
	
	Method Render()

		glTexCoord2f(0,0);glvertex2f(Points[0], Points[1])
		glTexCoord2f(1,0);glvertex2f(Points[2], Points[3])
		glTexCoord2f(1,1);glvertex2f(Points[4], Points[5])
		glTexCoord2f(0,1);glvertex2f(Points[6] , Points[7])
	End Method
	
End Type

Type TRing
	Field Segments:TList = New TList
	
	Function Create:TRing(X:Float , Y:Float, inner:Float,thickness:Float,seg:Int = 32)
		Local R:TRing = New TRing
		Local Stepper:Float = 360.0/seg
		Local Angle:Float = 0
		Repeat 
			Local x1:Float
			Local x2:Float
			Local x3:Float
			Local x4:Float
			
			Local y1:Float
			Local y2:Float
			Local y3:Float
			Local y4:Float
			
			x1 = X + Sin(Angle) * inner
			y1 = Y - Cos(Angle) * inner
			
			x4 = X + Sin(Angle) * (inner + thickness) 
			y4 = Y - Cos(Angle) * (inner + thickness) 
			
			angle:+ Stepper
			
			x2 = X + Sin(Angle) * inner
			y2 = Y - Cos(Angle) * inner
			
			x3 = X + Sin(Angle) * (inner + thickness) 
			y3 = Y - Cos(Angle) * (inner + thickness) 
			
			R.Segments.Addlast(TQuad.Create(x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4) )

		Until Angle >= 360
		
		Return R
	End Function
	
	Method Draw()
		For Local Q:TQuad = EachIn segments
			glPolygonMode(GL_FRONT,GL_LINE)
			Q.Render()
			glPolygonMode(GL_FRONT,GL_FILL)
		Next
	End Method
End Type



it seems to be where the quad is made of tri's and the join of the tri's is what is causing the issue, but i cant seem to find a way around it?

any hints :)


ImaginaryHuman(Posted 2007) [#10]
I had to think about this for a while but then it occurred to me.

You are right that it is to do with the fact that the quad is made of triangles, in that if you draw a diagonal line between opposite corners of each quad you'll see where the dividing line is of the triangle. The interpolation of the texture image is stretched across the triangle evenly. The problem is, your texture coordinates are a square but you are mapping the texture onto a non-square shape. The texture therefore HAS to stretch to fill the space and will warp in the way you are seeing.

If your quads were perfect squares you would not see the warping, because the texture coordinates then would match exactly the shape on the screen.
But because you have these trapezium shaped objects, GL has no choice but to stretch the texture over the shape in whatever way it can. It no longer directly maps to the shape so each triangle has to stretch out the texture. Then when you see both triangles next to each other they each have their own stretching.

To properly fill a trapezium shape, your texture coordinates must also be in the shape of a trapezium. While I said before that you step across your image horizontally, I'm sorry but in hindsight that won't work. You will have to store your `tiles` in the same shape as your disc, ie in a circle, within a texture, and as you step around the circle to define the vertices, you use basically the same position (converted from pixel coordinates to the 0..1 texel coordinates) to refer to where within the texture you are pulling from.

Alternatively you can use GL_QUADS, and store each segment as a trapezium shape (ie the shape it will be when you draw it), like tiles with gaps between (like a sprite-sheet). Then you can reference the exact corners of each trapezium in the texture and it will then map onto the object drawn without stretching.


Gavin Beard(Posted 2007) [#11]
Thanks

thought it was gonna go something like that but didnt have time to try so thought i'd ask :)

its turning into a bigger pain than i had, i'm essentially trying to create a disc like playing board that players need to traverse, maybe i should change my approach :-)

will give texturing another shot first tho :)