/// <summary> /// Generate rigid body transform snapshot for owned transforms with specified timestamp. /// </summary> /// <param name="time">Snapshot timestamp.</param> /// <param name="rootTransform">Root transform.</param> /// <returns>Generated snapshot.</returns> public Snapshot GenerateSnapshot(float time, UnityEngine.Transform rootTransform) { // collect transforms from owned rigid bodies // and generate update packet/snapshot List <Snapshot.TransformInfo> transforms = new List <Snapshot.TransformInfo>(_rigidBodies.Count); foreach (var rb in _rigidBodies.Values) { if (!rb.Ownership) { continue; } RigidBodyTransform transform; { transform.Position = rootTransform.InverseTransformPoint(rb.RigidBody.transform.position); transform.Rotation = UnityEngine.Quaternion.Inverse(rootTransform.rotation) * rb.RigidBody.transform.rotation; } Patching.Types.MotionType mType = (rb.IsKeyframed) ? (Patching.Types.MotionType.Keyframed) : (Patching.Types.MotionType.Dynamic); transforms.Add(new Snapshot.TransformInfo(rb.Id, transform, mType)); #if MRE_PHYSICS_DEBUG Debug.Log(" SEND Remote body: " + rb.Id.ToString() + " OriginalRot:" + transform.Rotation + " RigidBodyRot:" + rb.RigidBody.transform.rotation); #endif } return(new Snapshot(time, transforms)); }
/// <summary> /// Generate rigid body transform snapshot for owned transforms with specified timestamp. /// </summary> /// <param name="time">Snapshot timestamp.</param> /// <param name="rootTransform">Root transform.</param> /// <returns>Generated snapshot.</returns> public Snapshot GenerateSnapshot(float time, UnityEngine.Transform rootTransform) { // collect transforms from owned rigid bodies // and generate update packet/snapshot // these constants define when a body is considered to be sleeping const float globalToleranceMultipier = 1.0F; const float maxSleepingSqrtLinearVelocity = 0.1F * globalToleranceMultipier; const float maxSleepingSqrtAngularVelocity = 0.1F * globalToleranceMultipier; const float maxSleepingSqrtPositionDiff = 0.02F * globalToleranceMultipier; const float maxSleepingSqrtAngularEulerDiff = 0.15F * globalToleranceMultipier; const short limitNoUpdateForSleepingBodies = 500; const short numConsecutiveSleepingTrueConditionForNoUpdate = 5; int numSleepingBodies = 0; int numOwnedBodies = 0; List <Snapshot.TransformInfo> transforms = new List <Snapshot.TransformInfo>(_rigidBodies.Count); foreach (var rb in _rigidBodies.Values) { if (!rb.Ownership) { rb.numOfConsequentSleepingFrames = 0; continue; } RigidBodyTransform transform; { transform.Position = rootTransform.InverseTransformPoint(rb.RigidBody.transform.position); transform.Rotation = UnityEngine.Quaternion.Inverse(rootTransform.rotation) * rb.RigidBody.transform.rotation; } numOwnedBodies++; Patching.Types.MotionType mType = (rb.IsKeyframed) ? (Patching.Types.MotionType.Keyframed) : (Patching.Types.MotionType.Dynamic); UnityEngine.Vector3 posDiff = rb.lastValidLinerVelocityOrPos - transform.Position; UnityEngine.Vector3 rotDiff = UtilMethods.TransformEulerAnglesToRadians(rb.lastValidAngularVelocityorAng - transform.Rotation.eulerAngles); bool isBodySleepingInThisFrame = (!rb.IsKeyframed) && // if body is key framed and owned then we should just feed the jitter buffer (rb.RigidBody.velocity.sqrMagnitude < maxSleepingSqrtLinearVelocity && rb.RigidBody.angularVelocity.sqrMagnitude < maxSleepingSqrtAngularVelocity && posDiff.sqrMagnitude < maxSleepingSqrtPositionDiff && rotDiff.sqrMagnitude < maxSleepingSqrtAngularEulerDiff); if (isBodySleepingInThisFrame) { rb.numOfConsequentSleepingFrames++; rb.numOfConsequentSleepingFrames = (rb.numOfConsequentSleepingFrames > (short)limitNoUpdateForSleepingBodies) ? (short)limitNoUpdateForSleepingBodies : rb.numOfConsequentSleepingFrames; } else { rb.numOfConsequentSleepingFrames = 0; } // this is the real condition to put a body to sleep bool isBodySleeping = (rb.numOfConsequentSleepingFrames > numConsecutiveSleepingTrueConditionForNoUpdate); // test if this is sleeping, and when this was newly added then if (rb.lastTimeKeyFramedUpdate > 0.001F && isBodySleeping && rb.sendMotionType == Patching.Types.MotionType.Sleeping && rb.numOfConsequentSleepingFrames < limitNoUpdateForSleepingBodies) { // condition for velocity and positions are triggered and we already told the consumers to make body sleep, so just skip this update numSleepingBodies++; continue; } mType = (isBodySleeping) ? (Patching.Types.MotionType.Sleeping) : mType; // here we handle the case when after of 300 frames with no update we should send one update at least if (rb.numOfConsequentSleepingFrames >= limitNoUpdateForSleepingBodies) { rb.numOfConsequentSleepingFrames = numConsecutiveSleepingTrueConditionForNoUpdate + 1; } // store the last update informations rb.sendMotionType = mType; rb.lastTimeKeyFramedUpdate = time; rb.lastValidLinerVelocityOrPos = transform.Position; rb.lastValidAngularVelocityorAng = transform.Rotation.eulerAngles; transforms.Add(new Snapshot.TransformInfo(rb.Id, transform, mType)); #if MRE_PHYSICS_DEBUG Debug.Log(" SEND Remote body: " + rb.Id.ToString() + " OriginalRot:" + transform.Rotation + " RigidBodyRot:" + rb.RigidBody.transform.rotation + " lv:" + rb.RigidBody.velocity + " av:" + rb.RigidBody.angularVelocity + " posDiff:" + posDiff + " rotDiff:" + rotDiff + " isKeyF:" + rb.IsKeyframed); #endif } Snapshot.SnapshotFlags snapshotFlag = Snapshot.SnapshotFlags.NoFlags; bool allBodiesAreSleepingNew = numOwnedBodies == numSleepingBodies; if (allBodiesAreSleepingNew != _allOwnedBodiesAreSleeping) { _allOwnedBodiesAreSleeping = allBodiesAreSleepingNew; snapshotFlag = Snapshot.SnapshotFlags.ResetJitterBuffer; } _lastNumberOfTransformsToBeSent = numOwnedBodies; var ret = new Snapshot(time, transforms, snapshotFlag); #if MRE_PHYSICS_DEBUG Debug.Log(" Client:" + " Total number of sleeping bodies: " + numSleepingBodies + " total RBs" + _rigidBodies.Count + " num owned " + numOwnedBodies + " num sent transforms " + transforms.Count + " send:" + ret.DoSendThisSnapshot()); #endif return(ret); }