🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Get Angle of 3D Line and Rotate Polygon With Result

Started by
15 comments, last by d07RiV 6 years, 1 month ago

Hello. Basically I'm trying to get the angle of a 3D line and getting the result as an X Y and Z angle in order to rotate polygons to be parallel with that angle. In other words, I'm trying to create a "Wormhole Wall" by taking a series of lines and wrapping 8 polygons around each line at a certain distance away from the line. But honestly I don't think the results of getting the angle of a 3d line are correct. If, for example, point A is at <0, 0, 0> and  point B is at <1, 0, 1>, you would think the Yaw is at a 45 degree angle, while the Pitch and Roll remains zero. Instead, my Pitch is 45, Yaw is 90, and Roll is 45. I made a simplified version to see if you can help spot anything wrong. Thanks in advance:


#include <iostream>
using namespace std;

int main(void);

struct Vertex3D {
	float x;
	float y;
	float z;
};

struct Vector3D {
	float x;
	float y;
	float z;
};

const double PI = 4.0 * atan(1.0);

Vertex3D a = { 0.0f, 0.0f, 0.0f };
Vertex3D b = { 1.0f, 0.0f, 1.0f };
Vector3D u;

Vector3D vecX = { 1.0f, 0.0f, 0.0f };
Vector3D vecY = { 0.0f, 1.0f, 0.0f };
Vector3D vecZ = { 0.0f, 0.0f, 1.0f };

Vector3D angle;

Vector3D dot;
float mag;

int main() {

	//acos domain: -1 to 1			range: 0 < x < pi
	//asin domain: -1 to 1			range: -pi/2 < x < pi/2
	//atan domain: all real num		range: -pi/2 < x < pi/2
	
	u.x = b.x - a.x;
	u.y = b.y - a.y;
	u.z = b.z - a.z;
	
	mag = sqrtf(u.x * u.x + u.y * u.y + u.z * u.z);

	dot.x = u.x * vecX.x + u.y * vecX.y + u.z * vecX.z;
	dot.y = u.x * vecY.x + u.y * vecY.y + u.z * vecY.z;
	dot.z = u.x * vecZ.x + u.y * vecZ.y + u.z * vecZ.z;
	

	angle.x = acosf(dot.x / mag) * 180.0f / static_cast<float>(PI); //Pitch --
	angle.y = acosf(dot.y / mag) * 180.0f / static_cast<float>(PI); //Yaw	|
	angle.z = acosf(dot.z / mag) * 180.0f / static_cast<float>(PI); //Roll  .

	cout << "Point a is at " << a.x << ", " << a.y << ", " << a.z << endl;
	cout << "Point b is at " << b.x << ", " << b.y << ", " << b.z << endl;

	cout << "angle x (Pitch): " << angle.x << endl;
	cout << "angle y (Yaw): " << angle.y << endl;
	cout << "angle z (Roll): " << angle.z << endl;

	system("pause");
	return 0;
}

 

 

Advertisement

Wait a tick, I think I'm on to something. Since I need the results independently (angle X, angle Y, angle Z), I did getting the angle in 2D for 3D, only to get X, I get the atan2() of y and z, to get Y, I get the atan2() of x and z, and to get Z, I get the atan2() of y and x. So far the Yaw in 45 degrees while Pitch and Roll is zero as intended. I just hope the math is right o.O:


#include <iostream>
using namespace std;

int main(void);

struct Vertex3D {
	float x;
	float y;
	float z;
};

struct Vector3D {
	float x;
	float y;
	float z;
};

const double PI = 4.0 * atan(1.0);

Vertex3D a = { 0.0f, 0.0f, 0.0f };
Vertex3D b = { 1.0f, 0.0f, 1.0f };
Vector3D u;

Vector3D vecX = { 1.0f, 0.0f, 0.0f };
Vector3D vecY = { 0.0f, 1.0f, 0.0f };
Vector3D vecZ = { 0.0f, 0.0f, 1.0f };

Vector3D angle;

Vector3D dot;
float mag;

float x, y, z;

int main() {

	//acos domain: -1 to 1			range: 0 < x < pi
	//asin domain: -1 to 1			range: -pi/2 < x < pi/2
	//atan domain: all real num		range: -pi/2 < x < pi/2
	
	//u.x = b.x - a.x;
	//u.y = b.y - a.y;
	//u.z = b.z - a.z;
	
	//mag = sqrtf(u.x * u.x + u.y * u.y + u.z * u.z);

	//dot.x = u.x * vecX.x + u.y * vecX.y + u.z * vecX.z;
	//dot.y = u.x * vecY.x + u.y * vecY.y + u.z * vecY.z;
	//dot.z = u.x * vecZ.x + u.y * vecZ.y + u.z * vecZ.z;

	//angle.x = acosf(dot.x / mag) * 180.0f / static_cast<float>(PI); //Pitch --
	//angle.y = acosf(dot.y / mag) * 180.0f / static_cast<float>(PI); //Yaw	|
	//angle.z = acosf(dot.z / mag) * 180.0f / static_cast<float>(PI); //Roll  .

	x = b.x - a.x;
	y = b.y - a.y;
	z = b.z - a.z;

	angle.x = atan2f(y, z) * 180.0f / static_cast<float>(PI);
	angle.y = atan2f(x, z) * 180.0f / static_cast<float>(PI);
	angle.z = atan2f(y, x) * 180.0f / static_cast<float>(PI);

	cout << "Point a is at " << a.x << ", " << a.y << ", " << a.z << endl;
	cout << "Point b is at " << b.x << ", " << b.y << ", " << b.z << endl;

	cout << "angle x (Pitch): " << angle.x << endl;
	cout << "angle y (Yaw): " << angle.y << endl;
	cout << "angle z (Roll): " << angle.z << endl;

	system("pause");
	return 0;
}

 

1 hour ago, Psychopathetica said:

you would think the Yaw is at a 45 degree angle, while the Pitch and Roll remains zero. Instead, my Pitch is 45, Yaw is 90, and Roll is 45.

Strange, you shouldn't be getting anything from your roll.

2DVector.thumb.jpg.eca33d39646f0f936e81a322b44e3550.jpg

Looking at this image you can see how the 2D vector (1,1) shows a 45 degree angle (Yaw). Now with a 3D vector we can do the same thing:

3DVector.thumb.jpg.675b920d9dbd415bac083ac25350aad4.jpg

What I get is: Yaw = 90, Pitch = 45, Roll = 0; Try rotating each axis on it's own.

 

I am having a hard time understanding what you are trying to do. I think part of the problem is that there are several notions that seem to be somewhat confused here:

  • Angle: This is a single number that tells you how far apart the directions of two vectors are.
  • Rotation: This is what is described with yaw, pitch and roll (or in many other ways).
  • Direction: A class of vectors that are proportional to each other.

If you are trying to align a polygon to another polygon, you need to find the normal directions to both polygons and then find a rotation that aligns one to the other. There are many such rotations, but there is a notion of "shortest rotation".

But again, I am not sure what you are trying to do.

 

Here is what I'm trying to do.

I made a wormhole mesh in Autodesk 3D studio max using line segments interconnected, and made the line radius like 150, which made it into a wormhole tunnel.

906766977_wormholescreenshot.thumb.png.d7d5412dad633a1b8c5a339b1c624931.png

 

Problem is, when I exported the mesh, I got a line path out of it instead of a real mesh. So I had an idea to use this data in my program. Every line segment will have a series of walls around it (like 8 or 10 walls per segment) with the radius being the distance from the line segment, so as I load all the line segment data, it would spitout my wormhole I created. But at the moment, I'm only using one polygon quad hovering over the line segment, When the line segment is at a different angle, the polygon will rotate to be at the same angle with the line segment. Like in this image:

 

1318733138_wormholescreenshot2.png.104acf8813e231d4eb746a6c0ee699a1.png

 

Unfortunately, when I do other angles, it doesn't go with it. So I'm just trying to figure out the trig in order for it to always hover over the line. Once I figure this out, the other walls that will circle the line will just be offsets.

Easiest solution, download blender, create a bezier curve, choose bevel. Export. Job done.

I actually have no idea the best way of doing this, I can think of multiple (probably bad ways) off the top of my head, if you browse the blender source code you can find out how they do it.

Extruding a spline to a mesh has one problem, which is how to define a robust orientation. Form the spline (or a set of connected line segments) you only have a single direction, let's call it the tangent.

There are some usual methods to make an orientation out of this, like Gram Schmidt or Up-Vector, but they all cause flipping issues.

So what 3DS is probably doing here is to precalcualte an orientation ot each knot vertex of the spline, e.g. in a way like this:


	vec prevTangent = spline.CalcTangent(knot[0].t);
	vec biTangent = CalcAribtaryPerpendicularDirection(prevTangent);
	Orientation orn = MakeOrientationFromTwoOrthogonalDirections (prevTangent, biTangent);
	knot[0].orn = orn;
	for (i=1; i<spline.knotCount; i++)
	{
	vec nextTangent = spline.CalcTangent(knot[i].t);
	Rotation axisAndAngle (prevTangent, nextTangent); // make rotation between 2 tangent directions in order
	orn *= axisAndAngle; // apply this, so orn is again aligned to the current tangent - because tangents are not so differnt, no flipping occurs
	knot[i].orn = orn;
	prevTangent = nextTangent;
	}
	

Then to extrude a mesh you only need to generate vertices using knot[x].orn and connect them with vertices on the next knot.

 

If however the spline forms a loop as in your screenshot, there is one more step to do to avoid discontinuity (which is more involved):

Calculate angle difference from last to first knot along their tangents, and subtract this angle from all knot rotations using their own tangent directions and a weight of float(knotIndex) / float(knotCount).

 

If this sounds too hard (i'd do it using quaternions, which are easy to convert from / to axis and angle) , i'm pretty sure 3DS has an option to tesselate those splines to a mesh as well. I remember i did this somehow in the past.

 

10 hours ago, Psychopathetica said:

Problem is, when I exported the mesh, I got a line path out of it instead of a real mesh.

Use the "Turn To Mesh" or "Turn To Poly" Modifier. This will convert your curve mesh into a mesh that can be exported and imported into a engine.

This video, shows what to do and is short:

https://www.youtube.com/watch?v=7ST-pD7tqZo

 

When I get home, Ill check out the turn to mesh /poly modifier. I think this will be easier. I still need the line segment path though for the camera to follow, which means I still need to properly get the angles of the line for the camera to turn at that angle. I may need to resort to quaternions. I dont know.

Success! I created a wormhole mesh. Since I have both the linear path and the mesh, I attempted to find out how to get the angle of the 2 points in 3D, and managed to solve that as well. And I need this for many applications, such as sprite following, and camera following, at that particular angle. This is the math I used, and I actually found 2 methods. Roll is always zero because you never see the line "roll", and it threw off the values anyways. Method one was to use this:


	float x, y, z;
	D3DXVECTOR3 a = { -100.0f, 0.0f, 200.0f };
	D3DXVECTOR3 b = { 50.0f, 0.0f, 250.0f };
	D3DXVECTOR3 angle;

	x = b.x - a.x;
	y = b.y - a.y;
	z = b.z - a.z;

	angle.x = atan2f(sqrtf(x * x + z * z), y) * (180.0f / static_cast<float>(PI)) - 90.0f; //Pitch
	angle.y = atan2f(x, z) * (180.0f / static_cast<float>(PI)); //Yaw
	angle.z = 0.0f; //Roll

 

Another way was to the same thing with less math was this:


	float x, y, z;
	D3DXVECTOR3 a = { -100.0f, 0.0f, 200.0f };
	D3DXVECTOR3 b = { 50.0f, 0.0f, 250.0f };
	D3DXVECTOR3 angle;

	x = b.x - a.x;
	y = b.y - a.y;
	z = b.z - a.z;

	angle.x = -atan2f(y, z) * (180.0f / static_cast<float>(PI)); //Pitch
	angle.y = atan2f(x, z) * (180.0f / static_cast<float>(PI)); //Yaw
	angle.z = 0.0f; //Roll

 

And it did not matter what angle the line was in, the polygon always moved with it. With some Lerping, or Slerping, I can have it move along the line if I want to.

This topic is closed to new replies.

Advertisement