/*==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 3 ds 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 , 3 ds 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 ;
}