GetTerrainHeight for Atlas
by Dave Young · in Torque Game Engine Advanced · 09/28/2007 (7:03 am) · 6 replies
I'm in quick need of a getTerrainHeight for a position, can't find something like it for Atlas.
Intent is to get the terrain height and place players/ai above the terrain when they spawn in, if nothing exists under their feet to prvent falling through the world.
Did I miss one?
Intent is to get the terrain height and place players/ai above the terrain when they spawn in, if nothing exists under their feet to prvent falling through the world.
Did I miss one?
#2
09/28/2007 (7:18 am)
J, that was a record time response!! Thank you very much
#3
09/28/2007 (7:46 am)
Np, hope it helped
#4
I realized I need interiors too, so I am breaking it out into separate loops (this one was made backwards compatible for TGE, I realize there's not usually multiple terrain types in TGE, but was breaking out separate loops for object types:
1) This doesn't need to be a performant function
2) I need to handle situations differently for object types
but here is my version:
09/28/2007 (8:30 am)
It did, very much..I realized I need interiors too, so I am breaking it out into separate loops (this one was made backwards compatible for TGE, I realize there's not usually multiple terrain types in TGE, but was breaking out separate loops for object types:
1) This doesn't need to be a performant function
2) I need to handle situations differently for object types
but here is my version:
ConsoleMethod(Player,getHeightUnderneath,F32,2,2,"Player::getHeightUnderneath()")
{
Point3F pos;
object->getTransform().getColumn(3, &pos);
return object->getHeightUnderneath(pos.x,pos.y, pos.z);
}
F32 Player::getHeightUnderneath(F32 X, F32 Y, F32 Z)
{
F32 retval = 0;
GameConnection* conn;
conn = GameConnection::getConnectionToServer();
if (!conn)
return 0.0;
F32 thighest = -10000.0f;
F32 ihighest = -10000.0f;
F32 highest = -10000.0f;
RayInfo rayInfo;
for (SimSetIterator itr(conn); *itr; ++itr)
{
if ((*itr)->getType() & TerrainObjectType)
{
gClientContainer.castRay(Point3F(X, Y, Z), Point3F(X, Y, -10000), TerrainObjectType, &rayInfo);
if(rayInfo.point.z > -10000 && rayInfo.point.z < 10000)
{
//Con::errorf("Casting terrain ray results (me) %f %f",Z,rayInfo.point.z);
if(rayInfo.point.z > thighest){
thighest = rayInfo.point.z;
//Con::errorf("Casting terrain highest %f",thighest);
}
}
}
}
for (SimSetIterator itr(conn); *itr; ++itr)
{
if ((*itr)->getType() & InteriorObjectType)
{
gClientContainer.castRay(Point3F(X, Y, Z), Point3F(X, Y, -10000), InteriorObjectType, &rayInfo);
if(rayInfo.point.z > -10000 && rayInfo.point.z < 10000)
{
//Con::errorf("Casting interior ray results (me) %f %f",Z,rayInfo.point.z);
if(rayInfo.point.z > ihighest){
ihighest = rayInfo.point.z;
//Con::errorf("Casting interior highest %f",ihighest);
}
}
}
}
if(ihighest==thighest)
{
//Con::errorf("Effectively inf result");
return highest;
}
if(ihighest > thighest)
highest = ihighest;
else
highest = thighest;
return highest;
}
#5
Here's my version of the engine code, verified to work in TGEA:
And here's the script code that I put into our setupPlayer() function to call it properly and move the player above the terrain or interior if applicable:
09/28/2007 (6:07 pm)
I found a problem with the code in the case where the castRay() didn't find a collision, it would return a garbage number which would usually be interpreted as a 0 height. So it's important to test the return value from castRay() to ensure that it actually found something before using the resulting RayInfo.Here's my version of the engine code, verified to work in TGEA:
ConsoleMethod(Player,getHeightUnderneath,F32,2,2,"Player::getHeightUnderneath()")
{
Point3F pos;
object->getTransform().getColumn(3, &pos);
return object->getHeightUnderneath(pos.x, pos.y, pos.z);
}
ConsoleMethod(Player,getHighestPoint,F32,2,2,"Player::getHighestPoint()")
{
Point3F pos;
object->getTransform().getColumn(3, &pos);
return object->getHeightUnderneath(pos.x, pos.y, 10000);
}
F32 Player::getHeightUnderneath(F32 X, F32 Y, F32 Z)
{
F32 retval = 0;
GameConnection* conn;
conn = GameConnection::getConnectionToServer();
if (!conn)
return 0.0;
F32 thighest = -10000.0f;
F32 ihighest = -10000.0f;
F32 highest = -10000.0f;
RayInfo rayInfo;
for (SimSetIterator itr(conn); *itr; ++itr)
{
static U32 sTerrainCollisionMask = (AtlasObjectType | TerrainObjectType);
if ((*itr)->getType() & sTerrainCollisionMask)
{
if (gClientContainer.castRay(Point3F(X, Y, Z), Point3F(X, Y, -10000), sTerrainCollisionMask, &rayInfo))
{
Con::errorf("Found a terrain below the start point.");
if(rayInfo.point.z > -10000 && rayInfo.point.z < 10000)
{
Con::errorf("Starting height: %f, Terrain height: %f", Z, rayInfo.point.z);
if(rayInfo.point.z > thighest){
thighest = rayInfo.point.z;
Con::errorf("Replacing highest terrain point with: %f", thighest);
}
}
}
}
}
RayInfo rayInfo2;
for (SimSetIterator itr(conn); *itr; ++itr)
{
if ((*itr)->getType() & InteriorObjectType)
{
if (gClientContainer.castRay(Point3F(X, Y, Z), Point3F(X, Y, -10000), InteriorObjectType, &rayInfo2))
{
Con::errorf("Found an interior below the start point.");
if(rayInfo2.point.z > -10000 && rayInfo2.point.z < 10000)
{
Con::errorf("Starting height: %f, Interior height: %f", Z, rayInfo2.point.z);
if(rayInfo2.point.z > ihighest){
ihighest = rayInfo2.point.z;
Con::errorf("Replacing highest interior point with: %f", ihighest);
}
}
}
}
}
if (ihighest == thighest)
{
Con::errorf("Effectively inf result");
return highest;
}
if (ihighest > thighest)
highest = ihighest;
else
highest = thighest;
return highest;
}And here's the script code that I put into our setupPlayer() function to call it properly and move the player above the terrain or interior if applicable:
// check distance to terrain or an interior:
%player.setTransform(%player.location);
%underheight = %player.getHeightUnderneath();
echo("underheight (from getHeightUnderneath()): " @ %underheight);
if (%underheight < -1000)
{
// we need to move the player above the terrain...
%underheight = %player.getHighestPoint();
echo("underheight (from getHighestPoint()): " @ %underheight);
%myPosX = getWord(%player.location, 0);
%myPosY = getWord(%player.location, 1);
%myPosZ = getWord(%player.location, 2);
%myPosRest = getWords(%player.location, 3);
%underheight += 0.2;
%myLoc = %myPosX SPC %myPosY SPC %underheight SPC %myPosRest;
}
else
%myLoc = %player.location;
//%player.location = setAboveTerrain(%player.location);
echo("Moving Player to " @ %myLoc);
%player.setTransform(%myLoc);
#6
This one really does work this time, even on a Dedicated Server setup.
10/04/2007 (2:38 pm)
One last (hopefully) bugfix. Since this is a server function only, and we're just sending in coordinates and asking for the terrain height, there's actually no client involved, so this function will fail when using it with a dedicated server setup. We need to replace all client references with equivalent server references. Thanks to Dave Young for pointing this out to me, and pointing out a big part of the resulting solution, which is a new version of the engine function as follows:F32 Player::getHeightUnderneath(F32 X, F32 Y, F32 Z)
{
F32 retval = 0;
//GameConnection* conn;
//conn = GameConnection::getConnectionToServer();
//if (!conn)
// return 0.0;
F32 thighest = -10000.0f;
F32 ihighest = -10000.0f;
F32 highest = -10000.0f;
RayInfo rayInfo;
//for (SimSetIterator itr(conn); *itr; ++itr)
for (SimSetIterator itr(Sim::getRootGroup()); *itr; ++itr)
{
static U32 sTerrainCollisionMask = (AtlasObjectType | TerrainObjectType);
if ((*itr)->getType() & sTerrainCollisionMask)
{
if (gServerContainer.castRay(Point3F(X, Y, Z), Point3F(X, Y, -10000), sTerrainCollisionMask, &rayInfo))
{
Con::errorf("Found a terrain below the start point.");
if(rayInfo.point.z > -10000 && rayInfo.point.z < 10000)
{
Con::errorf("Starting height: %f, Terrain height: %f", Z, rayInfo.point.z);
if(rayInfo.point.z > thighest){
thighest = rayInfo.point.z;
Con::errorf("Replacing highest terrain point with: %f", thighest);
}
}
}
}
}
RayInfo rayInfo2;
//for (SimSetIterator itr(conn); *itr; ++itr)
for (SimSetIterator itr(Sim::getRootGroup()); *itr; ++itr)
{
if ((*itr)->getType() & InteriorObjectType)
{
if (gServerContainer.castRay(Point3F(X, Y, Z), Point3F(X, Y, -10000), InteriorObjectType, &rayInfo2))
{
Con::errorf("Found an interior below the start point.");
if(rayInfo2.point.z > -10000 && rayInfo2.point.z < 10000)
{
Con::errorf("Starting height: %f, Interior height: %f", Z, rayInfo2.point.z);
if(rayInfo2.point.z > ihighest){
ihighest = rayInfo2.point.z;
Con::errorf("Replacing highest interior point with: %f", ihighest);
}
}
}
}
}
if (ihighest == thighest)
{
Con::errorf("Effectively inf result");
return highest;
}
if (ihighest > thighest)
highest = ihighest;
else
highest = thighest;
return highest;
}This one really does work this time, even on a Dedicated Server setup.
Torque Owner JPaxson
F32 getTerrainHeightAtPoint(F32 X, F32 Y) { F32 retval = 0; GameConnection* conn; conn = GameConnection::getConnectionToServer(); if (!conn) return 0.0; AtlasInstance* currentAtlasBlock; RayInfo rayInfo; for (SimSetIterator itr(conn); *itr; ++itr) { // Make sure that the object is an Atlas Object if ((*itr)->getType() & AtlasObjectType) { currentAtlasBlock = (AtlasInstance*)(*itr); gClientContainer.castRay(Point3F(X, Y, 10000), Point3F(X, Y, -10000), AtlasObjectType, &rayInfo); if(rayInfo.point.z > -10000 && rayInfo.point.z < 10000) { retval = rayInfo.point.z; break; } } } return retval; }