Unrestrained Motion Through 3D Space

Started by TimmerCA, April 11, 2012, 04:08:45

Previous topic - Next topic

TimmerCA

Hi,

I'm trying to whip up a quick LWJGL game that allows the player to effectively fly through space.  There is no gravity, and for the purposes of the project at this point, there are no collisions, and there is no roll ability for the vehicle at this time (that is, there is no rotation about the "Z" axis for now).

So, I've got the following declarations:

private Vector3f location = new Vector3f(0.0f, 0.0f, 0.0f);
private float yaw = 0.0f;
private float pitch = 0.0f;


In my game loop, I'm doing:

if (Mouse.isButtonDown(0)) {
  yaw -= ((float)Mouse.getDX() / 5.0f) % 360.0f;
  pitch += ((float)Mouse.getDY() / 5.0f) % 360.0f;
}


And that part seems to be working fairly well.  But, the next step is to calculate my new position based on my current yaw and pitch, and that's where my trigonometry is failing me badly.  Currently, I have:

if (Mouse.isButtonDown(1)) {
  location.x += (float)(Math.sin(yaw * Math.PI / 180) * Math.cos(pitch * Math.PI / 180)) * time_delta * speed;
  location.y += (float)(Math.sin(pitch * Math.PI / 180)) * time_delta * speed;
  location.z += (float)(-Math.cos(yaw * Math.PI / 180) * Math.cos(pitch * Math.PI / 180)) * time_delta * speed;
}


But I know that's wrong.  What's the magic formula to calculate my new location based on the current location and my yaw and pitch?

matheus23

Sorry, for giving such a bad answer... but I think this is the fastest way to help you:
GLfloat posx;
	GLfloat posy;
	GLfloat posz;
	GLfloat rotx;
	GLfloat roty;
	GLfloat rotz;

	void move() {
		GLdouble mov = roty*PI/180;
		GLdouble movside = (roty+90.0f)*PI/180;

		if (Input::keys[Input::W]) {
			/** Relative movement forwards */
			posx += SPD*sin(mov);
			posz += SPD*cos(mov);
		} if (Input::keys[Input::S]) {
			/** Relative movement backwards */
			posx -= SPD*sin(mov);
			posz -= SPD*cos(mov);
		} if (Input::keys[Input::D]) {
			/** Relative movement to the right */
			posx += SPD*sin(movside);
			posz += SPD*cos(movside);
		} if (Input::keys[Input::A]) {
			/** Relative movement to the left */
			posx -= SPD*sin(movside);
			posz -= SPD*cos(movside);
		}
		if (Input::keys[Input::R]) {
			posx = 0;
			posy = 0;
			posz = 0;
			rotx = 0;
			roty = 0;
			rotz = 0;
		}
	}

	void rotation() {
		if (Input::MB_LEFT && Input::newevent) {
			Input::newevent = false;
			GLdouble dx = Input::mx-Input::lastmx;
			GLdouble changex = ((dx) * ROT_SPD)*(-1.0);
			GLdouble dy = Input::my-Input::lastmy;
			GLdouble changey = ((dy) * ROT_SPD)*(-1.0);
			roty += changex;
			rotx += changey;

		}

		if (Input::keys[Input::E]) {
			roty += 2.0f;
		} if (Input::keys[Input::Q]) {
			roty -= 2.0f;
		}

		/** limit to rotation to 359.9~ and positive */
		if (roty >= 360.0f) {
			roty -= 360.0f;
		} else if (roty < 0.0f) {
			roty += 360.0f;
		}
		if (rotx >= 360.0f) {
			rotx -= 360.0f;
		} else if (rotx < 0.0f) {
			rotx += 360.0f;
		}
		if (rotz >= 360.0f) {
			rotz -= 360.0f;
		} else if (rotz < 0.0f) {
			rotz += 360.0f;
		}
	}

	void render() {
		glRotatef(rotz, 0.0, 0.0, 1.0);
		glRotatef(rotx, 1.0, 0.0, 0.0);
		glRotatef(roty, 0.0, 1.0, 0.0);
		glTranslatef(-posx, -posy, posz);
	}


Got that from a very old GLUT c++ project. I think this should give you a good idea :) Also, rotation about the z axis is included. Only problem: Z position is not calculated from the mouse angle... That might be a problem for you ...
My github account and currently active project: https://github.com/matheus23/UniverseEngine

TimmerCA

So, I've made some progress here but I'm still not done.

I figured out that I need three Vector3f variables:

Vector3f location = new Vector3f(0.0f, 0.0f, 0.0f);
Vector3f velocity = new Vector3f(0.0f, 0.0f, 0.0f);
Vector3f rotation = new Vector3f(0.0f, 0.0f, 0.0f);


During each game loop, I adjust my location based on my velocity:

location.x += velocity.x * speed * time_delta;
location.y += velocity.y * speed * time_delta;
location.z += velocity.z * speed * time_delta;


If the player is holding down the left mouse button, I adjust the rotation:

if (Mouse.isButtonDown(0)) {
 rotation.x += ((float)Mouse.getDY() / 5.0f) % 360.0f;
 rotation.y -= ((float)Mouse.getDX() / 5.0f) % 360.0f;
}

rotation.z += ((float)Mouse.getDWheel() / 10.0f) % 360.0f;


Then during the drawing portion of my game loop, I use the rotation variable to translate the display into the proper orientation and the location variable to translate into the proper location:

GL11.glRotatef(
  rotation.x,
  1.0f,
  0.0f,
  0.0f
);

GL11.glRotatef(
  rotation.y,
  0.0f,
  1.0f,
  0.0f
);

GL11.glRotatef(
  rotation.z,
  0.0f,
  0.0f,
  1.0f
);

GL11.glTranslatef(
  location.x,
  location.y,
  location.z
);


All of this works absolutely beautifully.

But here's what I'm still confused about:

If the player is holding down the right mouse button, I want to accelerate the vehicle in the direction that the player is currently facing.  How do I adjust the velocity variable based on the current rotation?

CodeBunny

Basic trig functions.

This is kind of a weird control system... why doesn't the ship accelerate in the direction it's facing? You could give the player a way to turn the ship. That way, it'd be like Asteroids controls (which are fairly decent in showing space momentum).

TimmerCA

Quote from: CodeBunny on April 12, 2012, 12:27:15
Basic trig functions.

I know that.  Which ones?  The closest I can come is:

if (Mouse.isButtonDown(1)) {
  // the calculation for velocity.x is broken, but the other two seem ok
  velocity.x += (Math.sin(Math.toRadians(rotation.y)) * Math.cos(Math.toRadians(rotation.x))) * 0.001f;
  velocity.y += Math.sin(Math.toRadians(rotation.x)) * 0.001;
  velocity.z -= (-Math.cos(Math.toRadians(rotation.y)) * Math.cos(Math.toRadians(rotation.x))) * 0.001f;
}


The y and z accelerations work well, but the x one is off.  If I comment out the x acceleration line, then the system works as I expect it to on the y and z axis.

Quote from: CodeBunny on April 12, 2012, 12:27:15
This is kind of a weird control system... why doesn't the ship accelerate in the direction it's facing? You could give the player a way to turn the ship. That way, it'd be like Asteroids controls (which are fairly decent in showing space momentum).

We're saying the same thing.  Then you press the mouse button, the ship will accelerate in the direction it's facing.  When I said "the direction the player is facing", I meant "the direction the ship is facing".

CodeBunny

What is the vector in the direction of rotation [0, 0, 0]?

randfur

When dealing with rotation you often have to make a decision about what direction your angles go in (left/right, up/down, etc.).
Basically as CodeBunny was asking, decide in what orientation (0,0,0), (90,0,0), (0,90,0) and (0,0,90) put you in and then decide in what order the axis rotations occur.
These decisions because the basis of your trig equations.

Personally I prefer to use quaternions for rotation because once you get all the complex math down it becomes much simpler conceptually compared with hand crafted trig expressions.

TimmerCA

Quote from: randfur on April 15, 2012, 09:49:26Personally I prefer to use quaternions for rotation because once you get all the complex math down it becomes much simpler conceptually compared with hand crafted trig expressions.

I'm willing to learn something new if it is the better way to do it.  I'll Google around for quaternions and see what I can learn.

randfur

I'm happy to share what I know, feel free to post more questions on quaternions if you need answers.
You should also check out this algorithm shown under "Pseudo-code for rotating using a quaternion".
http://en.wikipedia.org/w/index.php?title=Quaternions_and_spatial_rotation&oldid=362009945#Performance_comparisons_with_other_rotation_methods