Render Worm Body (Tube)

I have been trying to figure out how to get the perpendicular to a normal vector so that I can draw the body of a worm. Not sure if I am doing this in the best way, so any feedback or suggestions would be great.

This is how the worm is supposed to look.
[attachment=1:6z4twkwc]Picture 4.png[/attachment:6z4twkwc]

But when I begin moving the worm around, it looks like this:
[attachment=0:6z4twkwc]Picture 2.png[/attachment:6z4twkwc]

Code

  
  
void Worm::draw() {  
	//cout << "nmPoints: " << spine[0].loc.x<<", "<< spine[0].loc.y<<", "<< spine[0].loc.z << endl;  
	numPts			= spine.size();  
	float minMid	= .2 * numPts;  
	float maxMid	= .5 * numPts;  
	//float angleInc	= (PI * 2) / 30;  
	float angleInc	= (PI * 2) / 30;  
	for (int i = 0; i < numPts; i++) {  
		ofxVec3f dirNormal;  
		if (i > 0 && i < numPts - 1) {  
			dirNormal = spine[i-1].loc - spine[i+1].loc;  
		} else if (i == numPts - 1) {  
			dirNormal = spine[i-1].loc - spine[i].loc;  
		} else if (i == 0) {  
			dirNormal = spine[i].loc - spine[i+1].loc;  
		}  
		dirNormal.normalize();  
		  
		glPushMatrix();  
			glTranslatef(spine[i].loc.x, spine[i].loc.y, spine[i].loc.z );  
			ofSetColor(255, 0, 0);  
			ofLine(0, 0, dirNormal.x, dirNormal.y);  
			ofSetColor(0, 0, 0);	  
			ofCircle(0, 0, 2);  
		glPopMatrix();  
		  
		float angle = 0;  
		for(int j = 0; j < 30; j++) {  
			float distX = 1;  
			  
			 if (i <= minMid) {  
				distX = (float)i / minMid;  
			} else if (i >= maxMid) {  
				distX = 1- ( ((float)i - maxMid) / maxMid);  
			}  
			  
			distX *= 70.f;  
			ofxVec3f dist(distX, 0, 0);  
			if (timePtsDiff.size() > 1) {  
				dist += timePtsDiff[i];  
			}  
			  
			dist.rotateRad( angle, dirNormal );  
			  
			ofSetColor(150, 150, 150);  
			glPushMatrix();  
				glTranslatef(spine[i].loc.x + dist.x, spine[i].loc.y + dist.y, spine[i].loc.z + dist.z );  
				ofCircle(0, 0, 2);  
			glPopMatrix();  
			angle += angleInc;  
		}  
	}  
}  
  

![](http://forum.openframeworks.cc/uploads/default/583/Picture 4.png)

![](http://forum.openframeworks.cc/uploads/default/615/Picture 2.png)

1 Like

i might be misunderstanding - but be careful here.

in 3d there isn’t really such a thing as a perpendicular vector to another vector. instead, a normal is only perpendicular to a plane. once you have the plane you can do 2d translations on that. but there’s no such thing as a vector perpendicular to another vector in 3d.

I got the cross vector with the up vector in the scene, which in this case is (0, 1, 0);

Code Excerpt from draw loop:

  
  
   
                        distX *= 70.f;  
			ofxVec3f dist(distX, 0, 0);  
			if (timePtsDiff.size() > 1) {  
				dist += timePtsDiff[i];  
			}  
			  
			ofxVec3f up(0, 1, 0);  
			  
			ofxVec3f cross = dirNormal.perpendiculared(up);  
			cross.rotateRad(angle, dirNormal);  
			cross *= dist.length();  
			  
			ofSetColor(150, 150, 150);  
			glPushMatrix();  
				glTranslatef(spine[i].loc.x + cross.x, spine[i].loc.y + cross.y, spine[i].loc.z + cross.z );  
				ofCircle(0, 0, 2);  
			glPopMatrix();  
  

This works well,except there seems to be a bit of snapping of rotation at certain points, along the spine. So this works for the circles around the spine, but as far as creating a skin or drawing polygons or triangles, it may function a little quirky.

Now it looks like this, there is no checking if the body is folding on itself right now.
[attachment=0:3vxyy1d3]Picture 5.png[/attachment:3vxyy1d3]

![](http://forum.openframeworks.cc/uploads/default/618/Picture 5.png)

ok.

could you be a little bit more specific about what the problem is exactly? i can see the worm in the first picture, then in the second the same worm that looks also to be drawn fine, assuming the twisting line of black dots is supposed to be the spine of the worm… just not sure what’s wrong, nor how you want to fix it.

sorry!
d

This is hard to explain without seeing the animation, but hopefully this picture will clear it up. Some of the body segments get twisted and do not line up with the others, this may just be the drawing of triangles. In this example, it is just running through the array and connecting the points based on the index in the array. I will try finding the nearest point in the next segment, but that will slow down processing and it only seems to happen when the segments are aligned along the up axis, as display in the picture.

[attachment=0:4koyo0ld]Picture 10.png[/attachment:4koyo0ld]

I hope this clears up what the problem is, thanks for the quick responses :slight_smile:

![](http://forum.openframeworks.cc/uploads/default/620/Picture 10.png)

right - i’m guessing this is because of the way you’re calculating the ‘perpendicular’ vector to the worm spine.

you need to somehow define an up vector for the entire worm that is independent of the camera up vector, and then use that to make sure your point creation is always starting from the same position ‘above’ the spine - does that make sense?

What you are after is something called the Parallel Transport Frame, or just Parallel Transport sometimes.
You can find some info here to get you started: http://en.wikipedia.org/wiki/Parallel-transport

If you happen to have access to Game Programming Gems 2, there is a pretty good explanation of it
in there, otherwise just google around and you will end up in a bunch of physics/math/game dev forum threads
discussing it.

  • Edvin

Thank you Edvin and Damian for the replies.

Damian, I have tried to get the up vector for the worm, but the twisting occurs even more. The problem seems to occur when the axis (spine vector) aligns with the up vector. Not sure if I was doing it correctly, but I tried several methods.

[quote author=“edvinbesic”]What you are after is something called the Parallel Transport Frame, or just Parallel Transport sometimes.
You can find some info here to get you started: http://en.wikipedia.org/wiki/Parallel-transport

  • Edvin[/quote]

Edvin, I have read a couple of articles on this subject, however, I am uncertain how to apply it to the issue that I am having. The book you recommended was on Google Books, but the part on Parallel Transform was left out. :frowning:
I understand that it rotates a vector based on a sphere, but not sure how it applies to the rotation around an arbitrary axis. This is the code that I am using to calculate the points around the spine, where dirNormal is the spine normal and up is the camera up (0, 1, 0);

  
  
ofxVec3f cross = dirNormal.perpendiculared(up);  
cross.rotateRad(angle, dirNormal);  
cross *= dist.length();  
  

Thanks to Edvin and Damian, I figured it out. :smiley:

I set the initial point of the worm to influence the up vector of the worm for the rest of the tube that I used parallel transport on.
Based on this info from Craig Reynolds, http://red3d.com/cwr/steer/gdc99/index.html,
I set the approximate up vector to the up vector of the previous frame and the forward vector to the direction normal. Then I set the side vector to the cross of the forward and approximate up vector. Then set the new up to the cross of the forward and the side vector.

Based on the awesome Game Programming Gems 2 book recommended by Edvin,
I performed parallel transport on the rest of the body points to extrude along the path and it solved the problem.

Nick, could you post your finished code for this? I’m trying to get up to speed on this as well.
Thanks,
Evan

This is located inside the worm update loop, where the spine vector is a list of points that compose the middle of the worm. For the first spine point, I used the Craig Reynolds method for storing and estimating the up vector. And for the rest, the parallel transport method, mentioned by Edvin in a previous post, based on the previous spine point’s orientation, since it lined up the points better.

This could probably be optimized, but it works for now.

Hope this helps

  
  
if (i == 0) {  
	// dirNormal is a normalized vector between spine points //  
	// Reynolds Method [url][http://red3d.com/cwr/steer/gdc99/index.html[/url]](http://red3d.com/cwr/steer/gdc99/index.html[/url])   
	ofxVec3f new_forward(dirNormal.x, dirNormal.y, dirNormal.z);  
	new_forward.normalize();  
	ofxVec3f approx_up(-spine[i].orientation.up.x, -spine[i].orientation.up.y, -spine[i].orientation.up.z);  
	approx_up.normalize();  
	ofxVec3f new_side = new_forward.crossed(approx_up);  
	ofxVec3f new_up = new_forward.crossed(new_side);  
	new_up.normalize(); // just in case  
	  
	spine[i].orientation.up.set(new_up.x, new_up.y, new_up.z);  
	spine[i].orientation.forward.set(new_forward.x, new_forward.y, new_forward.z);  
	spine[i].orientation.side.set(new_side.x, new_side.y, new_side.z);  
	  
	//////////////////////////////////////////////////////////////////////////////  
}  else if (i < numBodyPts) {  
	// Game Programming Jems 2 Approach ////////////////////////////////  
	ofxVec3f T1(spine[i-1].loc.x, spine[i-1].loc.y, spine[i-1].loc.z);  
	ofxVec3f T2(spine[i].loc.x, spine[i].loc.y, spine[i].loc.z);  
	ofxVec3f A = T1.crossed(T2);  
	float alpha = T1.angleRad(T2);  
	  
	ofxVec3f new_forward(dirNormal.x, dirNormal.y, dirNormal.z);  
	ofxVec3f approx_up(-spine[i-1].orientation.up.x, -spine[i-1].orientation.up.y, -spine[i-1].orientation.up.z);  
	approx_up.normalize();  
	approx_up.rotateRad(alpha, A);  
	ofxVec3f new_side = new_forward.crossed(approx_up);  
	ofxVec3f new_up = new_forward.crossed(new_side);  
	  
	spine[i].orientation.up.set(new_up.x, new_up.y, new_up.z);  
	spine[i].orientation.forward.set(new_forward.x, new_forward.y, new_forward.z);  
	spine[i].orientation.side.set(new_side.x, new_side.y, new_side.z);  
	  
}  
  
when it is time to draw the tubes, you can loop through and get the cross vector from the forward and up vectors.  
  

Hi Nick,

Any chance to reveal a bit more on how you build the worm body?

Thanks

  • rS

After comparing some code and reading the PTF paper this is my implementation, which seems to give me a perfect result, but I’m still not totally sure as the gab between the maths used and which were given in the paper is huge :slight_smile:

https://gist.github.com/1525303#comments

@roxlu you’re a legend, thanks a lot!