Cast Ray to a position on a Plane
by Christopher Beckford · in Torque Game Engine Advanced · 11/11/2009 (1:53 pm) · 5 replies
Hi Everyone, I'm currently working on an RTS set in space using TGEA. The control system is much like any other RTS with combat happening on a single plane (ships cannot move along the Z axis). I'm currently working on unit movement (using a fairly standard AIPlayer). At the moment once a unit is selected, I can click on another object and a ray is cast to get the object I clicked on, & return its position, thus commanding my unit to goto the position of the other object, but the problem is when I just want to move it to a point in space.
If I have terrain in my mission it is easy, as I can cast a ray & get the point on the terrain that it hits and move my unit there. Here is my code to handle that...
However since it is based in space, I have removed the terrain from the mission.
Therefore does anyone know of a way to cast a ray to a horizonal (and invisible) plane centered at 0,0,0 to determine where the ray would intersect that plane? Thanks for any help.
If I have terrain in my mission it is easy, as I can cast a ray & get the point on the terrain that it hits and move my unit there. Here is my code to handle that...
function serverCmdMoveToObject(%client, %mouseVec, %cameraPoint)
{
//Determine how far should the picking ray extend into the world?
%selectRange = 1500;
// scale mouseVec to the range the player is able to select with mouse
%mouseScaled = VectorScale(%mouseVec, %selectRange);
// cameraPoint = the world position of the camera
// rangeEnd = camera point + length of selectable range
%rangeEnd = VectorAdd(%cameraPoint, %mouseScaled);
// Search for anything that is selectable – below are some examples
%searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::CorpseObjectType |
$TypeMasks::ItemObjectType | $TypeMasks::TriggerObjectType |
$TypeMasks::StaticShapeObjectType | $TypeMasks::StaticObjectType
| $TypeMasks::TerrainObjectType ;
// Search for objects within the range that fit the masks above
%scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks);
// a target in range was found so return it
if (%scanTarg)
{
%targetObject = firstWord(%scanTarg);
%client.getSelectedObject().setMoveDestination(getWords(%scanTarg, 1, 3) );
}
}However since it is based in space, I have removed the terrain from the mission.
Therefore does anyone know of a way to cast a ray to a horizonal (and invisible) plane centered at 0,0,0 to determine where the ray would intersect that plane? Thanks for any help.
#2
11/12/2009 (5:21 am)
Thanks that's just what I needed. :)
#3
02/18/2011 (1:31 am)
Hi,Christopher Beckford,Would you like me enjoy your object selection resource for TGEA ? I want to implement object selection into TGEA 1.8.2. But I encounter a problem when I finish all steps of the solution which provided by Dave Myers.I get the correct object ID which I selected, but the bounding box is not shown. why? would you help me? Thanks very much.My email is : torque_sdk@163.com. I do not know how to contact you ,so I add a "Reply" here,Looking forward to your reply.Thanks again.
#4
I had a similar issue when I was working on this. I couldn't see the bounding box of the selected objects. However, eventually I noticed that the bounding box was being displayed, but it was always black, making it hard to see against the blackness of space.
I didn't worry too much about that problem though, because I made my own selection icons that were drawn below the selected object.
02/22/2011 (1:32 am)
Hi Ji,I had a similar issue when I was working on this. I couldn't see the bounding box of the selected objects. However, eventually I noticed that the bounding box was being displayed, but it was always black, making it hard to see against the blackness of space.
I didn't worry too much about that problem though, because I made my own selection icons that were drawn below the selected object.
#5
I am very glad to see your reply.I found that ShapeBase::renderObject() is not called so that the bounding box is not displayed. So I set GameBase::gShowBoundingBox to "true" in gameBase.cpp then the following code in ShapeBase::prepRenderImage() in shapeBase.cpp can be executed :
// render debug
if( gShowBoundingBox )
{
ObjectRenderInst *ri = gRenderInstManager->allocInst<ObjectRenderInst>();
ri->mRenderDelegate = mRenderDelegate;
ri->state = state;
ri->type = RenderPassManager::RIT_Object;
// not referenced anywhere (since TGEA 1.7)
//ri->mountedObjIdx = -1;
gRenderInstManager->addInst( ri );
}
Then shapeBase::renderObject() is called.In shapeBase::renderObject(),I control bounding box displayed for selected object, so I add the following code:
if( (!mShapeInstance || gShowBoundingBox) && (getId() == selectedId))
{
...
}
After that I see white bounding box.
I am a novice for TGEA and poor knowledge of C++. Am I right?Would you like me enjoy your resource of adding selection icons below the selected object?Does not matter if you can't.Thanks again for your reply.
03/03/2011 (7:33 am)
Hi Christopher,I am very glad to see your reply.I found that ShapeBase::renderObject() is not called so that the bounding box is not displayed. So I set GameBase::gShowBoundingBox to "true" in gameBase.cpp then the following code in ShapeBase::prepRenderImage() in shapeBase.cpp can be executed :
// render debug
if( gShowBoundingBox )
{
ObjectRenderInst *ri = gRenderInstManager->allocInst<ObjectRenderInst>();
ri->mRenderDelegate = mRenderDelegate;
ri->state = state;
ri->type = RenderPassManager::RIT_Object;
// not referenced anywhere (since TGEA 1.7)
//ri->mountedObjIdx = -1;
gRenderInstManager->addInst( ri );
}
Then shapeBase::renderObject() is called.In shapeBase::renderObject(),I control bounding box displayed for selected object, so I add the following code:
if( (!mShapeInstance || gShowBoundingBox) && (getId() == selectedId))
{
...
}
After that I see white bounding box.
I am a novice for TGEA and poor knowledge of C++. Am I right?Would you like me enjoy your resource of adding selection icons below the selected object?Does not matter if you can't.Thanks again for your reply.
Torque 3D Owner Scott Richards
Basically, to find a ray/plane intersect you compare the distance of the ray start to the plane with the distance the ray travels along the plane normal. That gives you a percentage along the ray at which it intersects the plane. Then you just multiply the ray vector by that value, and add the resulting vector to the start point. It requires a couple dot products and a bit of vector math, but.. In your case it's even easier than that. If you can be sure you're only interested in a flat plane at "0 0 0" you can simplify the math significantly. The plane normal would be "0 0 1" so right away we can safely ignore the x and y values. They'll just be 0s. And with the plane at "0 0 0" there is no offset so the start point z value is the distance to the plane. So:
function intersectWorldFloor(%start, %end) { // Find intersect time (t) as: // distance of %start to plane / ray travel along plane normal // distance of %start == %start.z (plane is at world "0 0 0") // travel along normal == z travel (plane normal is "0 0 1") %start_z = getWord(%start, 2); %end_z = getWord(%end, 2); %t = %start_z / (%start_z - %end_z); // %t value > 1 or < 0 indicates the intersect point was beyond // one end of the ray. So no hit. (unless you want an infinite ray) if (%t > 1 || %t < 0) return ""; // Find ray vector and scale by %t %ray = VectorSub(%end, %start); // yeah that looks backwards, but it's correct %ray = VectorScale(%ray, %t); // Find intersect as %start + %ray return VectorAdd(%start + %ray); } // Given %start and %end (in World coordinates) if ((%hitPoint = intersectWorldFloor(%start, %end)) !$= "") { // do something with %hitPoint }edit: Corrected code above. Forgot script "vectors" cannot be accessed as %vector.x :P