Drawing Ropes in OpenGL

Since OpenGL Core does not have a nice line-draw function with possibilities for texturing, line-width or curves, here is a small example to draw softbody ropes from the Bullet-Physic Engine. I’m using my YAOGL library to access the OpenGL State-Machine.
A rope (or string) thereby consists of several segments. A rope with one segment is a straight line.

The Rope Segment

There are several possibilities to create a rope segment. A simple form could be a 2D Billboard. A 2D Billboard consists of a plane which is alway oriented to the viewers direction. But even for small visual elements these depthless objects look ugly.

Therefore I’m using a 3D model, which is a regular cylinder with unit length.

int starStringImpId = [world createImpersonator:@"Rope"];
YAImpersonator* starStringImp = [world getImpersonator:starStringImpId];

The cylinder was created with Blender and exported to a file with the name Rope.y3d.

The Length of the Rope

To calculate the length of the rope you need its start and end position:

YAVector3f* positionFrom = star.translation;
YAVector3f* positionTo = [[YAVector3f alloc] initCopy: positionFrom];
positionTo.y = 10;

The rope starts at the current position of the star and ends at the ceiling which has a hight of 10 units.
In this case the length of the rope is easily calculate by positionTo.y - positionFrom.y. For a general approach you would use vector-subtraction or simply:

float stringLength = [positionFrom distanceTo: positionTo];

The length of the rope has now to be mapped to the size of the segment. Since the 3D Model “Rope” has unit length this is:

starStringImp.size.y = stringLength / 2.f;

Positioning the Rope

The center of the rope segment has to be centered between positionFrom (the star) and positionTo (the ceiling):

        YAVector3f* position = positionTo;
        [position subVector:positionFrom];
        [position mulScalar:.5f];
        [position addVector:positionFrom];
        [[starStringImp translation] setVector:position];

The positionTo vector is mapped according to the world-origin (0,0,0), halved and repositioned above the star-object.

Calculating the Segment Direction

The direction of the segment was already calculated during the positioning. It is the normalized vector from the origin.

        YAVector3f* direction = [[YAVector3f alloc] initCopy:position];
        [direction subVector:positionFrom];
        [direction normalize];

To calculate the object rotation you could do something like this to get the angles around the world X-Axis and Z-Axis:

        float rotZ = ToDegree(acosf([direction dotVector: vZAxis]));
        float rotX = ToDegree(acosf([direction dotVector: vXAxis]));

Only two angles are necessary to calculate the rotation of an object. But mapping to or from euler vectors is alway a bad idea. If the possibility exists, it is advisable to use quaternions.

    starStringImp.useQuaternionRotation = YES;
    YAQuaternion* quat = [[YAQuaternion alloc]initEulerDeg:0 pitch:rotZ roll: rotX];
    [starStringImp setRotationQuaternion:quat];

Or even faster with:

    YAQuaternion* quat = [[YAQuaternion alloc]initVector: direction];

The Recursion

As mentioned above, a rope typically consists of several segments. Instead of the complete distance you calculate each segment separately. In YAOGL you can do this with:

YABulletEngineTranslator* physics = [[YABulletEngineTranslator alloc] initIn:world];
NSArray* ropes = [physics ropeDescriptions];

Each rope element in the ropes array holds all segments of one rope. Here is an example of the presented algorithm:

Leave a Reply

Your email address will not be published.