You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
312 lines
11 KiB
312 lines
11 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "plLOSDispatch.h" |
|
#include "plSimulationMgr.h" |
|
#include "plgDispatch.h" |
|
#include "plMessage/plLOSRequestMsg.h" |
|
#include "plMessage/plLOSHitMsg.h" |
|
#include "pnKeyedObject/plFixedKey.h" |
|
#include "pnSceneObject/plSceneObject.h" |
|
#include "plModifier/plLogicModifier.h" |
|
#include "plPXPhysical.h" |
|
#include "plPXPhysicalControllerCore.h" |
|
#include "plPXConvert.h" |
|
|
|
#include "plAvatar/plAvatarMgr.h" |
|
#include "plAvatar/plArmatureMod.h" |
|
|
|
#include <NxPhysics.h> |
|
|
|
#include "plProfile.h" |
|
plProfile_Extern(LineOfSight); |
|
|
|
class myRaycastReport : public NxUserRaycastReport |
|
{ |
|
public: |
|
void InitCast(plSimDefs::plLOSDB db, plLOSRequestMsg::TestType type) |
|
{ |
|
fDB = db; |
|
fType = type; |
|
fHitObj = nil; |
|
fNormal.Set(0, 0, 0); |
|
fPoint.Set(0, 0, 0); |
|
fDist = FLT_MAX; |
|
} |
|
|
|
bool GotHit() { return fDist != FLT_MAX; } |
|
plKey GetObj() { return fHitObj; } |
|
float GetDistance() { return fDist; } |
|
const hsVector3& GetNormal() { return fNormal; } |
|
const hsPoint3& GetPoint() { return fPoint; } |
|
void ResetHitObj(){fHitObj=nil;} |
|
private: |
|
virtual bool onHit(const NxRaycastHit& hit) |
|
{ |
|
NxActor& hitActor = hit.shape->getActor(); |
|
plPXPhysical* phys = (plPXPhysical*)hitActor.userData; |
|
|
|
plKey objKey = nil; |
|
uint16_t objDB = plSimDefs::kLOSDBNone; |
|
|
|
if (phys) |
|
{ |
|
objKey = phys->GetObjectKey(); |
|
objDB = phys->GetAllLOSDBs(); |
|
} |
|
else |
|
{ |
|
bool isController; |
|
plPXPhysicalControllerCore* controller = plPXPhysicalControllerCore::GetController(hitActor,&isController); |
|
if (controller) |
|
{ |
|
objKey = controller->GetOwner(); |
|
objDB = controller->GetLOSDB(); |
|
} |
|
} |
|
|
|
// is the object's physic enabled and is it in the database that we are looking for? |
|
if ( !phys || !phys->GetProperty(plSimulationInterface::kDisable) ) |
|
{ |
|
if ( (fDB & objDB) != 0) |
|
{ |
|
if (fType == plLOSRequestMsg::kTestAny || hit.distance < fDist) |
|
{ |
|
// need one more test... if it is a clickable need to see if it is enabled |
|
bool disabled = false; |
|
if ( objKey ) |
|
{ |
|
plSceneObject* so = plSceneObject::ConvertNoRef( objKey->GetObjectPtr() ); |
|
if (so) |
|
{ |
|
int i; |
|
for ( i=0; i < so->GetNumModifiers(); i++) |
|
{ |
|
plLogicModifier* lo = (plLogicModifier*)plLogicModifier::ConvertNoRef(so->GetModifier(i) ); |
|
if (lo) |
|
{ |
|
disabled = lo->Disabled(); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (!disabled) |
|
{ |
|
fHitObj = objKey; |
|
fNormal.Set(hit.worldNormal.x, hit.worldNormal.y, hit.worldNormal.z); |
|
fPoint.Set(hit.worldImpact.x, hit.worldImpact.y, hit.worldImpact.z); |
|
fDist = hit.distance; |
|
|
|
if (fType == plLOSRequestMsg::kTestAny) |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
plSimDefs::plLOSDB fDB; |
|
plLOSRequestMsg::TestType fType; |
|
|
|
plKey fHitObj; |
|
hsVector3 fNormal; |
|
hsPoint3 fPoint; |
|
float fDist; |
|
} gMyReport; |
|
|
|
plLOSDispatch::plLOSDispatch() |
|
{ |
|
RegisterAs(kLOSObject_KEY); |
|
plgDispatch::Dispatch()->RegisterForExactType(plLOSRequestMsg::Index(), GetKey()); |
|
} |
|
|
|
plLOSDispatch::~plLOSDispatch() |
|
{ |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plLOSRequestMsg::Index(), GetKey()); |
|
} |
|
|
|
bool plLOSDispatch::MsgReceive(plMessage* msg) |
|
{ |
|
|
|
plLOSRequestMsg* requestMsg = plLOSRequestMsg::ConvertNoRef(msg); |
|
if (requestMsg) |
|
{ |
|
plProfile_BeginTiming(LineOfSight); |
|
|
|
plSimulationMgr* sim = plSimulationMgr::GetInstance(); |
|
|
|
plKey worldKey = requestMsg->fWorldKey; |
|
if (!worldKey) |
|
{ |
|
plArmatureMod* av = plAvatarMgr::GetInstance()->GetLocalAvatar(); |
|
if ( av && av->GetController() ) |
|
worldKey = av->GetController()->GetSubworld(); |
|
} |
|
|
|
hsPoint3 from = requestMsg->fFrom; |
|
hsPoint3 at = requestMsg->fTo; |
|
|
|
// requests are always sent in world space, but they might |
|
// need to be converted to subworld space |
|
hsMatrix44 l2w, w2l; |
|
if (worldKey) |
|
{ |
|
plSceneObject* so = plSceneObject::ConvertNoRef(worldKey->ObjectIsLoaded()); |
|
if (so) |
|
{ |
|
l2w = so->GetLocalToWorld(); |
|
w2l = so->GetWorldToLocal(); |
|
from = w2l * from; |
|
at = w2l * at; |
|
} |
|
} |
|
else |
|
{ |
|
l2w.Reset(); |
|
w2l.Reset(); |
|
} |
|
|
|
NxScene* scene = sim->GetScene(worldKey); |
|
|
|
gMyReport.InitCast(requestMsg->GetRequestType(), requestMsg->GetTestType()); |
|
|
|
hsVector3 norm = hsVector3(at - from); |
|
float dist = norm.Magnitude(); |
|
norm.Normalize(); |
|
|
|
NxRay worldRay; |
|
worldRay.dir = plPXConvert::Vector(norm); |
|
worldRay.orig = plPXConvert::Point(from); |
|
//PhysX will complain to log if ray distance is less than or equal to Zero, besides shouldn't bother throwing |
|
// a point, and if we have negative we have some serious problems |
|
if(dist>0.0f) |
|
{ |
|
scene->raycastAllShapes(worldRay, gMyReport, NX_ALL_SHAPES, 0xffffffff, dist, NX_RAYCAST_DISTANCE | NX_RAYCAST_IMPACT | NX_RAYCAST_NORMAL); |
|
} |
|
else{ |
|
SimLog("%s sent out a LOS request with a ray length of %d.", requestMsg->GetSender()->GetName().c_str(), dist); |
|
} |
|
if (gMyReport.GotHit()) |
|
{ |
|
// We got a hit, save off the info |
|
plMessage* hitMsg = ICreateHitMsg(requestMsg, l2w); |
|
|
|
if (requestMsg->GetCullDB() != plSimDefs::kLOSDBNone) |
|
{ |
|
// If we have a cull db, adjust the length of the raycast to be from the |
|
// original point to the object we hit. If we find anything from the cull |
|
// db in there, the cast fails. |
|
float dist = gMyReport.GetDistance(); |
|
if(dist!=0.0) |
|
{ |
|
gMyReport.InitCast(requestMsg->GetCullDB(), plLOSRequestMsg::kTestAny); |
|
scene->raycastAllShapes(worldRay, gMyReport, NX_ALL_SHAPES, 0xffffffff, dist, NX_RAYCAST_DISTANCE | NX_RAYCAST_IMPACT | NX_RAYCAST_NORMAL); |
|
|
|
if (gMyReport.GotHit()) |
|
{ |
|
delete hitMsg; |
|
hitMsg = nil; |
|
|
|
if (requestMsg->GetReportType() == plLOSRequestMsg::kReportMiss || |
|
requestMsg->GetReportType() == plLOSRequestMsg::kReportHitOrMiss) |
|
{ |
|
ICreateMissMsg(requestMsg)->Send(); |
|
} |
|
} |
|
} |
|
else// we are right on top of the object I assume that means we hit it |
|
{// since PhysX would have complained we will log it anyways. Just so we have a better idea, where this |
|
//was happening previously |
|
SimLog("%s sent out a LOS request. The second cast for culling was of length 0. ABORTING and assuming hit.", requestMsg->GetSender()->GetName().c_str()); |
|
} |
|
|
|
} |
|
|
|
if (hitMsg && |
|
(requestMsg->GetReportType() == plLOSRequestMsg::kReportHit || |
|
requestMsg->GetReportType() == plLOSRequestMsg::kReportHitOrMiss)) |
|
hitMsg->Send(); |
|
} |
|
else |
|
{ |
|
if (requestMsg->GetReportType() == plLOSRequestMsg::kReportMiss || |
|
requestMsg->GetReportType() == plLOSRequestMsg::kReportHitOrMiss) |
|
{ |
|
ICreateMissMsg(requestMsg)->Send(); |
|
} |
|
} |
|
|
|
plProfile_EndTiming(LineOfSight); |
|
gMyReport.ResetHitObj(); |
|
return true; |
|
} |
|
|
|
return hsKeyedObject::MsgReceive(msg); |
|
} |
|
|
|
plMessage* plLOSDispatch::ICreateHitMsg(plLOSRequestMsg* requestMsg, hsMatrix44& l2w) |
|
{ |
|
plKey ourKey = GetKey(); |
|
plKey rcvKey = requestMsg->GetSender(); |
|
plLOSHitMsg* hitMsg = new plLOSHitMsg(ourKey, rcvKey, nil); |
|
hitMsg->fNoHit = false; |
|
hitMsg->fObj = gMyReport.GetObj(); |
|
hitMsg->fDistance = gMyReport.GetDistance(); |
|
hitMsg->fNormal = l2w * gMyReport.GetNormal(); |
|
hitMsg->fHitPoint = l2w * gMyReport.GetPoint(); |
|
hitMsg->fRequestID = requestMsg->GetRequestID(); |
|
return hitMsg; |
|
} |
|
|
|
plMessage* plLOSDispatch::ICreateMissMsg(plLOSRequestMsg* requestMsg) |
|
{ |
|
plKey ourKey = GetKey(); |
|
plKey rcvKey = requestMsg->GetSender(); |
|
plLOSHitMsg* missMsg = new plLOSHitMsg(ourKey, rcvKey, nil); |
|
missMsg->fNoHit = true; |
|
missMsg->fObj = nil; |
|
missMsg->fRequestID = requestMsg->GetRequestID(); |
|
return missMsg; |
|
}
|
|
|