DirectX Freeform Camera problem

Started by
12 comments, last by GhostfromTexas 13 years, 9 months ago
This is my first post on these forums, but have been on #gamedev on the efnet IRC so howdy from Texas :)

To my problem. Please accept my apologies if I am not following some sort of forum etiquette. I read some initial rules so I hope nothing I do is out of line or looked down upon.

I am a Game and Simulation programming student who is working on a big project for our course. The 8 of us in the class on a team and we are to construct a demonstration/game that utilizes all the stuff we've learned from previous classes (AI, Graphics, physics, etc) - I promise that me asking for outside help is not against the class rules and is actually encouraged by our professor, so that's why I am stopping here.

Anyway, enough of the shenanigans. I have a freeform camera class in directX that I have structured and occasionally works quite well. The reason I say occasionally is because my camera often has fits depending on where I put it's starting position and it's target. Sometimes I can set a position and target, and the camera performs flawlessly, other times, I end up loosing my model completely and can never be found, or the 3d model (like a terrain that uses shaders) is skewed and leans randomly depending on the way the camera is facing. I am about 99.999% certain it's a mathematics problem. I have attempted several times in the past to really sit down and try to solve the issue, and I have a hunch specifically what it might be. I think that when I manually set a target and position, I am getting bad Right and Up vectors, which I have tried to correct but with no avail.

The code I have for the camera's structure is fairly complex and would possibly take several pages of a post to explain. Without stepping on anyone's toes, I would love for a kind soul to possibly get with me on Ventrilo/Teamspeak or heck even over the phone to help me hash out this problem. It would be much easier to explain over some sort of voice coms what the problem is and how it works, rather than type up the explanation here.

If someone would be willing to do this I would greatly appreciate it. I am not looking for someone to do this for me. I want to learn what my mistake is and I want to learn the solution that is required to fix it.

My code for most of the advanced directX users is going to be very basic structure. I am doing nothing that is cutting edge. I am actually using the book: "Introduction to 3D Game Programming w/ DirectX 9.0c - A Shader Approach" by Frank D. Luna so most of my code is based off of his. Surprisingly his code has similar issues for his FreeForm camera.

Thanks again,
Cameron
Advertisement
If you're looking for someone to help you off of the forums, you might have better luck asking in Help Wanted or the Lounge. Since you're asking in the technical forums though, it might be better to keep the discussion here in the thread so that other readers can benefit. (IMO, at least.)

Anyway, even if there's a lot of code, I imagine the problem itself might not be as complex as it seems and could be solved here on the forums in the usual fashion.

I think the first step will be to define precisely what you mean by 'freeform'. Can you describe in detail the movement scheme for the camera? That is, what keys or other input devices control it, and what do the controls do?
By FreeForm I mean "First Person" camera.. you use your normal "W and S" keys to move forward and backward in the direction you are looking, you use your "A and D" keys to strafe side to side. "Space" moves you up in the Y direction, like jumping. And last but not least holding down your shift key moves you faster, like a sprint. The mouse looks around like a normal FPS camera does to get you a new target.

My camera basically flies all over the Game World, no restrictions on where it can go.

Here is the good news. I figured out 90% of my problem with the camera's starting position and target!!! At least it seems to be working correctly now. But there is still a small issue that causes some odd behavior that I believe is related so let me explain.

My problem before is that I was not correctly setting my Up and Right vector depending on where I was and where I was looking. I've solved that initial issue now where I believe I am calculating them properly.

Here is my remaining problem. I have 2 methods in my camera class, one to set the position, and the other to set the target. Basically if I wanted to have the camera's target jump to a specific location in the middle the game without manually moving it with the mouse, then I could. Or if I wanted to all of a sudden change the camera's position by just one line of could, then I have that ability.
myCamera->setTarget(D3DXVECTOR3(0, 0, 0));


However, I need to be able to recalculate my UP and RIGHT vectors when I do that. I use the same code that I use in my camera's constructor to calculate the UP and RIGHT vectors. But when I do something like set a target or position, but it throws my my axes off.

Let me try and describe what happens. I have a flat basic terrain that is completely level. If I myCamera.setTarget(...) to a new position, the terrain will now lean and look slanted even though I am recalculating the UP and RIGHT vector.

If you need more explanation let me know. Here is my Camera's source file. I added the function "recalcVectors();" to help recalculate the UP and RIGHT vector at the beginning and possibly when I end up setting setTarget(...) or setPosition(...);

I am about 99.99% certain that it's still revolving around the calculation of the RIGHT and UP vector. I might be missing a key step in recalcVec()'s algorithm.

FYI, in the source code, my camera is a singleton class. The "adjust()" function is what's called every frame. Using normal DirectInput for the key commands. If you need anymore info on this then let me know!

Everything that has GS:: is our global settings static class. Target and Position are d3dxvectors. Speed and Sensitivity are floats. Mode is an int.

#include "DXCamera.h"#define DXCAMERA_FREEFORM 0;DXCamera::DXCamera() {	D3DXMatrixIdentity(&mView);	mPosition		= GS::CameraInitPosition;			//the position of the camera	mRight			= D3DXVECTOR3(1.0f, 0.0f, 0.0f);	// the right axis from the camera's position	mUpVec			= D3DXVECTOR3(0.0f, 1.0f, 0.0f);	//the up vector, default to Y	mTarget			= GS::CameraInitTarget;				//target that we want to look at	recalcVectors();									//calculate the vectors accordingly		mSpeed			= GS::CameraInitSpeed;				//camera's movement speed	mSensitivity	= GS::CameraInitSensitivity;		//camera's mouse sensativity	mCameraMode		= GS::CameraInitMode;				//camera mode}DXCamera::~DXCamera() {}DXCamera* DXCamera::Instance() {	static DXCamera *ptr = new DXCamera();		return ptr;}void DXCamera::adjust() {	switch(mCameraMode) 	{		case 0:			rotateFreeForm();			adjustFreeForm();					break;	}	buildView();}//mouse rotationvoid DXCamera::rotateFreeForm() {	if(gDInput->mouseButtonDown(1)) //if right mouse button are down	{ 				//grab angles		float pitch = gDInput->mouseDY() / 360; //get the change in the angle for moving the mouse up and down		float yAngle = gDInput->mouseDX() / 360; //get the change in the angle for moving the mouse left and right				//set sensativity		pitch *= mSensitivity;		yAngle *= mSensitivity;		//rotate Up and Down		D3DXMATRIX matRotAxis;		D3DXMatrixRotationAxis(&matRotAxis, &mRight, pitch);		D3DXVec3TransformCoord(&mTarget, &mTarget, &matRotAxis);		D3DXVec3TransformCoord(&mUpVec, &mUpVec, &matRotAxis);		//rotate left and right		D3DXMATRIX matRotYAxis;		D3DXMatrixRotationY(&matRotYAxis, yAngle);		D3DXVec3TransformCoord(&mRight, &mRight, &matRotYAxis);		D3DXVec3TransformCoord(&mUpVec, &mUpVec, &matRotYAxis);		D3DXVec3TransformCoord(&mTarget, &mTarget, &matRotYAxis);			}}void DXCamera::adjustFreeForm() {	//check speed modifier	float speedSet = mSpeed;	if(gDInput->keyDown(DIK_LSHIFT)) 	{		speedSet *= 5;	}	D3DXVECTOR3 dir(0.0f, 0.0f, 0.0f);	if(gDInput->keyDown(DIK_W))  //move forward	{		dir += mTarget;	}	if(gDInput->keyDown(DIK_S)) //move backward	{ 		dir -= mTarget;	}	if(gDInput->keyDown(DIK_D))  //strafe right	{		dir += mRight;	}	if(gDInput->keyDown(DIK_A)) //strafe left	{ 		dir -= mRight;	}	if(gDInput->keyDown(DIK_SPACE)) //move up in the y direction	{		dir.y += 1;	}	D3DXVec3Normalize(&dir, &dir);	mPosition += dir * speedSet;}//build and return the view matrix for the game worldvoid DXCamera::buildView() {	//Keep camera's axes orthogonal to each other and of unit length.	D3DXVec3Normalize(&mTarget, &mTarget);	//recalculate the up vector	D3DXVec3Cross(&mUpVec, &mTarget, &mRight);	D3DXVec3Normalize(&mUpVec, &mUpVec);	//D3DXVec3Cross(&mRight, &mUpVec, &mTarget);  // seems pointless	D3DXVec3Normalize(&mRight, &mRight);	//Fill in the view matrix entries.	float x = -D3DXVec3Dot(&mPosition, &mRight);	float y = -D3DXVec3Dot(&mPosition, &mUpVec);	float z = -D3DXVec3Dot(&mPosition, &mTarget);	mView(0,0) = mRight.x; 	mView(1,0) = mRight.y; 	mView(2,0) = mRight.z; 	mView(3,0) = x;   	mView(0,1) = mUpVec.x;	mView(1,1) = mUpVec.y;	mView(2,1) = mUpVec.z;	mView(3,1) = y;  	mView(0,2) = mTarget.x; 	mView(1,2) = mTarget.y; 	mView(2,2) = mTarget.z; 	mView(3,2) = z;   	mView(0,3) = 0.0f;	mView(1,3) = 0.0f;	mView(2,3) = 0.0f;	mView(3,3) = 1.0f;}//set the camera's movement speedvoid DXCamera::setSpeed(float speed) {	mSpeed = speed;}//get the camera's movement speedfloat DXCamera::getSpeed() {	return mSpeed;}//set the camera's mouse sensitivityvoid DXCamera::setSensitivity(float sens) {	mSensitivity = sens;}//get the camera's mouse sensitivityfloat DXCamera::getSensitivity() {	return mSensitivity;}//set the camera's position manuallyvoid DXCamera::setPosition(D3DXVECTOR3 pos) {	mPosition = pos;	//recalcVectors();}//get the camera's current positionD3DXVECTOR3 DXCamera::getPosition() {	return mPosition;}//set the camera's targetvoid DXCamera::setTarget(D3DXVECTOR3 target) {	mTarget = target;	//recalcVectors();}//get the camera's current targetD3DXVECTOR3 DXCamera::getTarget() {	return mTarget;}//set the camera's UP vector (default to 0, 1, 0)void DXCamera::setUpVec(D3DXVECTOR3 upVec) {	mUpVec = upVec;}//get the camera's current up vectorD3DXVECTOR3 DXCamera::getUpVec() {	return mUpVec;}D3DXMATRIX DXCamera::getView() {	return mView;}void DXCamera::recalcVectors(){	mTarget = mTarget - mPosition;	D3DXVec3Normalize(&mTarget, &mTarget);	D3DXVec3Cross(&mRight, &mUpVec, &mTarget);	D3DXVec3Cross(&mUpVec, &mTarget, &mRight);}
As far as I see, you are overcomplicating the thing (and that is an understatement).

The usual way to make a first person view:
Rotate(ViewAngle.x,1,0,0);Rotate(ViewAngle.y,0,0,1);Translate(ViewPos[0],ViewPos[1],ViewPos[2]);

or
Translate(ViewPos.x,ViewPos.y,ViewPos.z);Rotate(ViewAngle_Z,0,0,1);Rotate(ViewAngle_X,1,0,0);
Depending on the matrix convention (row/column major)

ViewAngle is controlled by the mouse, ViewPos is controlled by the keyboard.

If you have targets, look into "look at".

Look at is basically this: (I haven't implemented it myself, I'm thinking it through by typing here):
image

For a transformation, you need a base (e1: forward,e2:left,e3:up). UP is the world's up vector.

You need to apply the inverse of the camera transform to the world.

I think the image tells everything you need.

If you have more camera types (fps, 6DOF, arcball whatever), I suggest to use separate functions for them, instead of one god function, that does everything.

I have to go now, if something isn't clear, let me know.
In both of the places where you 're-build' the axes from the forward vector, I would try re-computing the axes as follows (pseudocode):
target = normalize(target);right = normalize(cross(vector3(0, 1, 0), target));up = cross(target, right);
The only place this might fail is if the camera looks more or less straight up or down. For this reason and others I recommend handling FPS motion differently than what you've shown here, but it would require significant revision to your code so I won't go into that further.

I would however recommend renaming your 'target' variable. 'Target' more or less implies a target position (a point), where what you really have here is a 'forward' vector. (As such, I'd just name it 'forward'.)
Haven't posted something:

so, the final transfor will be:
[e1\e2\e3]^T * [-x, 0, 0                 0,-y, 0                 0, 0,-z]
or
[-x, 0, 0  0,-y, 0  0, 0,-z]  * [e1\e2\e3]^T 
Depending on the convention. (note that the inverse of an orthogonal matrix is the same as its transpose)

e1,e2,e3 can be calculated in any ways. Maybe they are the base vectors of an object: this way you can 'bind' the camera to an object with arbitrary orientation and position.
Um... I'm confused about something. Why 'right' vector and not 'left?'

The basis isn't a right handed stuff? (Or I'm into another flamewar of handedness?)

I used the world's UP = {0,0,1} convention.
Now I'm totally confused.

Sorry.
Quote:The usual way to make a first person view:
Rotate(ViewAngle.x,1,0,0);Rotate(ViewAngle.y,0,0,1);Translate(ViewPos[0],ViewPos[1],ViewPos[2]);

or
Translate(ViewPos.x,ViewPos.y,ViewPos.z);Rotate(ViewAngle_Z,0,0,1);Rotate(ViewAngle_X,1,0,0);
Depending on the matrix convention (row/column major)
Just FYI, transformation order doesn't have anything to do with whether the matrices are row-major or column-major. Rather, it's a function of vector notation convention (that is, whether row or column vectors are being used).
Quote:Um... I'm confused about something. Why 'right' vector and not 'left?'

The basis isn't a right handed stuff? (Or I'm into another flamewar of handedness?)

I used the world's UP = {0,0,1} convention.
Now I'm totally confused.
It's all just a matter of conventions. D3D can be set up to be either left-handed or right-handed, but in D3D use of a left-handed coordinate system is fairly common.

As for which direction is up, that's also pretty much just a matter of preference.
*facepalm*

I don't know what I'm doing.

Anyway:
Rotate(ViewAngle.x,1,0,0);Rotate(ViewAngle.y,0,0,1);Translate(ViewPos[0],ViewPos[1],ViewPos[2]);
is in FPL openGL, but forget everything I said.

This topic is closed to new replies.

Advertisement