void plPXPhysicalControllerCore::MoveKinematicToController(hsPoint3& pos) { if ( fKinematicActor) { NxVec3 kinPos = fKinematicActor->getGlobalPosition(); if ( abs(kinPos.x-pos.fX) + abs(kinPos.y-pos.fY) + (abs(kinPos.z-pos.fZ+kPhysZOffset)) > 0.0001f) { NxVec3 newPos; newPos.x = (NxReal)pos.fX; newPos.y = (NxReal)pos.fY; newPos.z = (NxReal)pos.fZ+kPhysZOffset; if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) { if (plSimulationMgr::fExtraProfile) SimLog("Moving kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); // use the position fKinematicActor->moveGlobalPosition(newPos); } else { if (plSimulationMgr::fExtraProfile) SimLog("Setting kinematic from %f,%f,%f to %f,%f,%f",pos.fX,pos.fY,pos.fZ+kPhysZOffset,kinPos.x,kinPos.y,kinPos.z ); fKinematicActor->setGlobalPosition(newPos); } } } }
void plPXPhysical::SetSyncState(hsPoint3* pos, hsQuat* rot, hsVector3* linV, hsVector3* angV) { bool isLoading = plNetClientApp::GetInstance()->IsLoadingInitialAgeState(); bool isFirstIn = plNetClientApp::GetInstance()->GetJoinOrder() == 0; bool initialSync = isLoading && isFirstIn; // If the physical has fallen out of the sim, and this is initial age state, and we're // the first person in, reset it to the original position. (ie, prop the default state // we've got right now) if (pos && pos->fZ < kMaxNegativeZPos && initialSync) { SimLog("Physical %s loaded out of range state. Forcing initial state to server.", GetKeyName().c_str()); DirtySynchState(kSDLPhysical, plSynchedObject::kBCastToClients); return; } if (pos) ISetPositionSim(*pos); if (rot) ISetRotationSim(*rot); if (linV) SetLinearVelocitySim(*linV); if (angV) SetAngularVelocitySim(*angV); // If we're loading the age, then we should ensure the objects // stay asleep if they're supposed to be asleep. if (isLoading && GetProperty(plSimulationInterface::kStartInactive) && !fActor->readBodyFlag(NX_BF_KINEMATIC)) fActor->putToSleep(); SendNewLocation(false, true); }
void plPXPhysicalControllerCore::SetSubworld(plKey world) { if (fWorldKey != world) { bool wasEnabled = fEnabled; #ifdef USE_PHYSX_CONVEXHULL_WORKAROUND // PHYSX FIXME - before leaving this world, sending leaving detector events if we are inside a convex hull detector hsPoint3 pos; IGetPositionSim(pos); plSimulationMgr::GetInstance()->UpdateDetectorsInScene(fWorldKey,GetOwner(),pos,false); #endif // USE_PHYSX_CONVEXHULL_WORKAROUND //need to inform detectors in the old world that we are leaving IInformDetectors(false); //done informing old world SimLog("Changing subworlds!"); IDeleteController(); SimLog("Deleted old controller"); fWorldKey = world; if (GetSubworldCI()) fPrevSubworldW2L = GetSubworldCI()->GetWorldToLocal(); // Update our subworld position and rotation const plCoordinateInterface* subworldCI = GetSubworldCI(); if (subworldCI) { const hsMatrix44& w2s = fPrevSubworldW2L; hsMatrix44 l2s = w2s * fLastGlobalLoc; l2s.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(l2s); } else { fLastGlobalLoc.GetTranslate(&fLocalPosition); fLocalRotation.SetFromMatrix44(fLastGlobalLoc); } hsMatrix44 w2l; fLastGlobalLoc.GetInverse(&w2l); if (fProxyGen) fProxyGen->SetTransform(fLastGlobalLoc, w2l); // Update the physical position SimLog("creating new controller"); hsPoint3 PositionPlusOffset=fLocalPosition; PositionPlusOffset.fZ +=kPhysZOffset; //placing new controller and kinematic in the appropriate location ICreateController(PositionPlusOffset); RebuildCache(); } }
void plPXPhysicalControllerCore::UpdateControllerAndPhysicalRep() { if ( fKinematicActor) { if(this->fBehavingLikeAnimatedPhys) {//this means we are moving the controller and then synchnig the kin NxExtendedVec3 ControllerPos= fController->getPosition(); NxVec3 NewKinPos((NxReal)ControllerPos.x, (NxReal)ControllerPos.y, (NxReal)ControllerPos.z); if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) { if (plSimulationMgr::fExtraProfile) SimLog("Moving kinematic to %f,%f,%f",NewKinPos.x, NewKinPos.y, NewKinPos.z ); // use the position fKinematicActor->moveGlobalPosition(NewKinPos); } else { if (plSimulationMgr::fExtraProfile) SimLog("Setting kinematic to %f,%f,%f", NewKinPos.x, NewKinPos.y, NewKinPos.z ); fKinematicActor->setGlobalPosition(NewKinPos); } } else { NxVec3 KinPos= fKinematicActor->getGlobalPosition(); NxExtendedVec3 NewControllerPos(KinPos.x, KinPos.y, KinPos.z); if (plSimulationMgr::fExtraProfile) SimLog("Setting Controller to %f,%f,%f", NewControllerPos.x, NewControllerPos.y, NewControllerPos.z ); fController->setPosition(NewControllerPos); } hsPoint3 curLocalPos; GetPositionSim(curLocalPos); fLocalPosition = curLocalPos; } }
// Called after the simulation has run....sends new positions to the various scene objects // *** want to do this in response to an update message.... void plPXPhysical::SendNewLocation(bool synchTransform, bool isSynchUpdate) { // we only send if: // - the body is active or forceUpdate is on // - the mass is non-zero // - the physical is not passive bool bodyActive = !fActor->isSleeping(); bool dynamic = fActor->isDynamic(); if ((bodyActive || isSynchUpdate) && dynamic)// && fInitialTransform) { plProfile_Inc(MaySendLocation); if (!GetProperty(plSimulationInterface::kPassive)) { hsMatrix44 curl2w = fCachedLocal2World; // we're going to cache the transform before sending so we can recognize if it comes back IGetTransformGlobal(fCachedLocal2World); if (!CompareMatrices(curl2w, fCachedLocal2World, .0001f)) { plProfile_Inc(LocationsSent); plProfile_BeginLap(PhysicsUpdates, GetKeyName().c_str()); // quick peek at the translation...last time it was corrupted because we applied a non-unit quaternion // hsAssert(real_finite(fCachedLocal2World.fMap[0][3]) && // real_finite(fCachedLocal2World.fMap[1][3]) && // real_finite(fCachedLocal2World.fMap[2][3]), "Bad transform outgoing"); if (fCachedLocal2World.GetTranslate().fZ < kMaxNegativeZPos) { SimLog("Physical %s fell to %.1f (%.1f is the max). Suppressing.", GetKeyName().c_str(), fCachedLocal2World.GetTranslate().fZ, kMaxNegativeZPos); // Since this has probably been falling for a while, and thus not getting any syncs, // make sure to save it's current pos so we'll know to reset it later DirtySynchState(kSDLPhysical, plSynchedObject::kBCastToClients); IEnable(false); } hsMatrix44 w2l; fCachedLocal2World.GetInverse(&w2l); plCorrectionMsg *pCorrMsg = new plCorrectionMsg(GetObjectKey(), fCachedLocal2World, w2l, synchTransform); pCorrMsg->Send(); if (fProxyGen) fProxyGen->SetTransform(fCachedLocal2World, w2l); plProfile_EndLap(PhysicsUpdates, GetKeyName().c_str()); } } } }
void plPXPhysicalControllerCore::IMatchKinematicToController() { if ( fKinematicActor) { NxExtendedVec3 cPos = fController->getPosition(); NxVec3 prevKinPos = fKinematicActor->getGlobalPosition(); NxVec3 kinPos; kinPos.x = (NxReal)cPos.x; kinPos.y = (NxReal)cPos.y; kinPos.z = (NxReal)cPos.z; if (plSimulationMgr::fExtraProfile) SimLog("Match setting kinematic from %f,%f,%f to %f,%f,%f",prevKinPos.x,prevKinPos.y,prevKinPos.z,kinPos.x,kinPos.y,kinPos.z ); if (fKinematicActor->readBodyFlag(NX_BF_KINEMATIC)) fKinematicActor->moveGlobalPosition(kinPos); else fKinematicActor->setGlobalPosition(kinPos); } }
// This form is assumed by convention to be global. void plPXPhysical::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, bool force) { // hsAssert(real_finite(l2w.fMap[0][3]) && real_finite(l2w.fMap[1][3]) && real_finite(l2w.fMap[2][3]), "Bad transform incoming"); // make sure the physical is dynamic. // also make sure there is some difference between the matrices... // ... but not when a subworld... because the subworld maybe animating and if the object is still then it is actually moving within the subworld if (force || (fActor->isDynamic() && (fWorldKey || !CompareMatrices(l2w, fCachedLocal2World, .0001f))) ) { ISetTransformGlobal(l2w); plProfile_Inc(SetTransforms); } else { if ( !fActor->isDynamic() && plSimulationMgr::fExtraProfile) SimLog("Setting transform on non-dynamic: %s.", GetKeyName().c_str()); } }
bool plPXPhysical::Init(PhysRecipe& recipe) { bool startAsleep = false; fBoundsType = recipe.bounds; fGroup = recipe.group; fReportsOn = recipe.reportsOn; fObjectKey = recipe.objectKey; fSceneNode = recipe.sceneNode; fWorldKey = recipe.worldKey; NxActorDesc actorDesc; NxSphereShapeDesc sphereDesc; NxConvexShapeDesc convexShapeDesc; NxTriangleMeshShapeDesc trimeshShapeDesc; NxBoxShapeDesc boxDesc; plPXConvert::Matrix(recipe.l2s, actorDesc.globalPose); switch (fBoundsType) { case plSimDefs::kSphereBounds: { hsMatrix44 sphereL2W; sphereL2W.Reset(); sphereL2W.SetTranslate(&recipe.offset); sphereDesc.radius = recipe.radius; plPXConvert::Matrix(sphereL2W, sphereDesc.localPose); sphereDesc.group = fGroup; actorDesc.shapes.pushBack(&sphereDesc); } break; case plSimDefs::kHullBounds: // FIXME PHYSX - Remove when hull detection is fixed // If this is read time (ie, meshStream is nil), turn the convex hull // into a box. That way the data won't have to change when convex hulls // actually work right. if (fGroup == plSimDefs::kGroupDetector && recipe.meshStream == nil) { #ifdef USE_BOXES_FOR_DETECTOR_HULLS MakeBoxFromHull(recipe.convexMesh, boxDesc); plSimulationMgr::GetInstance()->GetSDK()->releaseConvexMesh(*recipe.convexMesh); boxDesc.group = fGroup; actorDesc.shapes.push_back(&boxDesc); #else #ifdef USE_PHYSX_CONVEXHULL_WORKAROUND // make a hull of planes for testing IsInside IMakeHull(recipe.convexMesh,recipe.l2s); #endif // USE_PHYSX_CONVEXHULL_WORKAROUND convexShapeDesc.meshData = recipe.convexMesh; convexShapeDesc.userData = recipe.meshStream; convexShapeDesc.group = fGroup; actorDesc.shapes.pushBack(&convexShapeDesc); #endif // USE_BOXES_FOR_DETECTOR_HULLS } else { convexShapeDesc.meshData = recipe.convexMesh; convexShapeDesc.userData = recipe.meshStream; convexShapeDesc.group = fGroup; actorDesc.shapes.pushBack(&convexShapeDesc); } break; case plSimDefs::kBoxBounds: { boxDesc.dimensions = plPXConvert::Point(recipe.bDimensions); hsMatrix44 boxL2W; boxL2W.Reset(); boxL2W.SetTranslate(&recipe.bOffset); plPXConvert::Matrix(boxL2W, boxDesc.localPose); boxDesc.group = fGroup; actorDesc.shapes.push_back(&boxDesc); } break; case plSimDefs::kExplicitBounds: case plSimDefs::kProxyBounds: if (fGroup == plSimDefs::kGroupDetector) { SimLog("Someone using an Exact on a detector region: %s", GetKeyName().c_str()); } trimeshShapeDesc.meshData = recipe.triMesh; trimeshShapeDesc.userData = recipe.meshStream; trimeshShapeDesc.group = fGroup; actorDesc.shapes.pushBack(&trimeshShapeDesc); break; default: hsAssert(false, "Unknown geometry type during read."); return false; break; } // Now fill out the body, or dynamic part of the physical NxBodyDesc bodyDesc; fMass = recipe.mass; if (recipe.mass != 0) { bodyDesc.mass = recipe.mass; actorDesc.body = &bodyDesc; if (GetProperty(plSimulationInterface::kPinned)) { bodyDesc.flags |= NX_BF_FROZEN; startAsleep = true; // put it to sleep if they are going to be frozen } if (fGroup != plSimDefs::kGroupDynamic || GetProperty(plSimulationInterface::kPhysAnim)) { SetProperty(plSimulationInterface::kPassive, true); // Even though the code for animated physicals and animated activators are the same // keep these code snippets separated for fine tuning. Thanks. if (fGroup == plSimDefs::kGroupDynamic) { // handle the animated physicals.... make kinematic for now. fNumberAnimatedPhysicals++; bodyDesc.flags |= NX_BF_KINEMATIC; startAsleep = true; } else { // handle the animated activators.... fNumberAnimatedActivators++; bodyDesc.flags |= NX_BF_KINEMATIC; startAsleep = true; } } } else { if ( GetProperty(plSimulationInterface::kPhysAnim) ) SimLog("An animated physical that has no mass: %s", GetKeyName().c_str()); } actorDesc.userData = this; actorDesc.name = GetKeyName().c_str(); // Put the dynamics into actor group 1. The actor groups are only used for // deciding who we get contact reports for. if (fGroup == plSimDefs::kGroupDynamic) actorDesc.group = 1; NxScene* scene = plSimulationMgr::GetInstance()->GetScene(fWorldKey); try { fActor = scene->createActor(actorDesc); } catch (...) { hsAssert(false, "Actor creation crashed"); return false; } hsAssert(fActor, "Actor creation failed"); if (!fActor) return false; NxShape* shape = fActor->getShapes()[0]; shape->setMaterial(plSimulationMgr::GetInstance()->GetMaterialIdx(scene, recipe.friction, recipe.restitution)); // Turn on the trigger flags for any detectors. // // Normally, we'd set these flags on the shape before it's created. However, // in the case where the detector is going to be animated, it'll have a rigid // body too, and that will cause problems at creation. According to Ageia, // a detector shape doesn't actually count as a shape, so the SDK will have // problems trying to calculate an intertial tensor. By letting it be // created as a normal dynamic first, then setting the flags, we work around // that problem. if (fGroup == plSimDefs::kGroupDetector) { shape->setFlag(NX_TRIGGER_ON_ENTER, true); shape->setFlag(NX_TRIGGER_ON_LEAVE, true); } if (GetProperty(plSimulationInterface::kStartInactive) || startAsleep) { if (!fActor->isSleeping()) { if (plSimulationMgr::fExtraProfile) SimLog("Deactivating %s in SetPositionAndRotationSim", GetKeyName().c_str()); fActor->putToSleep(); } } if (GetProperty(plSimulationInterface::kDisable)) IEnable(false); if (GetProperty(plSimulationInterface::kSuppressed_DEAD)) IEnable(false); plNodeRefMsg* refMsg = new plNodeRefMsg(fSceneNode, plRefMsg::kOnCreate, -1, plNodeRefMsg::kPhysical); hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kActiveRef); if (fWorldKey) { plGenRefMsg* ref = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPhysRefWorld); hsgResMgr::ResMgr()->AddViaNotify(fWorldKey, ref, plRefFlags::kActiveRef); } // only dynamic physicals without noSync need SDLs if ( fGroup == plSimDefs::kGroupDynamic && !fProps.IsBitSet(plSimulationInterface::kNoSynchronize) ) { // add SDL modifier plSceneObject* sceneObj = plSceneObject::ConvertNoRef(fObjectKey->ObjectIsLoaded()); hsAssert(sceneObj, "nil sceneObject, failed to create and attach SDL modifier"); delete fSDLMod; fSDLMod = new plPhysicalSDLModifier; sceneObj->AddModifier(fSDLMod); } return true; }
hsBool 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); }