At some time we all end up writing some kind of level editor, and one of the

issues is moving objects around the scene.

3D editors get around this by having multiple views so you can move the

object in the cardinal directions, this isn't always ideal.

My solution relies on the objects being on a defined plane, but it can be

extended to handle uneven terrain.

The idea is simple, and the code not much more complex.

Throw a ray from the mouse position through the scene. You are probably

already doing this for your picking. Find the intersection of this ray and the

plane.

Place your object there.

Simples.

In fact I do it a little differently, I add the difference between the object

intersection and the new, but that is a triviality.

The advantage of this is that it is view independant, so if you move the

camera, the code still works.

issues is moving objects around the scene.

3D editors get around this by having multiple views so you can move the

object in the cardinal directions, this isn't always ideal.

My solution relies on the objects being on a defined plane, but it can be

extended to handle uneven terrain.

The idea is simple, and the code not much more complex.

Throw a ray from the mouse position through the scene. You are probably

already doing this for your picking. Find the intersection of this ray and the

plane.

Place your object there.

Simples.

In fact I do it a little differently, I add the difference between the object

intersection and the new, but that is a triviality.

The advantage of this is that it is view independant, so if you move the

camera, the code still works.

//Intersection plane

Plane p = new Plane(Vector3.UnitY, 0);

// new mouse ray

Ray nmray = CalculateCursorRay(mpos,

editCamera.Projection, editCamera.View);

Ray omray = CalculateCursorRay(ompos,

editCamera.Projection, editCamera.View);

// find the old and new intersection points

float? op = omray.Intersects(p);

float? np = nmray.Intersects(p);

if ((op != null) && (np != null))

{

Vector3 opos = omray.Position + (float)op * omray.Direction;

Vector3 npos = nmray.Position + (float)np * nmray.Direction;

npos -= opos;

SelectedObject.Position += npos;

}

ompos = mpos;

Plane p = new Plane(Vector3.UnitY, 0);

// new mouse ray

Ray nmray = CalculateCursorRay(mpos,

editCamera.Projection, editCamera.View);

Ray omray = CalculateCursorRay(ompos,

editCamera.Projection, editCamera.View);

// find the old and new intersection points

float? op = omray.Intersects(p);

float? np = nmray.Intersects(p);

if ((op != null) && (np != null))

{

Vector3 opos = omray.Position + (float)op * omray.Direction;

Vector3 npos = nmray.Position + (float)np * nmray.Direction;

npos -= opos;

SelectedObject.Position += npos;

}

ompos = mpos;

The cursor ray calculation is standard, but it is here for reference

private Ray CalculateCursorRay(Vector2 mpos,

Matrix projectionMatrix, Matrix viewMatrix)

{

// create 2 positions in screenspace using the cursor // position. 0 is as close as possible to the camera,

// 1 is as far away as possible.

Vector3 nearSource = new Vector3(mpos, 0f);

Vector3 farSource = new Vector3(mpos, 1f);

// use Viewport.Unproject to tell what those two screen

// space positions would be in world space. we'll need

// the projection matrix and view matrix, which we have

// We also need a world matrix, which can just be identity.

Vector3 nearPoint = GraphicsDevice.Viewport.Unproject(nearSource, ProjectionMatrix, viewMatrix, Matrix.Identity);

Vector3 farPoint = GraphicsDevice.Viewport.Unproject(farSource, ProjectionMatrix, viewMatrix, Matrix.Identity);

// find the direction vector that goes from the nearPoint // to the farPoint and normalize it....

Vector3 direction = farPoint - nearPoint;

direction.Normalize();

// and then create a new ray using nearPoint as the

// source.

return new Ray(nearPoint, direction);

}

Matrix projectionMatrix, Matrix viewMatrix)

{

// create 2 positions in screenspace using the cursor // position. 0 is as close as possible to the camera,

// 1 is as far away as possible.

Vector3 nearSource = new Vector3(mpos, 0f);

Vector3 farSource = new Vector3(mpos, 1f);

// use Viewport.Unproject to tell what those two screen

// space positions would be in world space. we'll need

// the projection matrix and view matrix, which we have

// We also need a world matrix, which can just be identity.

Vector3 nearPoint = GraphicsDevice.Viewport.Unproject(nearSource, ProjectionMatrix, viewMatrix, Matrix.Identity);

Vector3 farPoint = GraphicsDevice.Viewport.Unproject(farSource, ProjectionMatrix, viewMatrix, Matrix.Identity);

// find the direction vector that goes from the nearPoint // to the farPoint and normalize it....

Vector3 direction = farPoint - nearPoint;

direction.Normalize();

// and then create a new ray using nearPoint as the

// source.

return new Ray(nearPoint, direction);

}

This is a very simple piece of code, but the number of times I see people

struggling with it, I thought it was a good idea to blog it for other people to

reference.

struggling with it, I thought it was a good idea to blog it for other people to

reference.