void plDistributor::ISetAngProbCosines() const { if( fAngProbHi == fAngProbLo ) return; float maxAng, minAng; if( fAngProbHi > fAngProbLo ) { maxAng = hsDegreesToRadians(fAngProbHi); minAng = hsDegreesToRadians(fAngProbLo); } else { maxAng = hsDegreesToRadians(fAngProbLo); minAng = hsDegreesToRadians(fAngProbHi); } float transAng = hsDegreesToRadians(fAngProbTrans); if( transAng > (maxAng - minAng) * 0.5f ) transAng = (maxAng - minAng) * 0.5f; float transAngMax = maxAng < M_PI ? transAng : 0; float transAngMin = minAng > 0 ? transAng : 0; fCosAngProbHi = cos(minAng); fCosAngProbLo = cos(maxAng); fCosAngProbHiTrans = cos(minAng + transAngMin); fCosAngProbLoTrans = cos(maxAng - transAngMax); }
void plObjectInVolumeAndFacingDetector::SetFacingTolerance(int degrees) { fFacingTolerance = cos(hsDegreesToRadians(degrees)); }
void plDistributor::SetPolarRange(float deg) { fPolarRange = hsDegreesToRadians(deg); fTanPolarRange = tan(fPolarRange); }
void plWalkingStrategy::ICheckForFalseGround() { if (fGroundHit) return; // Already collided with "real" ground. // We need to check for the case where the avatar hasn't collided with "ground", but is colliding // with a few other objects so that he's not actually falling (wedged in between some slopes). // We do this by answering the following question (in 2d top-down space): "If you sort the contact // normals by angle, is there a large enough gap between normals?" // // If you think in terms of geometry, this means a collection of surfaces are all pushing on you. // If they're pushing from all sides, you have nowhere to go, and you won't fall. There needs to be // a gap, so that you're pushed out and have somewhere to fall. This is the same as finding a gap // larger than 180 degrees between sorted normals. // // The problem is that on top of that, the avatar needs enough force to shove him out that gap (he // has to overcome friction). I deal with that by making the threshold (360 - (180 - 60) = 240). I've // seen up to 220 reached in actual gameplay in a situation where we'd want this to take effect. // This is the same running into 2 walls where the angle between them is 60. int i, j; const float threshold = hsDegreesToRadians(240.f); int numContacts = fContactNormals.GetCount() + fPrevSlidingNormals.GetCount(); if (numContacts >= 2) { // For extra fun... PhysX will actually report some collisions every other frame, as though // we're bouncing back and forth between the two (or more) objects blocking us. So it's not // enough to look at this frame's collisions, we have to check previous frames too. hsTArray<float> fCollisionAngles; fCollisionAngles.SetCount(numContacts); int angleIdx = 0; for (i = 0; i < fContactNormals.GetCount(); i++, angleIdx++) { fCollisionAngles[angleIdx] = atan2(fContactNormals[i].fY, fContactNormals[i].fX); } for (i = 0; i < fPrevSlidingNormals.GetCount(); i++, angleIdx++) { fCollisionAngles[angleIdx] = atan2(fPrevSlidingNormals[i].fY, fPrevSlidingNormals[i].fX); } // numContacts is rarely larger than 6, so let's do a simple bubble sort. for (i = 0; i < numContacts; i++) { for (j = i + 1; j < numContacts; j++) { if (fCollisionAngles[i] > fCollisionAngles[j]) { float tempAngle = fCollisionAngles[i]; fCollisionAngles[i] = fCollisionAngles[j]; fCollisionAngles[j] = tempAngle; } } } // sorted, now we check. for (i = 1; i < numContacts; i++) { if (fCollisionAngles[i] - fCollisionAngles[i - 1] >= threshold) break; } if (i == numContacts) { // We got to the end. Check the last with the first and make your decision. if (!(fCollisionAngles[0] - fCollisionAngles[numContacts - 1] >= (threshold - 2 * M_PI))) fFalseGround = true; } } }
void plInterMeshSmooth::SetAngle(float degs) { fMinNormDot = cos(hsDegreesToRadians(degs)); }
bool plClickDragComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) { plLocation loc = node->GetLocation(); plSceneObject *obj = node->GetSceneObject(); plKey logicKey = fLogicModKeys[node]; plLogicModifier *logic = plLogicModifier::ConvertNoRef(logicKey->GetObjectPtr()); logic->fMyCursor = plCursorChangeMsg::kCursorOpen; // Create the detector plDetectorModifier *detector = nil; detector = new plPickingDetector; // Register the detector plKey detectorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), detector, loc); hsgResMgr::ResMgr()->AddViaNotify(detectorKey, new plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); // set up the axis anim controller plKey axisKey = fAxisKeys[node]; plAxisAnimModifier* pAxis = plAxisAnimModifier::ConvertNoRef(axisKey->GetObjectPtr()); // attach the animation controller to the animation objects: // find an animation controller: hsTArray<plKey> receivers; IGetReceivers(node, receivers); int i; for (i = 0; i < receivers.Count(); i++) pAxis->GetNotify()->AddReceiver(receivers[i]); pAxis->SetNotificationKey(logicKey); uint32_t count = node->NumAttachedComponents(); bool bHasAnim = false; plAnimComponentBase* pAnim = nil; for (i = 0; i < count; i++) { plComponentBase *comp = node->GetAttachedComponent(i); if (comp->ClassID() == ANIM_COMP_CID || comp->ClassID() == ANIM_GROUP_COMP_CID) { pAnim = (plAnimComponentBase*)comp; break; } } if (!pAnim) { pErrMsg->Set(true, "WARNING", "Object %s has click-drag component attached but NO animation component!", ((INode*)node)->GetName()).Show(); pErrMsg->Set(false); } else { if (fCompPB->GetInt(kClickDragUseX)) { pAxis->SetXAnim( pAnim->GetModKey(node) ); } else // take out this else when we support multiple channels if (fCompPB->GetInt(kClickDragUseY)) { pAxis->SetYAnim( pAnim->GetModKey(node) ); } pAxis->SetAllOrNothing(fCompPB->GetInt(kClickDragAllOrNothing)); // add callbacks for beginning and end of animation plEventCallbackMsg* pCall1 = new plEventCallbackMsg; pCall1->fEvent = kBegin; pCall1->fRepeats = -1; pCall1->AddReceiver(axisKey); plEventCallbackMsg* pCall2 = new plEventCallbackMsg; pCall2->fEvent = kEnd; pCall2->fRepeats = -1; pCall2->AddReceiver(axisKey); plAnimCmdMsg* pMsg = new plAnimCmdMsg; plString tempAnimName = pAnim->GetAnimName(); if (tempAnimName.IsNull()) { //pMsg->SetAnimName(ENTIRE_ANIMATION_NAME); pMsg->SetAnimName(pAnim->GetModKey(node)->GetName()); pAxis->SetAnimLabel(ENTIRE_ANIMATION_NAME); } else { //pMsg->SetAnimName(tempAnimName); pMsg->SetAnimName(pAnim->GetModKey(node)->GetName()); pAxis->SetAnimLabel(tempAnimName); } pMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); pMsg->AddCallback(pCall1); pMsg->AddCallback(pCall2); hsRefCnt_SafeUnRef( pCall1 ); hsRefCnt_SafeUnRef( pCall2 ); pMsg->AddReceiver( pAnim->GetModKey(node) ); plgDispatch::MsgSend(pMsg); } // is this a using a proxy primitive? plPickingDetector* det2 = nil; plKey det2Key = nil; plMaxNode* pProxyNode = (plMaxNode*)fCompPB->GetINode(kClickDragProxy); if (pProxyNode && fCompPB->GetInt(kClickDragUseProxy)) { // verify that there is a physical proxy attached to this scene object: uint32_t count = ((plMaxNodeBase*)pProxyNode)->NumAttachedComponents(); bool bHasPhys = false; // for (uint32_t i = 0; i < count; i++) // { // plComponentBase *comp = ((plMaxNodeBase*)pProxyNode)->GetAttachedComponent(i); // if (comp->ClassID() == Class_ID(0x11e81ee4, 0x36b81450)) // { // bHasPhys = true; // break; // } // } // if (!bHasPhys) // { // pErrMsg->Set(true, "WARNING", "Object %s listed as draggable component proxy physical for %s but has NO physical component.\n Please attach a proxyTerrain componet!\n Export will continue but this gadget will not function",pProxyNode->GetName(), ((INode*)node)->GetName()).Show(); // pErrMsg->Set(false); // } if(pProxyNode->CanConvert()) { det2 = new plPickingDetector; // Register the detector det2Key = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), det2, loc); hsgResMgr::ResMgr()->AddViaNotify(det2Key, new plObjRefMsg(((plMaxNode*)pProxyNode)->GetSceneObject()->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); hsgResMgr::ResMgr()->AddViaNotify(logicKey, new plObjRefMsg( det2Key, plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); det2->SetProxyKey(node->GetSceneObject()->GetKey()); } else { pErrMsg->Set(true, "Unknown Error", "Invalid proxy physical detector set for draggable %s.", ((INode*)pProxyNode)->GetName()).Show(); pErrMsg->Set(false); return false; } } // create and register the CONDITIONS for the DETECTOR's Logic Modifier plActivatorConditionalObject* activatorCond = new plActivatorConditionalObject; plKey activatorKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), activatorCond, loc); // do we have a required region? plMaxNode* pProxyRegNode = (plMaxNode*)fCompPB->GetINode(kClickDragProxyRegion); if (pProxyRegNode) { // verify that there is a physical detector attached to this scene object: uint32_t count = ((plMaxNodeBase*)pProxyRegNode)->NumAttachedComponents(); bool bHasPhys = false; // for (uint32_t i = 0; i < count; i++) // { // plComponentBase *comp = ((plMaxNodeBase*)pProxyRegNode)->GetAttachedComponent(i); // if (comp->ClassID() == Class_ID(0x33b60376, 0x7e5163e0)) // { // bHasPhys = true; // break; // } // } // if (!bHasPhys) // { // pErrMsg->Set(true, "WARNING", "Object %s listed as draggable component detector region for %s but has NO physical detector component!\n Please attach a detector componet.\n Export will continue but this gadget will not function",((INode*)pProxyRegNode)->GetName(), ((INode*)node)->GetName()).Show(); // pErrMsg->Set(false); // } if(pProxyRegNode->CanConvert()) { // need a player in box condition here... // first a detector-any for the box plObjectInVolumeDetector* pCDet = new plObjectInVolumeDetector(plCollisionDetector::kTypeAny); plKey cDetKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), pCDet, loc); hsgResMgr::ResMgr()->AddViaNotify(cDetKey, new plObjRefMsg(((plMaxNode*)pProxyRegNode)->GetSceneObject()->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kModifier), plRefFlags::kActiveRef); pCDet->AddLogicObj(logicKey); // then an object-in-box condition for the logic mod plObjectInBoxConditionalObject* boxCond = new plObjectInBoxConditionalObject; plKey boxCondKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), boxCond, loc); logic->AddCondition(boxCond); } else { pErrMsg->Set(true, "Problem with region", "Can't convert region component on %s. This component will not be exported.\n", ((INode*)pProxyRegNode)->GetName()).Show(); pErrMsg->Set(false); return false; } } else { pErrMsg->Set(true, "Must specify trigger region", "No required trigger region specified for click-drag component on %s. This component will not be exported.\n", ((INode*)node)->GetName()).Show(); pErrMsg->Set(false); return false; } // How do we feel about player facing plFacingConditionalObject* facingCond = new plFacingConditionalObject; facingCond->SetDirectional(fCompPB->GetInt(kClickDragDirectional)); int deg = fCompPB->GetInt(kClickDragDegrees); if (deg > 180) deg = 180; float rad = hsDegreesToRadians(deg); facingCond->SetTolerance(cos(rad)); plKey facingKey = hsgResMgr::ResMgr()->NewKey(IGetUniqueName(node), facingCond, loc); // link everything up: if (det2) // set up the remote detector (if any) { activatorCond->SetActivatorKey(det2Key); det2->AddLogicObj(logicKey); } else { detector->AddLogicObj(logicKey); // send messages to this logic component activatorCond->SetActivatorKey(detectorKey); // Tells the activator condition to look for stimulus from the detector } logic->AddCondition(activatorCond); // add this activator condition logic->AddCondition(facingCond); logic->SetDisabled(fCompPB->GetInt(kClikDragEnabled) == 0); // If this is for the SceneViewer, set the local only flag since the read function will never be called if (plConvert::Instance().IsForSceneViewer()) logic->SetLocalOnly(true); return true; }