From 9342115b1ebc89a49ea0a084f4d5af9082a92698 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 13 May 2014 19:53:00 -0500 Subject: [PATCH 1/5] helper functions for AI: testing if an object is in line of sight (and optionally if it's enabled), if a point is clear, and if an object is within a given angular field of view (optionally specified beyond a stock 45 degrees, as well as optionally checking if the target is enabled) --- Engine/source/T3D/aiPlayer.cpp | 135 ++++++++++++++++++++++++++++----- Engine/source/T3D/aiPlayer.h | 3 + 2 files changed, 118 insertions(+), 20 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index 6d54358be..0b869c2a0 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -28,6 +28,10 @@ #include "T3D/gameBase/moveManager.h" #include "console/engineAPI.h" +static U32 AIPLAYER_LOSMASK = TerrainObjectType | WaterObjectType | + ShapeBaseObjectType | StaticShapeObjectType | + PlayerObjectType | ItemObjectType; + IMPLEMENT_CO_NETOBJECT_V1(AIPlayer); ConsoleDocClass( AIPlayer, @@ -417,28 +421,19 @@ bool AIPlayer::getAIMove(Move *movePtr) // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. - if (mAimObject) { - MatrixF eyeMat; - getEyeTransform(&eyeMat); - eyeMat.getColumn(3,&location); - Point3F targetLoc = mAimObject->getBoxCenter(); - - // This ray ignores non-static shapes. Cast Ray returns true - // if it hit something. - RayInfo dummy; - if (getContainer()->castRay( location, targetLoc, - StaticShapeObjectType | StaticObjectType | - TerrainObjectType, &dummy)) { - if (mTargetInLOS) { - throwCallback( "onTargetExitLOS" ); - mTargetInLOS = false; - } + if (mAimObject) + { + mTargetInLOS = checkInLos(mAimObject.getPointer(), false); + if (mTargetInLOS) + { + throwCallback("onTargetEnterLOS"); + mTargetInLOS = true; } else - if (!mTargetInLOS) { - throwCallback( "onTargetEnterLOS" ); - mTargetInLOS = true; - } + { + throwCallback("onTargetExitLOS"); + mTargetInLOS = false; + } } // Replicate the trigger state into the move so that @@ -610,3 +605,103 @@ DefineEngineMethod( AIPlayer, getAimObject, S32, (),, GameBase* obj = object->getAimObject(); return obj? obj->getId(): -1; } + +bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) +{ + if (!isServerObject()) return false; + if (!(bool(target))) return false; + if (_checkEnabled) + { + ShapeBase *shapeBaseCheck = dynamic_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + + RayInfo ri; + + disableCollision(); + + S32 mountCount = target->getMountedObjectCount(); + for (S32 i = 0; i < mountCount; i++) + { + target->getMountedObject(i)->disableCollision(); + } + Point3F muzzlePoint; + getMuzzlePointAI(0, &muzzlePoint); + bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), AIPLAYER_LOSMASK, &ri); + enableCollision(); + + for (S32 i = 0; i < mountCount; i++) + { + target->getMountedObject(i)->enableCollision(); + } + + if (hit) + { + if (target != dynamic_cast(ri.object)) hit = false; + } + + return hit; +} + +bool AIPlayer::checkLosClear(Point3F _pos) +{ + if (!isServerObject()) return false; + + RayInfo ri; + + disableCollision(); + + Point3F muzzlePoint; + getMuzzlePointAI(0, &muzzlePoint); + gServerContainer.castRay(muzzlePoint, _pos, AIPLAYER_LOSMASK, &ri); + bool emptySpace = bool(ri.object == NULL); + enableCollision(); + return emptySpace; +} + +DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool checkEnabled), (0, false), + "@brief Check for an object in line of sight.\n") +{ + return object->checkInLos(obj, checkEnabled); +} + +bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled = false) +{ + if (!isServerObject()) return false; + if (!(bool(target))) return false; + if (_checkEnabled) + { + ShapeBase *shapeBaseCheck = dynamic_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + + MatrixF cam = getTransform(); + Point3F camPos; + VectorF camDir; + + cam.getColumn(3, &camPos); + cam.getColumn(1, &camDir); + + camFov = mDegToRad(camFov) / 2; + + Point3F shapePos; + // Use the render transform instead of the box center + // otherwise it'll jitter. + MatrixF srtMat = target->getTransform(); + srtMat.getColumn(3, &shapePos); + VectorF shapeDir = shapePos - camPos; + // Test to see if it's within our viewcone, this test doesn't + // actually match the viewport very well, should consider + // projection and box test. + shapeDir.normalize(); + F32 dot = mDot(shapeDir, camDir); + return (dot > camFov); +} + +DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (0, 45, false), + "@brief Check for an object within a specified veiw cone.\n") +{ + return object->checkInFoV(obj, fov, checkEnabled); +} \ No newline at end of file diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h index 4e0bb5a3a..283db14db 100644 --- a/Engine/source/T3D/aiPlayer.h +++ b/Engine/source/T3D/aiPlayer.h @@ -80,6 +80,9 @@ public: void setAimLocation( const Point3F &location ); Point3F getAimLocation() const { return mAimLocation; } void clearAim(); + bool checkInLos(GameBase* target, bool _checkEnabled); + bool checkLosClear(Point3F _pos); + bool checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled); // Movement sets/gets void setMoveSpeed( const F32 speed ); From 8aebb67aa1c77c66ac7bf62b738830c9a34479c1 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 13 May 2014 22:45:25 -0500 Subject: [PATCH 2/5] conforms losmask to standard nomenclature --- Engine/source/T3D/aiPlayer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index 0b869c2a0..d3aed1fd4 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -28,7 +28,7 @@ #include "T3D/gameBase/moveManager.h" #include "console/engineAPI.h" -static U32 AIPLAYER_LOSMASK = TerrainObjectType | WaterObjectType | +static U32 sAIPlayerLoSMask = TerrainObjectType | WaterObjectType | ShapeBaseObjectType | StaticShapeObjectType | PlayerObjectType | ItemObjectType; @@ -628,7 +628,7 @@ bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) } Point3F muzzlePoint; getMuzzlePointAI(0, &muzzlePoint); - bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), AIPLAYER_LOSMASK, &ri); + bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), sAIPlayerLoSMask, &ri); enableCollision(); for (S32 i = 0; i < mountCount; i++) @@ -654,7 +654,7 @@ bool AIPlayer::checkLosClear(Point3F _pos) Point3F muzzlePoint; getMuzzlePointAI(0, &muzzlePoint); - gServerContainer.castRay(muzzlePoint, _pos, AIPLAYER_LOSMASK, &ri); + gServerContainer.castRay(muzzlePoint, _pos, sAIPlayerLoSMask, &ri); bool emptySpace = bool(ri.object == NULL); enableCollision(); return emptySpace; From ae55ad2b509be6c240bfd2c7528ad77f04ac224b Mon Sep 17 00:00:00 2001 From: Azaezel Date: Fri, 11 Jul 2014 06:58:19 -0500 Subject: [PATCH 3/5] revised checkInLos and CheckFoV. boith now take all parameters as optional as suggested, with a target value of NULL resulting in checking the present one. cleaned up internal usage of checkInLos, tightened the typemask used, and provided further documentation. --- Engine/source/T3D/aiPlayer.cpp | 61 +++++++++++++++++++++++----------- Engine/source/T3D/aiPlayer.h | 4 +-- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index d3aed1fd4..fd0c179a3 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -29,8 +29,7 @@ #include "console/engineAPI.h" static U32 sAIPlayerLoSMask = TerrainObjectType | WaterObjectType | - ShapeBaseObjectType | StaticShapeObjectType | - PlayerObjectType | ItemObjectType; + ShapeBaseObjectType | StaticShapeObjectType; IMPLEMENT_CO_NETOBJECT_V1(AIPlayer); @@ -423,13 +422,15 @@ bool AIPlayer::getAIMove(Move *movePtr) // which is not very accurate. if (mAimObject) { - mTargetInLOS = checkInLos(mAimObject.getPointer(), false); - if (mTargetInLOS) + if (checkInLos(mAimObject.getPointer())) { - throwCallback("onTargetEnterLOS"); - mTargetInLOS = true; + if (!mTargetInLOS) + { + throwCallback("onTargetEnterLOS"); + mTargetInLOS = true; + } } - else + else if (mTargetInLOS) { throwCallback("onTargetExitLOS"); mTargetInLOS = false; @@ -605,11 +606,16 @@ DefineEngineMethod( AIPlayer, getAimObject, S32, (),, GameBase* obj = object->getAimObject(); return obj? obj->getId(): -1; } - -bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) + +bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) { if (!isServerObject()) return false; - if (!(bool(target))) return false; + if (!(bool(target))) + { + target = mAimObject.getPointer(); + if (!(bool(target))) + return false; + } if (_checkEnabled) { ShapeBase *shapeBaseCheck = dynamic_cast(target); @@ -626,9 +632,18 @@ bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) { target->getMountedObject(i)->disableCollision(); } - Point3F muzzlePoint; - getMuzzlePointAI(0, &muzzlePoint); - bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), sAIPlayerLoSMask, &ri); + + Point3F checkPoint ; + if (_useMuzzle) + getMuzzlePointAI(0, &checkPoint ); + else + { + MatrixF eyeMat; + getEyeTransform(&eyeMat); + eyeMat.getColumn(3, &checkPoint ); + } + + bool hit = gServerContainer.castRay(checkPoint , target->getBoxCenter(), sAIPlayerLoSMask, &ri); enableCollision(); for (S32 i = 0; i < mountCount; i++) @@ -644,6 +659,7 @@ bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) return hit; } + bool AIPlayer::checkLosClear(Point3F _pos) { if (!isServerObject()) return false; @@ -660,13 +676,17 @@ bool AIPlayer::checkLosClear(Point3F _pos) return emptySpace; } -DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool checkEnabled), (0, false), - "@brief Check for an object in line of sight.\n") +DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool useMuzzle, bool checkEnabled),(NULL, false, false), + "@brief Check for an object in line of sight.\n" + "@obj Object to check. if blank it will check the current target.\n" + "@useMuzzle Use muzzle position (otherwise use eye position).\n" + "@checkEnabled check if the object is not disabled.\n") { - return object->checkInLos(obj, checkEnabled); + return object->checkInLos(obj, useMuzzle, checkEnabled); } -bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled = false) + +bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled) { if (!isServerObject()) return false; if (!(bool(target))) return false; @@ -700,8 +720,11 @@ bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled = fal return (dot > camFov); } -DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (0, 45, false), - "@brief Check for an object within a specified veiw cone.\n") +DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (NULL, 45.0f, false), + "@brief Check for an object within a specified veiw cone.\n" + "@obj Object to check. if blank it will check the current target.\n" + "@fov view angle (in degrees)\n" + "@checkEnabled check if the object is not disabled.\n") { return object->checkInFoV(obj, fov, checkEnabled); } \ No newline at end of file diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h index 283db14db..851482021 100644 --- a/Engine/source/T3D/aiPlayer.h +++ b/Engine/source/T3D/aiPlayer.h @@ -80,9 +80,9 @@ public: void setAimLocation( const Point3F &location ); Point3F getAimLocation() const { return mAimLocation; } void clearAim(); - bool checkInLos(GameBase* target, bool _checkEnabled); + bool checkInLos(GameBase* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); bool checkLosClear(Point3F _pos); - bool checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled); + bool checkInFoV(GameBase* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); // Movement sets/gets void setMoveSpeed( const F32 speed ); From 9bed39b7d08b52d6ec7f3ed642d0afa2d113175c Mon Sep 17 00:00:00 2001 From: Azaezel Date: Fri, 11 Jul 2014 07:47:31 -0500 Subject: [PATCH 4/5] logical inversion to stock functionality as requested. Now assumes if it has a target and does not hit anything Static by the time the ray terminates, then it must be the right target. --- Engine/source/T3D/aiPlayer.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index fd0c179a3..0c9b930fe 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -28,8 +28,7 @@ #include "T3D/gameBase/moveManager.h" #include "console/engineAPI.h" -static U32 sAIPlayerLoSMask = TerrainObjectType | WaterObjectType | - ShapeBaseObjectType | StaticShapeObjectType; +static U32 sAIPlayerLoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType; IMPLEMENT_CO_NETOBJECT_V1(AIPlayer); @@ -643,19 +642,13 @@ bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) eyeMat.getColumn(3, &checkPoint ); } - bool hit = gServerContainer.castRay(checkPoint , target->getBoxCenter(), sAIPlayerLoSMask, &ri); + bool hit = !gServerContainer.castRay(checkPoint, target->getBoxCenter(), sAIPlayerLoSMask, &ri); enableCollision(); for (S32 i = 0; i < mountCount; i++) { target->getMountedObject(i)->enableCollision(); } - - if (hit) - { - if (target != dynamic_cast(ri.object)) hit = false; - } - return hit; } From e332609003048069cd6d236d629e3d4b71b05efc Mon Sep 17 00:00:00 2001 From: Azaezel Date: Sun, 13 Jul 2014 12:24:17 -0500 Subject: [PATCH 5/5] requested alterations. bool AIPlayer::checkLosClear(Point3F _pos) removed as not fit for purpose at the present time. Something to revisit when I'm spread less thin, or give someone else a crack at it. --- Engine/source/T3D/aiPlayer.cpp | 73 +++++++++++++++------------------- Engine/source/T3D/aiPlayer.h | 1 - 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index 0c9b930fe..31ada9970 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -609,17 +609,22 @@ DefineEngineMethod( AIPlayer, getAimObject, S32, (),, bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) { if (!isServerObject()) return false; - if (!(bool(target))) + if (!target) { target = mAimObject.getPointer(); - if (!(bool(target))) + if (!target) return false; } if (_checkEnabled) { - ShapeBase *shapeBaseCheck = dynamic_cast(target); - if (shapeBaseCheck) - if (shapeBaseCheck->getDamageState() != Enabled) return false; + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase *shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + else + return false; } RayInfo ri; @@ -652,42 +657,34 @@ bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) return hit; } - -bool AIPlayer::checkLosClear(Point3F _pos) -{ - if (!isServerObject()) return false; - - RayInfo ri; - - disableCollision(); - - Point3F muzzlePoint; - getMuzzlePointAI(0, &muzzlePoint); - gServerContainer.castRay(muzzlePoint, _pos, sAIPlayerLoSMask, &ri); - bool emptySpace = bool(ri.object == NULL); - enableCollision(); - return emptySpace; -} - DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool useMuzzle, bool checkEnabled),(NULL, false, false), - "@brief Check for an object in line of sight.\n" - "@obj Object to check. if blank it will check the current target.\n" - "@useMuzzle Use muzzle position (otherwise use eye position).\n" - "@checkEnabled check if the object is not disabled.\n") + "@brief Check whether an object is in line of sight.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@useMuzzle Use muzzle position. Otherwise use eye position. (defaults to false).\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") { return object->checkInLos(obj, useMuzzle, checkEnabled); } - bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled) { if (!isServerObject()) return false; - if (!(bool(target))) return false; + if (!target) + { + target = mAimObject.getPointer(); + if (!target) + return false; + } if (_checkEnabled) { - ShapeBase *shapeBaseCheck = dynamic_cast(target); - if (shapeBaseCheck) - if (shapeBaseCheck->getDamageState() != Enabled) return false; + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase *shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + else + return false; } MatrixF cam = getTransform(); @@ -699,11 +696,7 @@ bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled) camFov = mDegToRad(camFov) / 2; - Point3F shapePos; - // Use the render transform instead of the box center - // otherwise it'll jitter. - MatrixF srtMat = target->getTransform(); - srtMat.getColumn(3, &shapePos); + Point3F shapePos = target->getBoxCenter(); VectorF shapeDir = shapePos - camPos; // Test to see if it's within our viewcone, this test doesn't // actually match the viewport very well, should consider @@ -714,10 +707,10 @@ bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled) } DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (NULL, 45.0f, false), - "@brief Check for an object within a specified veiw cone.\n" - "@obj Object to check. if blank it will check the current target.\n" - "@fov view angle (in degrees)\n" - "@checkEnabled check if the object is not disabled.\n") + "@brief Check whether an object is within a specified veiw cone.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@fov view angle in degrees.(Defaults to 45)\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") { return object->checkInFoV(obj, fov, checkEnabled); } \ No newline at end of file diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h index 851482021..86da43edb 100644 --- a/Engine/source/T3D/aiPlayer.h +++ b/Engine/source/T3D/aiPlayer.h @@ -81,7 +81,6 @@ public: Point3F getAimLocation() const { return mAimLocation; } void clearAim(); bool checkInLos(GameBase* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); - bool checkLosClear(Point3F _pos); bool checkInFoV(GameBase* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); // Movement sets/gets