unsigned int Forest::onCollideJumper(unsigned int id, Matrix4f* matrix, void* data) { Forest* __this = reinterpret_cast<Forest*>( data ); // determine obb of instance NxBox instanceOBB = calculateOBB( __this->_canopyBatch->getBatchScheme()->lodGeometry[0], *matrix, __this->_desc.collScale ); __this->_debugBoxes.push_back( instanceOBB ); // collide obbs if( NxBoxBoxIntersect( instanceOBB, __this->_jumperOBB ) ) { Jumper* jumper = dynamic_cast<Jumper*>( __this->_currentJumper ); // add impulse to jumper body NxVec3 linearVelocity = __this->_currentJumperActor->getLinearVelocity(); NxVec3 impulse = linearVelocity * getCore()->getRandToolkit()->getUniform( __this->_desc.minImpulseFactor, __this->_desc.maxImpulseFactor ) * -1; NxVec3 localPos( __this->_jumperOBB.extents.x * getCore()->getRandToolkit()->getUniform( -1, 1 ), __this->_jumperOBB.extents.y * getCore()->getRandToolkit()->getUniform( -1, 1 ), __this->_jumperOBB.extents.z * getCore()->getRandToolkit()->getUniform( -1, 1 ) ); __this->_currentJumperActor->addForceAtLocalPos( impulse, localPos, NX_IMPULSE ); // damage jumper jumper->damage( __this->_desc.damageFactor * impulse.magnitude(), 0.0f, linearVelocity.magnitude() ); // play rustle sound __this->playRustleSound( __this->_currentJumperCollision->getFrame()->getPos() ); } return id; }
NxDistanceJoint* World::CreateDistanceJoint(NxActor* a0, NxActor* a1, const NxVec3& anchor0, const NxVec3& anchor1, const NxVec3& globalAxis) { NxDistanceJointDesc distanceDesc; distanceDesc.actor[0] = a0; distanceDesc.actor[1] = a1; distanceDesc.localAnchor[0] = anchor0; distanceDesc.localAnchor[1] = anchor1; distanceDesc.setGlobalAxis(globalAxis); NxVec3 dist = a1->getGlobalPose()*anchor1 - a0->getGlobalPose()*anchor0; printf("dist is %f %f %f\n",dist.x, dist.y, dist.z); distanceDesc.maxDistance = dist.magnitude()*1.5f; distanceDesc.minDistance = dist.magnitude()*0.1f; printf("maxDistance %f, minDistance %f\n", distanceDesc.maxDistance, distanceDesc.minDistance); NxSpringDesc spring; spring.spring = 1000; // х»лн¤х╩§ spring.damper = 0.5; distanceDesc.spring = spring; distanceDesc.flags = (NX_DJF_MIN_DISTANCE_ENABLED | NX_DJF_MAX_DISTANCE_ENABLED); distanceDesc.flags |= NX_DJF_SPRING_ENABLED; distanceDesc.jointFlags |= NX_JF_COLLISION_ENABLED; return (NxDistanceJoint*)gScene->createJoint(distanceDesc); }
unsigned int Forest::onCollideCanopy(unsigned int id, Matrix4f* matrix, void* data) { Forest* __this = reinterpret_cast<Forest*>( data ); // determine obb of instance NxBox instanceOBB = calculateOBB( __this->_canopyBatch->getBatchScheme()->lodGeometry[0], *matrix, __this->_desc.collScale ); __this->_debugBoxes.push_back( instanceOBB ); // collide obbs if( NxBoxBoxIntersect( instanceOBB, __this->_canopyOBB ) ) { CanopySimulator* canopy = dynamic_cast<CanopySimulator*>( __this->_currentCanopy ); // calculate intersection details float volume; NxVec3 globalIntersectionCenter; calculateIntersectionDetails( instanceOBB, __this->_canopyOBB, volume, globalIntersectionCenter ); assert( volume <= 1.0f ); // add force to canopy NxVec3 linearVelocity = __this->_currentCanopyActor->getLinearVelocity(); float linearVelocityMagnitude = linearVelocity.magnitude(); linearVelocity.normalize(); NxVec3 force = linearVelocity * 0.2f * sqr( linearVelocityMagnitude ) * __this->_currentCanopyInfo->square * -volume; __this->_currentCanopyActor->addForceAtPos( force, globalIntersectionCenter, NX_FORCE ); // damage canopy canopy->rip( __this->_desc.ripFactor * force.magnitude() ); // entangle canopy if( volume > __this->_desc.entangleFactor ) { canopy->entangle( globalIntersectionCenter ); __this->playSqueakSound( wrap( globalIntersectionCenter ) ); } else { __this->playRustleSound( wrap( globalIntersectionCenter ) ); } } return id; }
void DrawForce(NxActor* actor, NxVec3& forceVec, const NxVec3& color) { // Draw only if the force is large enough NxReal force = forceVec.magnitude(); if (force < 0.1f) return; forceVec = 3*forceVec/force; NxVec3 pos = actor->getCMassGlobalPosition(); DrawArrow(pos, pos + forceVec, color); }
void Jumper::CanopyOpening::updatePhysics(void) { // velocity of base jumper's body NxVec3 velocity = _phActor->getLinearVelocity(); // local coordinate system of base jumper NxMat34 pose = _phActor->getGlobalPose(); NxVec3 x = pose.M.getColumn(0); NxVec3 y = pose.M.getColumn(1); NxVec3 z = pose.M.getColumn(2); // air resistance force float AR = _jumper->getVirtues()->getTrackingAirResistance(); // terminal velocity float Vt = sqrt( 9.8f * _phActor->getMass() / AR ); float It = velocity.magnitude() / Vt; // air resistance force NxVec3 Far = NxVec3(0,1,0) * getAirResistancePower( velocity.magnitude() / Vt ) * _phActor->getMass() * 9.8f; // finalize motion equation _phActor->addForce( Far ); // linear damping is function of jumper velocity // this is prevents calculation errors due to high speed rates float minVel = 50.0f; float minDamping = _initialLD; float maxVel = 70.0f; float maxDamping = 2.5f; float factor = ( velocity.magnitude() - minVel ) / ( maxVel - minVel ); factor = factor < 0 ? 0 : ( factor > 1 ? 1 : factor ); float damping = minDamping * ( factor - 1 ) + maxDamping * ( factor ); _phActor->setLinearDamping( damping ); // shallow brake setting _canopy->setLeftDeep( 0.0f ); _canopy->setRightDeep( 0.0f ); }
void DynamicImage::update() { if (!sleep) { NxVec3 towardsVector = tarPos - curPos; switch (mode) { case 0: //Linear if (towardsVector.magnitude() < LINEAR_VELOCITY) { curPos = tarPos; sleep = true; } else { towardsVector.normalize(); curPos = curPos + (towardsVector * LINEAR_VELOCITY); } break; case 1: //Halfling if (towardsVector.magnitude() < THRESHOLD) { curPos = tarPos; sleep = true; } else { curPos = curPos + (towardsVector * 0.033f); } break; } } }
void DrawForceAtShape(NxActor* actor, NxShape* shape, NxVec3& forceVec, const NxVec3& color) { // Draw only if the force is large enough NxReal force = forceVec.magnitude(); if (force < 0.1f) return; forceVec = 3*forceVec/force; NxVec3 pos; if (bShapeSelectMode && bForceMode) pos = shape->getGlobalPosition(); else pos = actor->getCMassGlobalPosition(); DrawArrow(pos, pos + forceVec, color); }
void CreateExperiment() { switch (gExperimentType) { case LANDER: { gScene->releaseActor(*groundPlane); groundPlane=NULL; // will be recreated with the container cell CreateExperimentMaterials(); CreateContainerCell(gCellSize); gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES,0); gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS,0); gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES,1); gPhysicsSDK->setParameter(NX_VISUALIZE_WORLD_AXES,0); //gLander=CreateLander(NxVec3(0.0f,0.0f,0.0f)); gLander=CreateLander(NxVec3(0.0f,gCellSize/2.0f,0.0f)); IdentifyGravitators(); if (gLander) { ReOrientActor(gLander); SpinActor(gLander,gLandingRoughness*sqrt(gCellSize*gDefaultGravity.magnitude()/(gLander->getMassSpaceInertiaTensor().magnitude()))); LaunchActor(gLander,gLandingRoughness*sqrt(gCellSize*gDefaultGravity.magnitude())); gSelectedActor=gLander; gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES,gLanderSize.magnitude()); isRunning=true; } else { printf("Error: Unable to create lander\a\n"); isRunning=false; } break; } case LAY_SUBSTRATE: { gScene->releaseActor(*groundPlane); groundPlane=NULL; // will be recreated with the container cell CreateExperimentMaterials(); CreateContainerCell(gCellSize); gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES,0); gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_FNORMALS,0); gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES,1); gPhysicsSDK->setParameter(NX_VISUALIZE_WORLD_AXES,0); //ThrowStone(); isRunning=true; break; } default: {printf("Error: unimplemented experiment type\a\n"); isRunning=false; break;} } }
void PxSingleActor::sweepTest( MatrixF *mat ) { NxVec3 nxCurrPos = getPosition(); // If the position is zero, // the parent hasn't been updated yet // and we don't even need to do the sweep test. // This is a fix for a problem that was happening // where on the add of the PxSingleActor, it would // set the position to a very small value because it would be getting a hit // even though the current position was 0. if ( nxCurrPos.isZero() ) return; // Set up the flags and the query structure. NxU32 flags = NX_SF_STATICS | NX_SF_DYNAMICS; NxSweepQueryHit sweepResult; dMemset( &sweepResult, 0, sizeof( sweepResult ) ); NxVec3 nxNewPos = mat->getPosition(); // Get the velocity which will be our sweep direction and distance. NxVec3 nxDir = nxNewPos - nxCurrPos; if ( nxDir.isZero() ) return; // Get the scene and do the sweep. mActor->wakeUp(); mActor->linearSweep( nxDir, flags, NULL, 1, &sweepResult, NULL ); if ( sweepResult.hitShape && sweepResult.t < nxDir.magnitude() ) { nxDir.normalize(); nxDir *= sweepResult.t; nxCurrPos += nxDir; mat->setPosition( Point3F( nxCurrPos.x, nxCurrPos.y, nxCurrPos.z ) ); } }
void PxSingleActor::applyCorrection( const MatrixF& mat, const NxVec3& linVel, const NxVec3& angVel ) { // Sometimes the actor hasn't been // created yet during the call from unpackUpdate. if ( !mActor || !mWorld ) return; mWorld->releaseWriteLock(); NxVec3 newPos = mat.getPosition(); NxVec3 currPos = getPosition(); NxVec3 offset = newPos - currPos; // If the difference isn't large enough, // just set the new transform, no correction. if ( offset.magnitude() > 0.3f ) { // If we're going to set the linear or angular velocity, // we do it before we add a corrective force, since it would be overwritten otherwise. NxVec3 currLinVel, currAngVel; currLinVel = mActor->getLinearVelocity(); currAngVel = mActor->getAngularVelocity(); // Scale the corrective force by half, // otherwise it will over correct and oscillate. NxVec3 massCent = mActor->getCMassGlobalPosition(); mActor->addForceAtPos( offset, massCent, NX_VELOCITY_CHANGE ); // If the linear velocity is divergent enough, change to server linear velocity. if ( (linVel - currLinVel).magnitude() > 0.3f ) mActor->setLinearVelocity( linVel ); // Same for angular. if ( (angVel - currAngVel).magnitude() > 0.3f ) mActor->setAngularVelocity( angVel ); } Parent::setTransform( mat ); }
void WindPointer::onEvent(Actor* initiator, unsigned int eventId, void* eventData) { if( eventId == EVENT_CAMERA_IS_ACTUAL ) { // current wind NxVec3 wind = _scene->getWindAtPoint( NxVec3( 0,0,0 ) ); // make signature worldspace position Matrix4f cameraPose = _scene->getCamera()->getPose(); Vector3f cameraPos( cameraPose[3][0], cameraPose[3][1], cameraPose[3][2] ); Vector3f pos( wind.x, wind.y, wind.z ); pos.normalize(); pos *= 10000.0f; pos += cameraPos; // update signature window Vector3f screenPos = Gameplay::iEngine->getDefaultCamera()->projectPosition( pos ); if( screenPos[2] > 1 ) { _signature->getPanel()->setVisible( false ); return; } else { _signature->getPanel()->setVisible( true ); gui::Rect oldRect = _signature->getPanel()->getRect(); gui::Rect newRect( int( screenPos[0] ), int( screenPos[1] ), int( screenPos[0] ) + oldRect.getWidth(), int( screenPos[1] ) + oldRect.getHeight() ); _signature->getPanel()->setRect( newRect ); _windSpeed->getStaticText()->setText( wstrformat( Gameplay::iLanguage->getUnicodeString(295), wind.magnitude() ).c_str() ); } } }
// This is the generic sweep test for all swept volumes, but not character-controller specific bool SweepTest::DoSweepTest(void* user_data, void* user_data2, NxU32 nb_boxes, const NxExtendedBounds3* boxes, const void** box_user_data, NxU32 nb_capsules, const NxExtendedCapsule* capsules, const void** capsule_user_data, SweptVolume& swept_volume, const NxVec3& direction, NxU32 max_iter, NxU32* nb_collisions, NxU32 group_flags, float min_dist, const NxGroupsMask* groupsMask, bool down_pass) { // Early exit when motion is zero. Since the motion is decomposed into several vectors // and this function is called for each of them, it actually happens quite often. if(direction.isZero()) return false; bool HasMoved = false; mValidTri = false; NxExtendedVec3 CurrentPosition = swept_volume.mCenter; NxExtendedVec3 TargetPosition = swept_volume.mCenter; TargetPosition += direction; NxU32 NbCollisions = 0; while(max_iter--) { gNbIters++; // Compute current direction NxVec3 CurrentDirection = TargetPosition - CurrentPosition; // Make sure the new TBV is still valid { // Compute temporal bounding box. We could use a capsule or an OBB instead: // - the volume would be smaller // - but the query would be slower // Overall it's unclear whether it's worth it or not. // TODO: optimize this part ? NxExtendedBounds3 TemporalBox; swept_volume.ComputeTemporalBox(*this, TemporalBox, CurrentPosition, CurrentDirection); // Gather touched geoms UpdateTouchedGeoms(user_data, swept_volume, nb_boxes, boxes, box_user_data, nb_capsules, capsules, capsule_user_data, group_flags, TemporalBox, groupsMask); } const float Length = CurrentDirection.magnitude(); if(Length<min_dist) break; CurrentDirection /= Length; // From Quake2: "if velocity is against the original velocity, stop dead to avoid tiny occilations in sloping corners" if((CurrentDirection|direction) <= 0.0f) break; // From this point, we're going to update the position at least once HasMoved = true; // Find closest collision SweptContact C; C.mDistance = Length + mSkinWidth; if(!CollideGeoms(this, swept_volume, mGeomStream, CurrentPosition, CurrentDirection, C)) { // no collision found => move to desired position CurrentPosition = TargetPosition; break; } ASSERT(C.mGeom); // If we reach this point, we must have touched a geom if(C.mGeom->mType==TOUCHED_USER_BOX || C.mGeom->mType==TOUCHED_USER_CAPSULE) { // We touched a user object, typically another CCT if(mValidateCallback) UserHitCallback(user_data2, C, CurrentDirection, Length); // Trying to solve the following problem: // - by default, the CCT "friction" is infinite, i.e. a CCT will not slide on a slope (this is by design) // - this produces bad results when a capsule CCT stands on top of another capsule CCT, without sliding. Visually it looks // like the character is standing on the other character's head, it looks bad. So, here, we would like to let the CCT // slide away, i.e. we don't want friction. // So here we simply increase the number of iterations (== let the CCT slide) when the first down collision is with another CCT. if(down_pass && !NbCollisions) max_iter += 9; } else { // We touched a normal object #ifdef USE_CONTACT_NORMAL_FOR_SLOPE_TEST mValidTri = true; mCN = C.mWorldNormal; #else if(C.mIndex!=INVALID_ID) { mValidTri = true; mTouched = mWorldTriangles[C.mIndex]; } #endif { if(mValidateCallback) ShapeHitCallback(user_data2, C, CurrentDirection, Length); } } NbCollisions++; mContactPointHeight = (float)C.mWorldPos[mUpDirection]; // UBI const float DynSkin = mSkinWidth; if(C.mDistance>DynSkin/*+0.01f*/) CurrentPosition += CurrentDirection*(C.mDistance-DynSkin); NxVec3 WorldNormal = C.mWorldNormal; if(mWalkExperiment) { // Make sure the auto-step doesn't bypass this ! WorldNormal[mUpDirection]=0.0f; WorldNormal.normalize(); } const float Bump = 0.0f; // ### doesn't work when !=0 because of Quake2 hack! const float Friction = 1.0f; CollisionResponse(TargetPosition, CurrentPosition, CurrentDirection, WorldNormal, Bump, Friction, mNormalizeResponse); } if(nb_collisions) *nb_collisions = NbCollisions; // Final box position that should be reflected in the graphics engine swept_volume.mCenter = CurrentPosition; // If we didn't move, don't update the box position at all (keeping possible lazy-evaluated structures valid) return HasMoved; }
void Jumper::Flight::updatePhysics(void) { SpinalCord* spinalCord = _jumper->getSpinalCord(); Virtues* virtues = _jumper->getVirtues(); CanopySimulator* canopy = _jumper->getDominantCanopy(); // velocity of base jumper's body NxVec3 velocity = _phActor->getLinearVelocity(); // horizontal velocity (including wind) NxVec3 velocityH = velocity; velocityH += _jumper->getScene()->getWindAtPoint( _phActor->getGlobalPosition() ); velocityH.y = 0; // shock penalty float penalty = _jumper->getVirtues()->getControlPenalty( _jumper->getShock() ); penalty = ( 1 - penalty ); // update canopy controls // modifier to 50% input if (spinalCord->modifier) { float dt = _jumper->getDeltaTime(); if (spinalCord->left > 0.5f && Gameplay::iGameplay->getActionChannel(iaLeft)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaLeft)->downAmplitude(dt, 0.5f); if (spinalCord->right > 0.5f && Gameplay::iGameplay->getActionChannel(iaRight)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaRight)->downAmplitude(dt, 0.5f); if (spinalCord->leftWarp > 0.5f && Gameplay::iGameplay->getActionChannel(iaLeftWarp)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaLeftWarp)->downAmplitude(dt, 0.5f); if (spinalCord->rightWarp > 0.5f && Gameplay::iGameplay->getActionChannel(iaRightWarp)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaRightWarp)->downAmplitude(dt, 0.5f); if (spinalCord->leftRearRiser > 0.5f && Gameplay::iGameplay->getActionChannel(iaLeftRearRiser)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaLeftRearRiser)->downAmplitude(dt, 0.5f); if (spinalCord->rightRearRiser > 0.5f && Gameplay::iGameplay->getActionChannel(iaRightRearRiser)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaRightRearRiser)->downAmplitude(dt, 0.5f); if (spinalCord->leftReserve > 0.5f && Gameplay::iGameplay->getActionChannel(iaReserveLeft)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaReserveLeft)->downAmplitude(dt, 0.5f); if (spinalCord->rightReserve > 0.5f && Gameplay::iGameplay->getActionChannel(iaReserveRight)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaReserveRight)->downAmplitude(dt, 0.5f); if (spinalCord->leftReserveWarp > 0.5f && Gameplay::iGameplay->getActionChannel(iaReserveLeftWarp)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaReserveLeftWarp)->downAmplitude(dt, 0.5f); if (spinalCord->rightReserveWarp > 0.5f && Gameplay::iGameplay->getActionChannel(iaReserveRightWarp)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaReserveRightWarp)->downAmplitude(dt, 0.5f); if (spinalCord->leftReserveRearRiser > 0.5f && Gameplay::iGameplay->getActionChannel(iaReserveLeftRearRiser)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaReserveLeftRearRiser)->downAmplitude(dt, 0.5f); if (spinalCord->rightReserveRearRiser > 0.5f && Gameplay::iGameplay->getActionChannel(iaReserveRightRearRiser)->getTrigger()) Gameplay::iGameplay->getActionChannel(iaReserveRightRearRiser)->downAmplitude(dt, 0.5f); } // set risers canopy->setLeftWarpDeep( spinalCord->leftWarp * penalty ); canopy->setRightWarpDeep( spinalCord->rightWarp * penalty ); canopy->setLeftRearRiser( spinalCord->leftRearRiser * penalty ); canopy->setRightRearRiser( spinalCord->rightRearRiser * penalty ); canopy->setWLOToggles( spinalCord->trigger_wlo ); canopy->setHookKnife( spinalCord->trigger_hook ); // update reserve canopy controls if (_jumper->isPlayer() && _jumper->getCanopyReserveSimulator() && _jumper->getDominantCanopy() != _jumper->getCanopyReserveSimulator() && _jumper->getCanopyReserveSimulator()->isOpened()) { // set toggles CanopySimulator *reserve_canopy = _jumper->getCanopyReserveSimulator(); reserve_canopy->setLeftDeep( spinalCord->leftReserve * penalty ); reserve_canopy->setRightDeep( spinalCord->rightReserve * penalty ); // set risers reserve_canopy->setLeftWarpDeep( spinalCord->leftReserveWarp * penalty ); reserve_canopy->setRightWarpDeep( spinalCord->rightReserveWarp * penalty ); reserve_canopy->setLeftRearRiser( spinalCord->leftReserveRearRiser * penalty ); reserve_canopy->setRightRearRiser( spinalCord->rightReserveRearRiser * penalty ); reserve_canopy->setWLOToggles( spinalCord->trigger_wlo ); reserve_canopy->setHookKnife( spinalCord->trigger_hook ); } // determine animation sequence to be played _targetSequence = &passiveFlightSequence; // get total left and right input float totalInputLeft = 0.0f; float totalInputRight = 0.0f; float toggleLeft = spinalCord->left; float toggleRight = spinalCord->right; if (canopy->getLeftForcedDeep() != -1.0f) toggleLeft = 0.0f; if (canopy->getRightForcedDeep() != -1.0f) toggleRight = 0.0f; // hard toggle input if unstowing if (canopy->getLeftStowed() && spinalCord->trigger_left) { toggleLeft = 0.75f; } if (canopy->getRightStowed() && spinalCord->trigger_right) { toggleRight = 0.75f; } // set toggles canopy->setLeftDeep( spinalCord->left * penalty ); canopy->setRightDeep( spinalCord->right * penalty ); if (toggleLeft > spinalCord->leftRearRiser && toggleLeft > spinalCord->leftWarp) { totalInputLeft = toggleLeft; } else if (spinalCord->leftRearRiser > toggleLeft && spinalCord->leftRearRiser > spinalCord->leftWarp) { totalInputLeft = spinalCord->leftRearRiser; } else { totalInputLeft = spinalCord->leftWarp; } if (toggleRight > spinalCord->rightRearRiser && toggleRight > spinalCord->rightWarp) { totalInputRight = toggleRight; } else if (spinalCord->rightRearRiser > toggleRight && spinalCord->rightRearRiser > spinalCord->rightWarp) { totalInputRight = spinalCord->rightRearRiser; } else { totalInputRight = spinalCord->rightWarp; } if( totalInputLeft != 0 && totalInputRight == 0 ) { if (canopy->getLeftStowed() && spinalCord->trigger_left) { _targetSequence = &unstowLeftSequence; } else if (spinalCord->modifier) { _targetSequence = &steerLeftSequenceHalf; } else { _targetSequence = &steerLeftSequence; } } if( totalInputRight != 0 && totalInputLeft == 0 ) { if (canopy->getRightStowed() && spinalCord->trigger_right) { _targetSequence = &unstowRightSequence; } else if (spinalCord->modifier) { _targetSequence = &steerRightSequenceHalf; } else { _targetSequence = &steerRightSequence; } } if( totalInputLeft != 0 && totalInputRight != 0 ) { if (canopy->getLeftStowed() || canopy->getRightStowed()) { _targetSequence = &groupingUnstowSequence; } else if (spinalCord->modifier) { _targetSequence = &groupingSequenceHalf; } else { _targetSequence = &groupingSequence; } } // unstow toggles if (spinalCord->left > 0.1f) { canopy->setLeftStowed(false); } if (spinalCord->right > 0.1f) { canopy->setRightStowed(false); } // local coordinate system of base jumper NxMat34 pose = _phActor->getGlobalPose(); NxVec3 x = pose.M.getColumn(0); NxVec3 y = pose.M.getColumn(1); NxVec3 z = pose.M.getColumn(2); // altitude [m] //NxVec3 pos = _phActor->getGlobalPosition(); //const float altitude = pos.y; //const float arch_pose_area = 1.9f; //// air density: converted to linear from barometric equation [0:10] km altitude //// http://www.denysschen.com/catalogue/density.aspx //const float AirDensityOld = altitude <= 10000.0f ? (1.196f - 0.0000826f * altitude) : (0.27f); //// add drag force //float Cd = 0.4f; // altitude suit = 0.4f //const float mTracking = database::Suit::getRecord( _jumper->getVirtues()->equipment.suit.id )->mTracking; //Cd *= mTracking; //// Drag force vector //NxVec3 VecFd = -_phActor->getLinearVelocity(); //VecFd.normalize(); //float Fd = 0.5f * AirDensityOld * velocity.magnitudeSquared() * Cd * arch_pose_area * getCore()->getRandToolkit()->getUniform(0.94f, 1.06f); //_phActor->addForceAtLocalPos(Fd*VecFd, NxVec3(0, -3.0f, 0)); // air resistance coefficient const float ARmult = 2.5f; float AR = ARmult * virtues->getTrackingAirResistance(); if( database::Suit::getRecord( virtues->equipment.suit.id )->wingsuit ) { AR = ARmult * virtues->getFrogAirResistance(); } // terminal velocity const float Vt = sqrt( 9.8f * _phActor->getMass() / AR ); const float It = velocity.magnitude() / Vt; NxVec3 dir = velocity * -1; dir.normalize(); // air resistance force const NxVec3 Far = dir * getAirResistancePower( velocity.magnitude() / Vt ) * _phActor->getMass() * 9.8f; // finalize motion equation _phActor->addForce( Far ); }
void CanopySimulator::Rope::initialize(NxActor* actor1, NxVec3 anchor1, NxActor* actor2, NxVec3 anchor2) { _nxActor1 = actor1; assert( _nxActor1 ); _nxActor2 = actor2; assert( _nxActor2 ); _anchor1 = anchor1; _anchor2 = anchor2; // calculate worldspace positions for connections points Matrix4f ltm1 = wrap( _nxActor1->getGlobalPose() ); Matrix4f ltm2 = wrap( _nxActor2->getGlobalPose() ); NxVec3 pos1 = wrap( Gameplay::iEngine->transformCoord( wrap( _anchor1 ), ltm1 ) ); NxVec3 pos2 = wrap( Gameplay::iEngine->transformCoord( wrap( _anchor2 ), ltm2 ) ); NxVec3 dir = pos2 - pos1; // check distance btw connection points (correction just for stability purpose) float idist = dir.magnitude(); dir.normalize(); // create intermediate segment bodies NxVec3 pos = pos1; float step = idist / _numJoints; for( unsigned int i=0; i<_numJoints-1; i++ ) { // segment position pos += dir * step; // create segment body NxBodyDesc nxBodyDesc; nxBodyDesc.mass = _mass / ( _numJoints - 1 ); nxBodyDesc.massSpaceInertia.set( nxBodyDesc.mass, nxBodyDesc.mass, nxBodyDesc.mass ); nxBodyDesc.linearDamping = 2.0f; nxBodyDesc.angularDamping = 0.0f; nxBodyDesc.flags = NX_BF_VISUALIZATION; nxBodyDesc.solverIterationCount = 32; NxActorDesc nxActorDesc; nxActorDesc.body = &nxBodyDesc; nxActorDesc.globalPose.M.id(); nxActorDesc.globalPose.t = pos; _nxSegmentBody[i] = _nxScene->createActor( nxActorDesc ); assert( _nxSegmentBody[i] ); // initialize velocity _nxSegmentBody[i]->addForce( _nxActor1->getLinearVelocity(), NX_VELOCITY_CHANGE ); } // create joints for( i=0; i<_numJoints; i++ ) { // first joint (actor1-segment1) if( i == 0 ) { NxDistanceJointDesc jointDesc; jointDesc.actor[0] = _nxActor1; jointDesc.actor[1] = _nxSegmentBody[0]; jointDesc.maxDistance = _length / _numJoints; jointDesc.minDistance = 0.0f; jointDesc.flags = NX_DJF_MAX_DISTANCE_ENABLED; jointDesc.localAnchor[0] = _anchor1; jointDesc.localAnchor[1].set( 0,0,0 ); jointDesc.jointFlags = NX_JF_VISUALIZATION; _nxSegmentJoint[i] = _nxScene->createJoint( jointDesc ); assert( _nxSegmentJoint[i] ); } // last joint (segmentN-actor2) else if( i == _numJoints-1 ) { NxDistanceJointDesc jointDesc; jointDesc.actor[0] = _nxSegmentBody[_numJoints-2]; jointDesc.actor[1] = _nxActor2; jointDesc.maxDistance = _length / _numJoints; jointDesc.minDistance = 0.0f; jointDesc.flags = NX_DJF_MAX_DISTANCE_ENABLED; jointDesc.localAnchor[0].set( 0,0,0 ); jointDesc.localAnchor[1] = _anchor2; jointDesc.jointFlags = NX_JF_VISUALIZATION; _nxSegmentJoint[i] = _nxScene->createJoint( jointDesc ); assert( _nxSegmentJoint[i] ); } // intermediate joint else { NxDistanceJointDesc jointDesc; jointDesc.actor[0] = _nxSegmentBody[i-1]; jointDesc.actor[1] = _nxSegmentBody[i]; jointDesc.maxDistance = _length / _numJoints; jointDesc.minDistance = 0.0f; jointDesc.flags = NX_DJF_MAX_DISTANCE_ENABLED; jointDesc.localAnchor[0].set( 0,0,0 ); jointDesc.localAnchor[1].set( 0,0,0 ); jointDesc.jointFlags = NX_JF_VISUALIZATION; _nxSegmentJoint[i] = _nxScene->createJoint( jointDesc ); assert( _nxSegmentJoint[i] ); } } }
void DrawWireCapsule(const NxCapsule& capsule, const NxVec3& color) { NxReal r = capsule.radius; NxVec3 dir = capsule.p0 - capsule.p1; NxReal h = dir.magnitude(); NxMat34 pose; pose.t = (capsule.p0 + capsule.p1)*0.5f; if(h!=0.0f) { dir/=h; NxVec3 right, up; computeBasis(dir, right, up); pose.M.setColumn(0, right); pose.M.setColumn(1, dir); pose.M.setColumn(2, up); } else { pose.M.id(); } // NxMat34 pose = capsule->getGlobalPose(); // const NxReal & r = capsule->isCapsule()->getRadius(); // const NxReal & h = capsule->isCapsule()->getHeight(); NxSegment SG; pose.M.getColumn(1, SG.p1); SG.p1 *= 0.5f*h; SG.p0 = -SG.p1; SG.p0 += pose.t; SG.p1 += pose.t; NxVec3 c0; pose.M.getColumn(0, c0); NxVec3 c1; pose.M.getColumn(1, c1); NxVec3 c2; pose.M.getColumn(2, c2); DrawLine(SG.p0 + c0*r, SG.p1 + c0*r, color); DrawLine(SG.p0 - c0*r, SG.p1 - c0*r, color); DrawLine(SG.p0 + c2*r, SG.p1 + c2*r, color); DrawLine(SG.p0 - c2*r, SG.p1 - c2*r, color); pose.M.setColumn(0, -c1); pose.M.setColumn(1, -c0); pose.M.setColumn(2, c2); pose.t = SG.p0; DrawCircle(20, pose, color, r, true); //halfcircle -- flipped pose.M.setColumn(0, c1); pose.M.setColumn(1, -c0); pose.M.setColumn(2, c2); pose.t = SG.p1; DrawCircle(20, pose, color, r, true); pose.M.setColumn(0, -c1); pose.M.setColumn(1, c2); pose.M.setColumn(2, c0); pose.t = SG.p0; DrawCircle(20, pose, color, r, true);//halfcircle -- good pose.M.setColumn(0, c1); pose.M.setColumn(1, c2); pose.M.setColumn(2, c0); pose.t = SG.p1; DrawCircle(20, pose, color, r, true); pose.M.setColumn(0, c2); pose.M.setColumn(1, c0); pose.M.setColumn(2, c1); pose.t = SG.p0; DrawCircle(20, pose, color, r); //full circle pose.t = SG.p1; DrawCircle(20, pose, color, r); }
void NPCFreefall_DZ::onUpdate(float dt) { // obtain spinal cord SpinalCord* spinalCord = getNPC()->getSpinalCord(); spinalCord->reset(); // update target position Matrix4f catToyPose = getNPC()->getCatToy()->getCurrentPose(); _targetPos.set( catToyPose[3][0], catToyPose[3][1], catToyPose[3][2] ); // jump only when the wind is right Vector2f pos = Vector2f(catToyPose[3][0], catToyPose[3][2]); pos.normalize(); NxVec3 wind = getNPC()->getJumper()->getScene()->getWindAtPoint(NxVec3(catToyPose[3][0], catToyPose[3][1], catToyPose[3][2])); wind.normalize(); Vector2f wind2(wind.x, wind.z); float wind_angle = 0.0; if (wind.magnitude() < 1.5f) { wind_angle = 1.0f; } else { wind2.normalize(); wind_angle = pos.dot(wind2); } // if my character is still roaming if (wind_angle >= 0.8f && catToyPose[3][1] >= 300.0f) _timeUntilJump -= dt; //getCore()->logMessage("%s has %2.4f seconds to jump", getNPC()->getNPCName(), _timeUntilJump); if( _timeUntilJump <= 0.0f && getNPC()->getJumper()->getPhase() == ::jpRoaming ) { Vector3f pos = Vector3f(catToyPose[3][0], catToyPose[3][1], catToyPose[3][2]); // if npc on airplane if( getNPC()->getJumper()->getAirplane() != NULL ) { // just jump! spinalCord->phase = true; return; } // if npc at the exit point and ready to jump else if( _positionIsSucceed && _directionIsSucceed ) { // jump! spinalCord->phase = true; return; } // if npc at the exit point and not surely ready to clear jump else { if( !_positionIsSucceed ) { // move to abyss Matrix4f jumpPose = getNPC()->getCatToy()->getJumpPose(); Vector3f jumpPos( jumpPose[3][0], jumpPose[3][1], jumpPose[3][2] ); call( new NPCMove( getNPC(), jumpPos ) ); _positionIsSucceed = true; return; } if( !_directionIsSucceed ) { // jumper absolute orientation Vector3f jumperAt = getNPC()->getJumper()->getClump()->getFrame()->getAt(); jumperAt.normalize(); // direction of cat toy jump Matrix4f jumpPose = getNPC()->getCatToy()->getJumpPose(); Vector3f targetDir( jumpPose[2][0], 0.0f, jumpPose[2][2] ); targetDir.normalize(); // angle to target Vector3f atH = jumperAt; atH[1] = 0; atH.normalize(); Vector3f dirH = targetDir; dirH[1] = 0; dirH.normalize(); float targetAngle = ::calcAngle( dirH, atH, Vector3f( 0,1,0 ) ); if( fabs( targetAngle ) < 1.0f ) { _directionIsSucceed = true; return; } else { // turn jumper by AI algo float aiRotationVel = 180.0f; float aiRotationAngle = sgn( targetAngle ) * aiRotationVel * dt; if( fabs( aiRotationAngle ) > fabs( targetAngle ) ) aiRotationAngle = targetAngle; getNPC()->getJumper()->getClump()->getFrame()->rotateRelative( Vector3f( 0,1,0 ), aiRotationAngle ); } } } } // else, turn & track towards the target else if( getNPC()->getJumper()->getPhase() == ::jpFreeFalling ) { // jumper absolute orientation Vector3f jumperAt = getNPC()->getJumper()->getClump()->getFrame()->getAt(); jumperAt.normalize(); Vector3f jumperRight = getNPC()->getJumper()->getClump()->getFrame()->getRight(); jumperRight.normalize(); Vector3f jumperUp = getNPC()->getJumper()->getClump()->getFrame()->getUp(); jumperUp.normalize(); // retrieve current jumper position Vector3f jumperPos = getNPC()->getJumper()->getClump()->getFrame()->getPos(); jumperPos += Vector3f( 0, jumperRoamingSphereSize, 0 ); // wingsuit bool wingsuit = database::Suit::getRecord(getNPC()->getJumper()->getVirtues()->equipment.suit.id)->wingsuit; // leveling // wlo - when npc higher //if (!wingsuit) { // if (jumperPos[1] - _targetPos[1] > 1500.0f) { // spinalCord->wlo = true; // } else if (jumperPos[1] - _targetPos[1] < -1500.0f) { // spinalCord->hook = true; // } //} // direction to target Vector3f targetDir = _targetPos - jumperPos; float distanceToTarget = Vector3f( targetDir[0], 0, targetDir[2] ).length(); targetDir.normalize(); // landing too far away? let's deploy in order to return safely // glide ratio is based on canopy size database::Canopy *canopy = database::Canopy::getRecord(getNPC()->getJumper()->getVirtues()->equipment.canopy.id); Vector3f pos = getNPC()->getJumper()->getClump()->getFrame()->getPos(); float glide = canopy->square / 150.0f; float alt = pos[1]; pos[1] = 0; bool farEnough = pos.length() >= alt*glide; //getCore()->logMessage("alt: %2.5f; dst: %2.5f; coverage: %2.5f", alt, pos.length(), alt*glide); // deploy now? if ((alt <= 85000.0f || farEnough) && alt <= 150000.0f) { if (getNPC()->getJumper()->getCanopySimulator()) { //getCore()->logMessage("npc pull. Far enough: %d", (int)farEnough); spinalCord->phase = true; spinalCord->modifier = wingsuit; } else { //getCore()->logMessage("npc pull reserve. Far enough: %d", (int)farEnough); spinalCord->pullReserve = true; spinalCord->modifier = wingsuit; } } // if toy tracking modifier is on if( wingsuit /* getNPC()->getCatToy()->getModifier()*/ ) { // sum up target direction Vector3f targetFlatAt( getNPC()->getCatToy()->getCurrentPose()[2][0], 0, getNPC()->getCatToy()->getCurrentPose()[2][2] ); /*getCore()->logMessage( "POS: %3.2f %3.2f %3.2f AT : %3.2f 0.0 %3.2f", getNPC()->getCatToy()->getCurrentPose()[3][0], getNPC()->getCatToy()->getCurrentPose()[3][1], getNPC()->getCatToy()->getCurrentPose()[3][2], targetFlatAt[0], targetFlatAt[2] );*/ targetFlatAt.normalize(); targetDir += targetFlatAt * 3; targetDir.normalize(); } // horizontal steering float minAngle = 5.0f; float minValue = 0.0f; float maxAngle = 45.0f; float maxValue = 1.0f; // angle to target Vector3f atH = jumperAt; // headdown if (getNPC()->getJumper()->getDefaultPose() == 1) { atH = jumperUp; minAngle = 25.0f; maxValue = 0.1f; // sitfly } else if (getNPC()->getJumper()->getDefaultPose() == 2) { atH = jumperUp; atH[2] *= -1; //minAngle = 25.0f; maxValue = 0.1f; } atH[1] = 0; atH.normalize(); Vector3f dirH = targetDir; dirH[1] = 0; dirH.normalize(); float targetAngle = ::calcAngle( dirH, atH, Vector3f( 0,1,0 ) ); // inclination angle relative to the horizont float horizontalAngle = -1 * ::calcAngle( atH, jumperUp, jumperRight ); // horizontal steering float factor = ( fabs( targetAngle ) - minAngle ) / ( maxAngle - minAngle ); factor = ( factor < 0 ) ? 0 : ( ( factor > 1 ) ? 1 : factor ); float impulse = minValue * ( 1 - factor ) + maxValue * factor; if (wingsuit) impulse = 0.0f; // smooth impulse impulse = pow( impulse, 1.25f ); if (alt <= 130000.0f) impulse = 0.0f; // apply impulse if( targetAngle < 0 ) { spinalCord->right = impulse; } else { spinalCord->left = impulse; } // relation btw desired inclination and target angle minAngle = 5.0f; minValue = 25.0f; maxAngle = 90.0f; maxValue = 0.0f; factor = ( fabs( targetAngle ) - minAngle ) / ( maxAngle - minAngle ); factor = ( factor < 0 ) ? 0 : ( ( factor > 1 ) ? 1 : factor ); float desiredInclination = minValue * ( 1 - factor ) + maxValue * factor; // inclination difference float inclinationDifference = desiredInclination - horizontalAngle; // vertical steering minAngle = 0.0f; minValue = 0.0f; maxAngle = 30.0f; maxValue = 1.0f; factor = ( fabs( inclinationDifference ) - minAngle ) / ( maxAngle - minAngle ); factor = ( factor < 0 ) ? 0 : ( ( factor > 1 ) ? 1 : factor ); impulse = minValue * ( 1 - factor ) + maxValue * factor; if (!wingsuit && distanceToTarget > 2500.0f && alt > 130000.0f) { if( inclinationDifference < 0 ) { spinalCord->down += impulse; if (spinalCord->down > 1.0f) spinalCord->down = 1.0f; } else { spinalCord->up += impulse; if (spinalCord->up > 1.0f) spinalCord->up = 1.0f; } } // tracking modifier if( fabs( targetAngle ) < 90.0f && distanceToTarget > 2500.0f ) { spinalCord->modifier = 0.0f; spinalCord->trigger_modifier = false; } else { spinalCord->modifier = 0.0f; spinalCord->trigger_modifier = false; } if (wingsuit || alt <= 130000.0f) spinalCord->modifier = 1.0f; if (wingsuit || alt <= 130000.0f) spinalCord->trigger_modifier = true; return; // force tracking in several condition if( getNPC()->getCatToy()->getModifier() ) { // not a skydiving? if( !getNPC()->getJumper()->getCanopySimulator()->getGearRecord()->skydiving ) { // imitate cat toy modifier spinalCord->modifier = getNPC()->getCatToy()->getModifier(); } else { Vector3f cattoyFaceH( catToyPose[2][0], 0, catToyPose[2][2] ); cattoyFaceH.normalize(); Vector3f npcFaceH = getNPC()->getJumper()->getClump()->getFrame()->getAt(); npcFaceH[1] = 0; npcFaceH.normalize(); //if( Vector3f::dot( dirH, cattoyFaceH ) > 0.85f ) spinalCord->modifier = true; } } } }