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: