How to create an 3D arrow that always points north.

Chulein

29-07-2008 13:37:39

Hi all!

Im new to Mogre and just started on a project a couple of weeks ago. In the application I now want implement an arrow onscreen that always points toward north in my 3d world, and I was wondering if anybody got some tips to get me started.
I want to use a 3d model\mesh to represent the arrow.

So far I think a good way to do it is to connect the arrow entity to the camera entity and do the rotation from there. what do you think?

Thanks.

Mephs

29-07-2008 14:58:48

Hi all!

Im new to Mogre and just started on a project a couple of weeks ago. In the application I now want implement an arrow onscreen that always points toward north in my 3d world, and I was wondering if anybody got some tips to get me started.
I want to use a 3d model\mesh to represent the arrow.

So far I think a good way to do it is to connect the arrow entity to the camera entity and do the rotation from there. what do you think?

Thanks.


Here's some code I used to make a Camera that functions similarly to what you want your arrow to do. There's two objects involved. A SceneNode and the Camera itself, and the camera is always focused on the sceneNode.

You can adjust the PivotCamera's rotation around the node, the tilt, and the tether (distance from sceneNode to the camera) and it sort of simplifies controlling it.


public class PivotCamera
{
SceneNode pivotNode = null;
Camera pivotCam = null;

float tetherLength = 0;
double tetherAngle = 0;
double rotateAngle = 0;

public double TiltMin = 0;
public double TiltMax = 0;
public double ZoomMin = 0;
public double ZoomMax = 0;

/// <summary>
/// Constructors
/// </summary>
public PivotCamera(SceneManager scnMgr)
{
pivotNode = scnMgr.RootSceneNode.CreateChildSceneNode("PivotNode");
pivotNode.Position = Vector3.ZERO;

pivotCam = scnMgr.CreateCamera("PivotCam");
pivotCam.Position = Vector3.ZERO;
pivotCam.NearClipDistance = 5;

UpdateCamera();
}
public PivotCamera(SceneManager scnMgr, double zMin, double zMax)
{
pivotNode = scnMgr.RootSceneNode.CreateChildSceneNode("PivotNode");
pivotNode.Position = Vector3.ZERO;

pivotCam = scnMgr.CreateCamera("PivotCam");
pivotCam.Position = Vector3.ZERO;
pivotCam.NearClipDistance = 5;

ZoomMin = zMin;
ZoomMax = zMax;

UpdateCamera();
}
public PivotCamera(SceneManager scnMgr, float teth)
{
pivotNode = scnMgr.RootSceneNode.CreateChildSceneNode("PivotNode");
pivotNode.Position = Vector3.ZERO;

pivotCam = scnMgr.CreateCamera("PivotCam");
pivotCam.Position = Vector3.ZERO;
pivotCam.NearClipDistance = 5;

SetTetherLength(teth);
UpdateCamera();
}

//Parent connectors
public Camera GetCamera()
{
return pivotCam;

}

/// Control functions
/// <summary>
/// Change rotation angle
/// </summary>
public void Rotate(double degrees)
{
double deltaDegrees = RadianToDegree(rotateAngle + DegreeToRadian(degrees));
SetRotateAngle(deltaDegrees);
}
/// <summary>
/// Change tether length
/// </summary>
public void Zoom(float delta)
{
if (tetherLength + delta > ZoomMin && tetherLength + delta < ZoomMax )
SetTetherLength(tetherLength + delta);
}
/// <summary>
/// Change the Tether angle
/// </summary>
public void Tilt(double degrees)
{
double deltaDegrees = RadianToDegree(tetherAngle + DegreeToRadian(degrees));
if (deltaDegrees < TiltMin) deltaDegrees = TiltMin;
else if (deltaDegrees > TiltMax) deltaDegrees = TiltMax;

SetTetherAngle(deltaDegrees);
}
/// <summary>
/// Move camera and it's focus point based on X mouse movement
/// </summary>
public void ShiftX(float delta)
{
float deltaX = (float) System.Math.Cos(rotateAngle) * delta;
float deltaZ = (float) System.Math.Sin(rotateAngle) * delta * -1;
Vector3 pCam = pivotCam.Position;
Vector3 pNode = pivotNode.Position;
pivotCam.Position = new Vector3(pCam.x+deltaX,pCam.y,pCam.z+deltaZ);
pivotNode.Position = new Vector3(pNode.x+deltaX, pNode.y, pNode.z+deltaZ);
}

/// <summary>
/// Move camera and it's focus point based on Y mouse movement
/// </summary>
public void ShiftZ(float delta)
{
float deltaX = (float)System.Math.Sin(rotateAngle) * delta;
float deltaZ = (float)System.Math.Cos(rotateAngle) * delta;
Vector3 pCam = pivotCam.Position;
Vector3 pNode = pivotNode.Position;
pivotCam.Position = new Vector3(pCam.x + deltaX, pCam.y, pCam.z + deltaZ);
pivotNode.Position = new Vector3(pNode.x + deltaX, pNode.y, pNode.z + deltaZ);
}

/// <summary>
/// //Set constant distance from Focus point to camera
/// </summary>
public void SetTetherLength(float i)
{
tetherLength = i;
}
/// <summary>
/// Set Angle from Focus point to camera
/// </summary>
public void SetTetherAngle(double degrees)
{
tetherAngle = DegreeToRadian(degrees);
}
/// <summary>
/// Set rotation from the origin around the focus point
/// </summary>
public void SetRotateAngle(double degrees)
{
rotateAngle = DegreeToRadian(degrees);
}
/// <summary>
/// Set horizontal tilt limit
/// </summary>
public void SetTiltLimit(int minTilt, int maxTilt)
{
TiltMin = minTilt;
TiltMax = maxTilt;
}

/// <summary>
/// Get the rotation angle (in degrees)
/// </summary>
public int GetRotationAngle()
{
int angle = (int) RadianToDegree(rotateAngle);
return angle;
}

public void SetVisibility(uint vis)
{
pivotCam.Viewport.SetVisibilityMask(vis);
}

/// <summary>
/// Update Camera view
/// </summary>
public void UpdateCamera()
{
float newRadius = (float)(System.Math.Sin(tetherAngle) * tetherLength);

float newX = (float)(System.Math.Sin(rotateAngle) * newRadius);
float newY = (float)(System.Math.Cos(tetherAngle) * tetherLength);
float newZ = (float)(System.Math.Cos(rotateAngle) * newRadius);

Vector3 tempOrigin = pivotNode.Position;
tempOrigin.x += newX;
tempOrigin.y += newY;
tempOrigin.z += newZ;

pivotCam.Position = tempOrigin;
pivotCam.LookAt(pivotNode.Position);
}

/// <summary>
/// Convert Degrees to Radians
/// </summary>
private double DegreeToRadian(double angle)
{
return System.Math.PI * angle / 180.0;
}

/// <summary>
/// Convert Radians to Degrees
/// </summary>
private double RadianToDegree(double angle)
{
return (180.0 * angle) / System.Math.PI;
}
}


Use that as an example, but what you need are 3 objects. A sceneNode for your Arrow, the Camera, and a SceneNode for your constant North.

Instead of making the Camera work around the SceneNode like in the above, make the "North" node, which is invisible Pivot around your camera as it moves. The TetherLength (distance from Camera to SceneNode) should always be constant, and a larger number than the Tether on your arrow node. Both the arrow and North node need to move as the camera moves to maintain this constant axis. Then once you have the positioning set up right, just have Arrow.LookAt(NorthNode)

Here's a quickie diagram.



The blue dot is your NorthNode, the green is your arrow, and black is your camera. The rotateAngle would be the angle (in radians) around the camera that the NorthNode should always be at. In this case, it will never change. The rotateAngle for your NorthNode handles the X,Z rotation only. The Tilt handles the Y, and would always stay the same also. The northNode's position should move in relation to the Camera at all times, so if the camera moves +5 units on the X axis, so does your North Node.

The Arrow just needs to be in front of the camera at all times, and on the same Y (constant TetherDistance and TetherAngle from the camera) and use LookAt(NorthNode)

The rest can be left up to your camera controls once you write the code to make your Arrow and NorthNode behave that way :)

Chulein

31-07-2008 08:18:54

Thank you Mephs :)

I played around with the code last night and so far i think im on the right track. What I did was to create a new scenenode and attach an "arrow" entity to this scenenode. next was to rotate the node so the model arrow always points north. then set the position of this node in front of the camera.
The arrow behaves exacly how i wanted, except that i need the arrow in the bottom right corner. I need to transform or do the vectors on this.

what i did


ent = mgr.CreateEntity("Pipes", "arrow.mesh");
ent.CastShadows = false;
win.ArrowNode = mgr.RootSceneNode.CreateChildSceneNode("Arrow");
win.ArrowNode.AttachObject(ent);
win.ArrowNode.Position = ((win.Camera.Position + win.Camera.Direction * 4)); //puts the arrownode just in front of the camera
win.ArrowNode.Rotate(new Vector3(1, 0, 0), 11);
win.ArrowNode.SetScale(0.01f, 0.02f, 0.01f);
win.ArrowNode = null;



Do you know what i need to do to get the vector direction to the right and then down from this position?

Thanks!

Mephs

31-07-2008 08:36:56


win.ArrowNode.Position = ((win.Camera.Position + win.Camera.Direction * 4));


I think you could use the camera's direction to create a vector based on that that would send it so many units "down" and "right" relational to the camera's facing. It's really late but if you don't figure it out by my lunch tomorrow I'll take some time and see if I can figure it out.

Chulein

31-07-2008 11:39:54

I cant figure it out how to find the vectors to get to bottom\right part of the screen.

Ive added a quick paint to show you what i mean.




lilljohan

31-07-2008 11:50:51

So you want to move it along the cameras x-axis?
I think this should work.

Arrow.Position = Camera.Position + (new Vector3(ArrowDistance * Camera.Orientation, 0, 0);

Chulein

31-07-2008 12:08:13

Hey lilljohan.

I didnt quite understand the code you just posted. the Camera.Orientation is a quaternian and doesnt works well as a parameter for a vector3()..
And yeah.. move it along the cameras x-axis is what i want.. and then down the y-axis to get to the bottom right corner.

Thanks.

lilljohan

31-07-2008 13:31:20

Opps ... made a little mistake =)
It should be:
Arrow.Position = Camera.Position;
Arrow.Translate(Camera.Orientation * (new Vector3(ArrowDistance , 0, 0));


This will move the arrow ArrowDistance-units in the camera x-axis. Or at least it does in my map editor =)

Chulein

31-07-2008 13:55:32

"Who da man!?" You da man lilljohan!

i used your code and it works perfectly!

This is how my final code looks like:

win.ArrowNode.Position = ((win.Camera.Position) + (win.Camera.Direction) * 6 + (win.Camera.Orientation * (new Vector3(3.4f, -2.0f, 0))));


Thank you so much.

lilljohan

31-07-2008 14:31:32

"Who da man!?" You da man lilljohan!

i used your code and it works perfectly!

This is how my final code looks like:

win.ArrowNode.Position = ((win.Camera.Position) + (win.Camera.Direction) * 6 + (win.Camera.Orientation * (new Vector3(3.4f, -2.0f, 0))));


Thank you so much.

:D

I can recommend this page if you hare having problems with quaternions in the future =) http://www.ogre3d.org/wiki/index.php/Quaternions