tags:

views:

626

answers:

2

I apologize for this rather long question.

I am working on an ongoing project where I want to align the links of a chain so that it follows the contours of a Bezier curve. I am currently following the steps below.

  1. Drawing the curve.
  2. Use a display list to create one link of the chain.
  3. Use a FOR loop to repeatedly call a function that calculates the angle between two points on the curve, returns the angle and the axis around which the link should be rotated.
  4. Rotate by the angle "a" and translate to new position, place the link at the new position.

edit:I should also say that the centres of the two half torus must lie on the Bezier curve. Also I am aware that the method i use to draw the torus i tedious, I will use TRIANGLE_FAN or QUAD_STRIP later on to draw the torus in a more efficient way.

While at first glance this logic looks like it would render the chain properly, the end result is not what I had imagined it to be. here is a picture of what the chain looks like.

gold_chain

I read that you have to translate the object to the origin before rotation? would I just call glTranslate(0,0,0) and then follow step 4 from above?

I have included the relevant code from what I have done so far, I would appreciate any suggestions to get me code work properly.

/* this function calculates the angle between two vectors oldPoint and new point contain the x,y,z coordinates of the two points,axisOfRot is used to return the x,y,z coordinates of the rotation axis*/
double getAxisAngle(pointType oldPoint[],
pointType newPoint[],pointType axisOfRot[]){

float tmpPoint[3];   

float normA = 0.0,normB = 0.0,AB = 0.0,angle=0.0;
int i;

axisOfRot->x= oldPoint->y * newPoint->z - oldPoint->z * newPoint->y;
axisOfRot->y= oldPoint->z * newPoint->x - oldPoint->x * newPoint->z;
axisOfRot->z= oldPoint->x * newPoint->y - oldPoint->y * newPoint->x; 


normA=sqrt(oldPoint->x * oldPoint->x + oldPoint->y * oldPoint->y + oldPoint->z * 
oldPoint->z);
normB=sqrt(newPoint->x * newPoint->x + newPoint->y * newPoint->y + newPoint->z *    
newPoint->z);

tmpPoint[0] =  oldPoint->x * newPoint->x;
tmpPoint[1] =  oldPoint->y * newPoint->y;
tmpPoint[2] =  oldPoint->z * newPoint->z;   

for(i=0;i<=2;i++)
 AB+=tmpPoint[i];  

AB /= (normA * normB);  

return angle = (180/PI)*acos(AB);

}

/* this function calculates and returns the next point on the curve give the 4 initial points for the curve, t is the tension of the curve */ void bezierInterpolation(float t,pointType cPoints[], pointType newPoint[]){

newPoint->x = pow(1 - t, 3) * cPoints[0].x +3 * pow(1 - t , 2) * t * cPoints[1].x + 3 
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].x + pow(t, 3) * cPoints[3].x;

newPoint->y = pow(1 - t, 3) * cPoints[0].y +3 * pow(1 - t , 2) * t * cPoints[1].y + 3 
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].y + pow(t, 3) * cPoints[3].y;

newPoint->z = pow(1 - t, 3) * cPoints[0].z +3 * pow(1 - t , 2) * t * cPoints[1].z + 3 
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].z + pow(t, 3) * cPoints[3].z;

}

/* the two lists below are used to create a single link in a chain, I realize that creating a half torus using cylinders is a bad idea, i will use GL_STRIP or TRIANGLE_FAN once I get the alignment right */ torusList=glGenLists(1);

glNewList(torusList,GL_COMPILE);
for (i=0; i<=180; i++)
{
  degInRad = i*DEG2RAD;
  glPushMatrix();
  glTranslatef(cos(degInRad)*radius,sin(degInRad)*radius,0);
  glRotated(90,1,0,0);
  gluCylinder(quadric,Diameter/2,Diameter/2,Height/5,10,10);
  glPopMatrix();      
}

glEndList();

/*! create a list for the link , 2 half torus and 2 columns */

linkList = glGenLists(1);

glNewList(linkList,GL_COMPILE);
glPushMatrix(); glCallList(torusList);
glRotatef(90,1,0,0); glTranslatef(radius,0,0); gluCylinder(quadric,Diameter/2,Diameter/2,Height,10,10);
glTranslatef(-(radius*2),0,0); gluCylinder(quadric,Diameter/2,Diameter/2,Height,10,10);
glTranslatef(radius,0,Height); glRotatef(90,1,0,0); glCallList(torusList);
glPopMatrix(); glEndList();

finally here is the code for creating the three links in the chain

t=0.031; 
bezierInterpolation(t,cPoints,newPoint);  
a=getAxisAngle(oldPoint,newPoint,axisOfRot);
 glTranslatef(newPoint->x,newPoint->y,newPoint->z); 
   glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);     
    glCallList(DLid);
   glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
 glTranslatef(-newPoint->x,-newPoint->y,-newPoint->z);  

oldPoint[0]=newPoint[0];
bezierInterpolation(t+=GAP,cPoints,newPoint);  
a=getAxisAngle(oldPoint,newPoint,axisOfRot);
 glTranslatef(newPoint->x,newPoint->y,newPoint->z); 
  glRotatef(90,0,1,0);
   glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);     
    glCallList(DLid);
   glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
  glRotatef(90,0,1,0);  
 glTranslatef(-newPoint->x,-newPoint->y,-newPoint->z);  

 oldPoint[0]=newPoint[0];
 bezierInterpolation(t+=GAP,cPoints,newPoint);    
  a=getAxisAngle(oldPoint,newPoint,axisOfRot); 
   glTranslatef(newPoint->x,newPoint->y,newPoint->z);    
    glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
     glCallList(DLid);
    glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
   glTranslatef(-newPoint->x,-newPoint->y,newPoint->z);
+1  A: 

I looked at your code and assuming that code performing bezierInterp and axis angle is correct. Based on code, I have following suggestions:

  1. The way you are creating a single link looks very costly. As you are using gluCylinder for 180 times. This will generate a lot of vertices for a small link. You can create a single torus and apply scale such that it appears like a link!

  2. Whenever you do any matrix operation, it is good idea to set the mode before. This is important before doing push and pop. In you display list you have push and pop without setting any mode and neither it is set in caller. This is not good practice and will result in lot of bugs/issues. You can remove push and pop from call list and keep only geometry in it.

  3. You have heard advice suggesting to do translation to origin before rotation as translation * rotation != rotation * translation. So the way you would write your render loop is:

// Set matrix mode

glMatrixMode(GL_MODELVIEW);

for(number of links) {

glLoadIdentity();     // makes model view matrix identity - default location`
glTranslatef(x,y,z);  // Translate to a point on beizer curve
glRotatef(..);        // Rotate link
glCallList(link);     // can be simple torus, only geometry centered at origin
}

Above code renders a link repeated at specified location. Read OpenGL Red book's chapter 3 - Example 3.6 (planetary system) example to understand how you can place each link at different location correctly.

Ketan
My program already contains the MODELVIEW statement which is set in the main() function, As I noted in the comment for that display List"I realize that creating a half torus using cylinders is a bad idea, I will use GL_STRIP or TRIANGLE_FAN once I get the alignment right". Using just a torus will not make the chain realistic, this is why choose to create the chain using two half torus and two cylinders. I have simplified the program taking away the for loop and most of the main() function for the sake of simplicity in this post.thanks for your help.
NeatRobot
So did you figure out correct way to get your translation and rotation work?
Ketan
No, I tried Moving the initial Link to the origin using a glTranslatef(0,0,0); and then calling a glRotate(); followed by a glTranslate() to the original position and then rendering the link but I still have the same problem of the centres of the two half tori not being on the curve.
NeatRobot
It looks like then you have code setup correctly from OpenGL perspective but your value may not be correct.When you are creating initial link, I would assume it is centered at the origin itself! You should not be doing any translation at that point. Render the link created via glCallList and ensure that it is centered at you want. Once done, you can always rotate and translate to the desired location in the loop as described before.Hope you get it fixed!
Ketan
Ketan, you are right about the displaylist being created at the origin, I manually put in the angle of rotation for each link (started with angle=3 and kept incrementing it by 5) this centres the tori on the curve properly. I think my getAxisAngle() is wrong. Trying to debug that currently.
NeatRobot
+1  A: 

One thing to note is that glTranslate function builds on previous translations. I.E. a glTranslatef(0.0,0.0,0.0); won't go to the origin, it will just move the "pen" nowhere. Luckily, the "pen" starts at the origin. if you translate out to 1.0,1.0,1.0 then try a glTranslatef(0.0,0.0,0.0); you will still be drawing at 1.0,1.0,1.0;

Also, you seem to grasp the fact that openGL post-multiplies matricies. To that end, you are correctly "undoing" your matrix operations after a draw. I only see one spot where you could potentially be off here and that is in this statement:

glRotatef(90,0,1,0);
   glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);     
    glCallList(DLid);
   glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
  glRotatef(90,0,1,0);

Here you correctly undo the second rotation, but the first one you seem to rotate even more around the y axis. the very last glRotatef needs to read glRotatef(-90,0,1,0); if you want to be undoing that rotation.

SauceMaster