Drawing lines in OpenGL

(with the help of GLKit)

There are many ways to do this, its often used in paint programs.

Philip Rideout’s iPhone 3D Programming has a way to do it in C++, but its hard to abstract the method from the code.

There is a nice article written here which gives the method I used.
In my case the lines are temporary exhaust lines – so I don’t need to bake the lines into a texture as the above solution does.

The method just draws line segments from point to point as rectangles. Each line segment is a sprite which is rendered which ever way you render your OpenGL sprites.

Here is a picture of a ship emitting an exhaust – that uses the code below – the ship is moving very fast – so the sharp angles don’t matter so much.

swoop pic

And below is the code I wrote to do the lines:

//requires GLKMath
//A sprite is drawn for every line segment

//in decision method somewhere

paintLineFrame = 30; //draw a segment for the next 30 update methods


// in ship sprite update method
//a section of the line is drawn 
 if (paintLineFrame > 0)
 {
//paint from previous point to current point
     [self paintLineSpriteAtPoint:previousLocation toPoint:location width:8];
 }



 -(void)paintLineSpriteAtPoint:(CGPoint)point1 toPoint:(CGPoint)point2 width:(int)width
 {
//don't draw anything if the sprite has not moved
     if (point1.x == point2.x && point1.y == point2.y)
     {
         return;
     }
//get the sprite from the pool of line sprites
     Sprite *lineSprite = [self.gameController.resourceController dequeueLineSegment];
     if (lineSprite)
     {
//setup
         lineSprite->spriteType = kLineSegment;
//set the appropriate rainbow texture
         [lineSprite reTexRect:lineTextureRect];
//set how long you want this segment of the line to hang about
         lineSprite->lineSegmentFrame = self.gameController->slowFrameRate ? 15 : 30;
//put a bit of alpha on it
         [lineSprite reColourR:255 G:255 B:255 A:125];
     }
//still do the calcs if no line sprite
     GLKVector2 v1 = PTV(point1); //PTV: point to vector macro
     GLKVector2 v2 = PTV(point2);

     GLKVector2 A;
     GLKVector2 B;
     if (firstLineSegment)
     {
//the first painted segment goes from the previous point to the current point - and we need to calculate the edges of the rectangle

         firstLineSegment = NO;
         GLKVector2 normal = GLKVector2Normalize(GLKVector2Subtract(v1, v2)); // find the vector in the direction from point 1 to point 2 and normalise it
         GLKVector2 rNormal = GLKVector2Normalize(GLKVector2Subtract(v2, v1)); // find the vector in the direction from point 2 to point 1 and normalise it

         GLKVector2 nA = GLKVector2Make(-normal.y, +normal.x); //find the perpendicular vector of the vector from point1 to point 2 (from origin 0,0)
         GLKVector2 nB =  GLKVector2Make(normal.y, -normal.x); //find the other perpendicular vector of the vector from point1 to point 2

         GLKVector2 nC = GLKVector2Make(-rNormal.y, +rNormal.x);  //find the perpendicular vector of the vector from point 2 to point 1
         GLKVector2 nD = GLKVector2Make(rNormal.y, -rNormal.x); //find the other perpendicular vector of the vector from point 2 to point 1

         A = GLKVector2Add(v1, GLKVector2MultiplyScalar(nA, width)); //multiply these perpendicular vectors by the chosen width 
         B = GLKVector2Add(v1, GLKVector2MultiplyScalar(nB, width));

         C = GLKVector2Add(v2, GLKVector2MultiplyScalar(nC, width));
         D = GLKVector2Add(v2, GLKVector2MultiplyScalar(nD, width));
//et voila four corners of the rect (A,B,C,D)

     }
     else
     {
//for other segments we want to reuse the previously calculated C and D and make them A and B.
//so only need to calculate the new C and D.
         GLKVector2 rNormal = GLKVector2Normalize(GLKVector2Subtract(v2, v1));
         GLKVector2 nC = GLKVector2Make(-rNormal.y, +rNormal.x);
         GLKVector2 nD = GLKVector2Make(rNormal.y, -rNormal.x);

         A = D;
         B = C;

         C = GLKVector2Add(v2, GLKVector2MultiplyScalar(nC, width));
         D = GLKVector2Add(v2, GLKVector2MultiplyScalar(nD, width));
     }


     if (lineSprite)
     {
//update your sprites coordinates here - eg convert A,B,C,D to a CGRect or whatever
     }
     paintLineFrame--;

 }

Extras

    Here are some additions that might be nice:

  • have a different start and end textures than the main line texture for the sprite,
  • if the distance between point 1 and point 2 is large split the line up into two or more segments (you’ll have to calculate the path using the velocity),
  • make the line thicker and thinner depending on velocity.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>