Page 1 of 1

line/plane intersection

Posted: Thu Dec 20, 2007 10:14 am
by pippy
I'm trying intersect a line with a plane. The line is the camera position to the mouse position, and to the plane is the where X and Z equal 0. The result is a 3d position

The desired effect would be the user to drag over units in a 3d space and form a square to where the mouse is on the plane in 3d



What would be the best way to do this?

So far I have thought of 3 possible options:

1) use smgr->getSceneCollisionManager ()->getRayFromScreenCoordinates(), then work out where the line returned intersects with the plane

2) Use the same method as used in the number 07 tutorial, but write a hack to offset the mouse position. (easy to write, but would cause mayhem if the user changes screen resolution)

3) write my own function (7th form maths anyone?)

Posted: Thu Dec 20, 2007 10:28 am
by JP
1) and 2) are exactly the same i would think... not sure why you'd want to hack your own version of getRayFromScreenCoordinates when it's provided for you!

I guess you'd have to do 4 ray casts, one for each corner of the box you're drawing on the screen with the mouse in order to calculate the box's dimensions in 3D space.

Posted: Thu Dec 20, 2007 10:58 am
by pippy
JP Thanks for your response. I had a few beers and got cracking on the problem and ended up with much what you said. For those who where wondering this is my code. (Selector is just an extension of scene node, position is where its at)

Code: Select all

if(selector == NULL){
						selector = new Selector(mysmgr->getRootSceneNode(), mysmgr, 100000);
					}
					// where the mouse is from the camers (forms line)
					core::line3d< f32 > lol = mysmgr->getSceneCollisionManager()->
						getRayFromScreenCoordinates(from, camera);
					
					// plane, first position, second normal
					core::plane3d< f32 > ground(core::vector3d<f32>(0, 0, 0), core::vector3d<f32>(0, 10, 0));

					// where the line hits the ground
					core::vector3d< f32 >intersection(0,2,0);

					// if it intersects, it will return true and store intersetion where the line meets
					if(ground.getIntersectionWithLimitedLine(lol.start,lol.end,intersection)){
						intersection.Y += 2; // so it sticks above the ground
					}
                    selector->setPosition (intersection);

Posted: Thu Dec 20, 2007 11:25 am
by rogerborg
WTF... someone figured out the answer to their own problem? Whoa... feeling dizzy... need to sit down... :P

Heh, you actually posted your solution as I was composing a reply suggesting exactly that solution, based on this previous answer.

Your solution looks great, although if I have to nitpick (and I must, I really must!) you might as well pass 0, 1, 0 as the plane normal, and use getIntersectionWithLine() rather than getIntersectionWithLimitedLine() as the actual end points of the ray returned from getRayFromScreenCoordinates() probably aren't meaningful for the purposes of this test (although they will be if your camera ever goes below your ground plane).

Still, kudos for that solution; I think you're on the success path. ;)

Posted: Thu Dec 20, 2007 2:18 pm
by MasterGod
Sorry but I didn't understood what you're trying to achieve?

Posted: Thu Dec 20, 2007 2:58 pm
by cdrwolfe
My guess would be,

That in a normal RTS you drag the mouse to create a selection box which is represented/displayed to the screen coordinates.

While here you have a 3D plane i.e flat ground where your unit may be situated and when drawing the selection box it is represented on the world? coordinates or what ever it is called, and essentially it is mapped to the plane.

damn thats a bad explanation by me :)

Regards Wolfe

Posted: Fri Dec 21, 2007 3:53 am
by pippy
rogerborg: Thanks for your reply, any help is great, it improves my coding style (I've been coding c++ for only half a year)

Here's my function as of now:

Code: Select all

// User over units
void DragFrom(scene::ICameraSceneNode* camera, 
	core::position2d<s32> from){
	
	if(selector == NULL){
		selector = new Selector(mysmgr->getRootSceneNode(), mysmgr, 100000);
	}
	// line from the camera to the mouse (technically its a ray)
	core::line3d< f32 > lol = mysmgr->getSceneCollisionManager()->
		getRayFromScreenCoordinates(from, camera);
	
	// plane, first position, second normal
	core::plane3d< f32 > ground(core::vector3d<f32>(0, 0, 0), core::vector3d<f32>(0, 1, 0));

	// where the line hits the ground
	core::vector3d< f32 >intersection(0,2,0);

	// if it intersects, it will return true and store intersetion where the line meets
	if(ground.getIntersectionWithLine(lol.start,lol.end,intersection)){
		intersection.Y += 2; // so it sticks above the ground
	}
    selector->setPosition (intersection);
}
In order to make it so that the user can select over units I'll need to:
1) take where the mouse started dragging and where its draggin to now
2) stretch the square to fit over the unit

I'll post back in 20 minutes with the code =D

Posted: Fri Dec 21, 2007 4:11 am
by pippy
Here's my 1337 code:

Code: Select all

				// Calculates where the line intersects the ground plane
				core::vector3d< f32 > IntersectPlane(core::line3d< f32 > myLine){
					core::vector3d< f32 >intersection(0, 0, 0);

				   // plane, first position, second normal. 
				   // You can of course replace it with your own plane
				   core::plane3d< f32 > ground(
					   core::vector3d<f32>(0, 0, 0), 
					   core::vector3d<f32>(0, 1, 0)); 

					// calculates the intersection
					ground.getIntersectionWithLine(myLine.start,myLine.end,intersection);

					return intersection; // so it sticks above the ground
				}

				// User drags from X to Y
				void DragFrom(scene::ICameraSceneNode* camera, 
					core::position2d<s32> from, core::position2d<s32> to){

					// create new selector class 
					// (extends ISceneNode with custom function strechPosition) 
					if(selector == NULL){
						selector = new Selector(mysmgr->getRootSceneNode(), mysmgr, 100000);
					}

					// line from the camera to the mouse (technically its a ray)
					core::line3d< f32 > cameraRay = mysmgr->getSceneCollisionManager()->
						getRayFromScreenCoordinates(to, camera);
					
					// where the line hits the ground
					core::vector3d< f32 > intersection = IntersectPlane(cameraRay);

					// line from the camera to the mouse (technically its a ray)
					core::line3d< f32 > cameraRayTo = mysmgr->getSceneCollisionManager()->
						getRayFromScreenCoordinates(from, camera);
					
					// where the line hits the ground
					core::vector3d< f32 >intersectionTo = IntersectPlane(cameraRayTo);

					// Strech the selector unit
					selector->strechPosition(intersection,intersectionTo);
				}

				// deletes the draginging thing
				void removeDrag(){
					if(selector != NULL){
						selector->remove();
						selector->drop();
						selector= NULL;
					}
				}