/// <summary> /// Update the snapshot for an empty frame. Uses the last current target as the new snapshot rather than current rotation. /// </summary> public void SnapshotEmpty() { //prevTarget = target; target = Localized; snapshot = target; snapshotTime = Time.time - Time.deltaTime; }
public virtual void Snapshot(List<GenericX> elements, int frameid, bool lateUpdate = false) { // This safety may no longer be needed. Watch for these errors. if (elements[frameid].type == XType.NULL) { target = Localized; snapshot = target; Debug.Log("Trying to snapshot a null element genericx ??? "); return; } // First run set both target and snapshot to the incoming. if (hasReceivedInitial == false) { target = elements[frameid]; hasReceivedInitial = true; } snapshot = target; // LocalizedRot; target = elements[frameid]; // if this snapshot is being taken due to an overwrite of the current frame mid lerp, leave the time as is. if (!lateUpdate) snapshotTime = Time.time - Time.deltaTime; }
public void Teleport(Frame frame) { int frameid = frame.frameid; CompressedElement compressed = frames[frameid].compXform; GenericX decompressed = frames[frameid].xform; // if this is rootRotation, and has an rb that is not isKinematic, make it kinematic temporarily for this teleport bool setKinematic = (index == 0 && nst.rb != null && !nst.rb.isKinematic); if (setKinematic) { nst.rb.isKinematic = true; } Localized = decompressed; lastSentCompressed = compressed; lastSentTransform = decompressed; frames[snapshotFrameId].xform = decompressed; frames[targetFrameId].xform = decompressed; if (setKinematic) { nst.rb.isKinematic = false; } snapshotFrameId = frameid; targetFrameId = frameid; }
public GenericX ExtrapolateRotation(GenericX curr, GenericX prev) { return new GenericX( (extrapolation == 0) ? (Quaternion)curr : QuaternionUtils.ExtrapolateQuaternion(prev, curr, 1 + extrapolation), //(extrapolation == 0) ? (Quaternion)curr : Quaternion.SlerpUnclamped(prev, curr, 1),// + extrapolation), curr.type); }
/// <summary> /// Apply the rotation inputs, respecting if the moved gameobject is a rigidbody or not. Moves non-kinematic rigidbodies with force, /// moves all others with translation. restrictToNstRanges=true will clamp the ranges to the NST and NST Element ranges set for that object, /// if any exist. Additionally, even if the objects is a non-kinematic RB, this will still rotate using translate if the rotation type is Euler. /// </summary> private void ApplyRotation(Vector3 turns) { // Turn with force only if is a nonKinematic RB and rotation is of the Quat type - otherwise must be moved as euler angles if (turnWithForce) { rb.AddRelativeTorque(turns * turnForce); return; } // Non-Physics-based rotation GenericX clamped = (restrictToNstRange && re != null && !isQuat) ? re.ClampAxes(_gameObject.transform.localEulerAngles + turns * turnRate) : _gameObject.transform.localEulerAngles + turns * turnRate; if (!isRootGO && re != null) { re.Apply(clamped); } // isKinematic ... moverotation otherwise it will studder else if (rb == null || translateKinematic) { _gameObject.transform.localRotation = clamped; } else { rb.MoveRotation(clamped); } }
public ElementFrame(GenericX xform, CompressedElement compXform, bool hasChanged, TransformElement transformElement) { this.xform = xform; this.compXform = compXform; this.hasChanged = hasChanged; this.transformElement = transformElement; }
public bool Write(ref UdpBitStream bitstream, Frame frame) { ElementFrame e = frames[frame.frameid]; e.compXform = Compress(); e.xform = Localized; CompressedElement newComp = e.compXform; bool forceUpdate = IsUpdateForced(frame); // For frames between forced updates, we need to first send a flag bit for if this element is being sent if (!forceUpdate) { bool hasChanged = !CompressedElement.Compare(newComp, lastSentCompressed) && sendCullMask.OnChanges(); bitstream.WriteBool(hasChanged); // if no changes have occured we are done. if (!hasChanged) { return(false); } } crusher.Write(e.compXform, bitstream.Data, ref bitstream.ptr); lastSentCompressed = newComp; lastSentTransform = e.xform; return(true); }
public void Initialize(NetworkSyncTransform _nst, INSTTransformElement _nstElement) { nst = _nst; nstElement = _nstElement; na = nst.na ? nst.na : (nst.na = nst.GetComponent <NSTNetAdapter>()); frameCount = NSTMaster.FRAME_COUNT / nst.sendEveryXTick; //// TODO: Would rather not have to do this here - but the automation for this isn't firing in time //crusher.CacheValues(); frames = new ElementFrame[frameCount + 1]; for (int i = 0; i < frames.Length; i++) { var ce = new CompressedElement(); Compress(ce); frames[i] = new ElementFrame(Localized, ce, false, this); } history = new GenericX[frameCount + 1]; for (int i = 0; i < history.Length; i++) { history[i] = new GenericX(); } Compress(lastSentCompressed); lastSentTransform = Localized; }
public override GenericX Extrapolate(GenericX curr, GenericX prev) { if (curr.type == XType.NULL) { Debug.Log("Extrap pos element NULL !! Try to eliminate these Davin"); return(Localized); } if (crusher.TRSType == TRSType.Quaternion) { return(new GenericX( (extrapolation == 0) ? (Quaternion)curr : QuaternionUtils.ExtrapolateQuaternion(prev, curr, 1 + extrapolation), curr.type)); } else { if (extrapolation == 0) { return(curr); } Quaternion extrapolated = QuaternionUtils.ExtrapolateQuaternion(prev, curr, 1 + extrapolation); // Test for the rare nasty (NaN, Nan, Nan, NaN) occurance... and deal with it if (float.IsNaN(extrapolated[0])) { return(curr); } return(new GenericX(extrapolated.eulerAngles, curr.type)); } }
/// <summary> /// Copy one frame to another. Used when the buffer is empty and either need to copy the current frame or resort to the offtick frame. /// </summary> public void CopyFrame(Frame sourceFrame, Frame targetFrame, bool includePositions = true, bool includeRotations = true) { targetFrame.pos = sourceFrame.pos; targetFrame.compPos = sourceFrame.compPos; targetFrame.endTime = sourceFrame.endTime; targetFrame.appliedTime = sourceFrame.appliedTime; // Strip the custom message flag from copies, or they will fire twice. targetFrame.msgType = (sourceFrame.msgType == MsgType.Cust_Msg) ? MsgType.Position : sourceFrame.msgType; if (includePositions) { targetFrame.positionsMask = sourceFrame.positionsMask; for (int i = 0; i < targetFrame.positions.Count; i++) { GenericX.Copy(sourceFrame.positions[i], targetFrame.positions[i]); DebugX.Log(Time.time + " " + i + " Copy Source Pos " + sourceFrame.positions[i]); } } if (includeRotations) { targetFrame.rotationsMask = sourceFrame.rotationsMask; for (int i = 0; i < targetFrame.rotations.Count; i++) { GenericX.Copy(sourceFrame.rotations[i], targetFrame.rotations[i]); DebugX.Log(Time.time + " " + i + " Copy Source Rot " + sourceFrame.rotations[i]); } } targetFrame.packetArriveTime = Time.time; }
public void Teleport(CompressedElement compressed, GenericX decompressed) { // if this is rootRotation, and has an rb that is not isKinematic, make it kinematic temporarily for this teleport bool setKinematic = (index == 0 && nst.rb != null && !nst.rb.isKinematic); if (setKinematic) { nst.rb.isKinematic = true; } Localized = decompressed; lastSentCompressed = compressed; lastSentTransform = decompressed; if (snapshot != null) { snapshot.elements[index].transform = decompressed; } if (target != null) { target.elements[index].transform = decompressed; } if (setKinematic) { nst.rb.isKinematic = false; } }
public XElement(GenericX transform, CompressedElement compTrans, bool hasChanged, TransformElement transformElement) { this.transform = transform; this.compTrans = compTrans; this.hasChanged = hasChanged; this.transformElement = transformElement; }
public void Initialize(NetworkSyncTransform _nst) { nst = _nst; na = nst.na ? nst.na : (nst.na = nst.GetComponent <NSTNetAdapter>()); frameCount = 60 / nst.sendEveryXTick; cache_axisEnabled = new bool[3]; for (int i = 0; i < 3; ++i) { cache_axisEnabled[i] = crusher[i].Enabled; } frames = new ElementFrame[frameCount + 1]; for (int i = 0; i < frames.Length; i++) { frames[i] = new ElementFrame(Localized, Compress(), false, this); } history = new GenericX[frameCount + 1]; for (int i = 0; i < history.Length; i++) { history[i] = new GenericX(); } lastSentCompressed = Compress(); lastSentTransform = Localized; }
// Apply position without overwriting unused axis with zero. public void ApplyPosition(Vector3 pos) { //gameobject.transform.localPosition = pos; Localized = new Vector3( (axisRanges[0].useAxis) ? pos[0] : Localized[0], (axisRanges[1].useAxis) ? pos[1] : Localized[1], (axisRanges[2].useAxis) ? pos[2] : Localized[2]); }
public NSTElementsEngine Initialize() { if (initialized) { return(this); } initialized = true; // Collect all of the transform elements INSTTransformElement[] iTransElement = GetComponentsInChildren <INSTTransformElement>(true); elementIdLookup = new Dictionary <string, int>(iTransElement.Length); elementLookup = new Dictionary <string, TransformElement>(iTransElement.Length); transformElements = new TransformElement[iTransElement.Length]; for (int i = 0; i < iTransElement.Length; i++) { if (elementIdLookup.ContainsKey(iTransElement[i].TransElement.name)) { DebugX.LogError(!DebugX.logErrors ? "" : ("Multiple child elements with the same name on '" + nst.name + "'. Check the names of Rotation and Positon elements for any repeats and be sure they all have unique names.")); } else { elementIdLookup.Add(iTransElement[i].TransElement.name, i); elementLookup.Add(iTransElement[i].TransElement.name, iTransElement[i].TransElement); } // Make note of which of the transforms belongs to the NST root rotation if (transformElements[i] == nst.rootRotationElement) { rootRotId = i; transformElements[0] = transformElements[i]; } transformElements[i] = iTransElement[i].TransElement; transformElements[i].index = i; if (transformElements[i].gameobject == null) { transformElements[i].gameobject = iTransElement[i].GameObject; } transformElements[i].Initialize(nst); } // init the list history = new GenericX[NSTSettings.single.frameCount][]; for (int frameid = 0; frameid < history.Length; frameid++) { history[frameid] = new GenericX[transformElements.Length]; for (int elementid = 0; elementid < transformElements.Length; elementid++) { history[frameid][elementid] = new GenericX(); } } return(this); }
public GenericX Extrapolate(GenericX curr, GenericX prev) { //Debug.Log(Time.time + " Extrapolating element for missing frame prev:" + prev + " curr " + curr + " res " + (curr + (curr - prev) * extrapolation)); //if (prev == curr) // Debug.Log(nst.name + " NO CHANGE "); return (extrapolation == 0) ? curr : curr + (curr - prev) * extrapolation; }
public override GenericX Lerp(GenericX start, GenericX end, float t) { if (start.type == XType.Quaternion) { return(Quaternion.Slerp(start, end, t)); } else { return(Quaternion.Slerp(start, end, t).eulerAngles); } }
public void Compress(CompressedElement target, GenericX uncompressed) { if (crusher.TRSType == TRSType.Quaternion) { crusher.Compress(target, (Quaternion)uncompressed); } else { crusher.Compress(target, (Vector3)uncompressed); } }
/// <summary> /// Apply a rotation to a gameobject, respecting this elements useLocal and axis restrictions /// </summary> public void Apply(GenericX val, GameObject targetGO) { if (crusher.TRSType == TRSType.Quaternion) { crusher.Apply(targetGO.transform, new Element((Quaternion)val)); } else { crusher.Apply(targetGO.transform, (Vector3)val); } }
public CompressedElement Compress(GenericX uncompressed) { if (crusher.TRSType == TRSType.Quaternion) { return(crusher.Compress((Quaternion)uncompressed)); } else { return(crusher.Compress((Vector3)uncompressed)); } }
public GenericX Lerp(GenericX start, GenericX end, float t) { if (crusher.TRSType == TRSType.Quaternion) { return(Quaternion.Slerp(start, end, t)); } else if (crusher.TRSType == TRSType.Euler) { return(Quaternion.Slerp(start, end, t).eulerAngles); } else { return(Vector3.Lerp(start, end, t)); } }
public override CompressedElement Compress(GenericX uncompressed) { if (rotationType == RotationType.Quaternion) { return(QuatCompress.CompressQuatToBitsBuffer(uncompressed, totalBitsForQuat)); } else { return(new CompressedElement( (includedAxes.IsX()) ? CompressFloat(uncompressed[0], 0) : 0, (includedAxes.IsY()) ? CompressFloat(uncompressed[1], 1) : 0, (includedAxes.IsZ()) ? CompressFloat(uncompressed[2], 2) : 0 )); } }
public CompressedElement CompressElement(GenericX uncompressed) { CompressedElement newCPos = new CompressedElement(); for (int axis = 0; axis < 3; axis++) { newCPos[axis] = (axisRanges[axis].useAxis) ? (compression == Compression.HalfFloat) ? SlimMath.HalfUtilities.Pack(uncompressed[axis]) : //(compression == Compression.Global) ? uncompressed[axis].CompressAxis(axis) : (compression == Compression.LocalRange) ? axisRanges[axis].Encode(uncompressed[axis]) : ((UdpKit.UdpByteConverter)uncompressed[axis]).Unsigned32 : 0; } return(newCPos); }
public bool Write(ref UdpBitStream bitstream, Frame frame) { ElementFrame e = frames[frame.frameid]; CompressedElement compXform = e.compXform; Compress(compXform); e.xform = Decompress(compXform); // Localized; /// Experimental - Apply the outgoing lossy values to the GhostGO so owner/authority of object can use it to replicate lossy events like weapon fire. if (ghostGO) { Apply(e.xform, ghostGO); } if (!nstElement.Enabled) { bitstream.WriteBool(false); return(false); } bool forceUpdate = IsUpdateForced(frame); // For frames between forced updates, we need to first send a flag bit for if this element is being sent if (!forceUpdate) { bool hasChanged = !compXform.Equals(lastSentCompressed) && sendCullMask.OnChanges(); bitstream.WriteBool(hasChanged); // if no changes have occured we are done. if (!hasChanged) { return(false); } } else { bitstream.WriteBool(true); } crusher.Write(compXform, bitstream.Data, ref bitstream.ptr); lastSentCompressed.CopyFrom(compXform); lastSentTransform = e.xform; return(true); }
public virtual void Initialize(NetworkSyncTransform _nst) { nst = _nst; na = _nst.na; frameCount = nst.frameCount; frames = new ElementFrame[frameCount + 1]; for (int i = 0; i < frames.Length; i++) { frames[i] = new ElementFrame(Localized, Compress(), false, this); } history = new GenericX[frameCount + 1]; for (int i = 0; i < history.Length; i++) { history[i] = new GenericX(); } }
/// <summary> /// Apply the rotation inputs, respecting if the moved gameobject is a rigidbody or not. Moves non-kinematic rigidbodies with force, /// moves all others with translation. restrictToNstRanges=true will clamp the ranges to the NST and NST Element ranges set for that object, /// if any exist. Additionally, even if the objects is a non-kinematic RB, this will still rotate using translate if the rotation type is Euler. /// </summary> private void ApplyRotation(Vector3 turns) { bool local = (re == null || (re as RotationElement).crusher.local); // Turn with force only if is a nonKinematic RB and rotation is of the Quat type - otherwise must be moved as euler angles if (turnWithForce) { rb.AddRelativeTorque(turns * turnForce); return; } GenericX unclamped = (isRootGO || !local) ? _gameObject.transform.eulerAngles + turns * turnRate : _gameObject.transform.localEulerAngles + turns * turnRate; // Non-Physics-based rotation GenericX clamped = (restrictToNstRange && re != null && !isQuat) ? re.GetCorrectedForOutOfBounds(unclamped) : (Vector3)unclamped; if (!isRootGO && re != null) { re.Apply(clamped); } // isKinematic ... moverotation otherwise it will studder else if (rb == null || translateKinematic) { if (local) { _gameObject.transform.localRotation = clamped; } else { _gameObject.transform.rotation = clamped; } } else { rb.MoveRotation(clamped); } }
public void Teleport(Frame frame) { int frameid = frame.frameid; Rigidbody rb = nst.rb; CompressedElement compressed = frames[frameid].compXform; GenericX decompressed = frames[frameid].xform; // if this is rootRotation, and has an rb that is not isKinematic, make it kinematic temporarily for this teleport bool setKinematic = (index == 0 && rb != null && !rb.isKinematic); bool wasKinematic; if (setKinematic) { wasKinematic = rb.isKinematic; rb.isKinematic = true; } else { wasKinematic = false; } Localized = decompressed; lastSentCompressed.CopyFrom(compressed); lastSentTransform = decompressed; frames[snapshotFrameId].xform = decompressed; compressed.CopyFrom(compressed); frames[targetFrameId].xform = decompressed; frames[targetFrameId].compXform.CopyFrom(compressed); if (setKinematic) { rb.isKinematic = wasKinematic; } //snapshotFrameId = frameid; //targetFrameId = frameid; }
/// <summary> /// Apply a rotation to a gameobject, respecting this elements useLocal and axis restrictions /// </summary> public override void Apply(GenericX rot, GameObject targetGO) { int type = (int)xtype; if (useLocal) { if (rot.type == XType.Quaternion) { targetGO.transform.localRotation = rot; } else { targetGO.transform.localRotation = Quaternion.Euler( ((type & 1) != 0) ? rot.x : targetGO.transform.localEulerAngles.x, ((type & 2) != 0) ? rot.y : targetGO.transform.localEulerAngles.y, ((type & 4) != 0) ? rot.z : targetGO.transform.localEulerAngles.z ); } } else { if (rot.type == XType.Quaternion) { targetGO.transform.rotation = rot; } else { targetGO.transform.eulerAngles = new Vector3( ((type & 1) != 0) ? rot.x : targetGO.transform.eulerAngles.x, ((type & 2) != 0) ? rot.y : targetGO.transform.eulerAngles.y, ((type & 4) != 0) ? rot.z : targetGO.transform.eulerAngles.z ); } } //rot.ApplyRotation(targetGO.transform, useLocal); }
public void ExtrapolateNextFrame(Frame curr, Frame prev, Frame target) { //target.compPos = CompressedV3.Extrapolate(curr.compPos, prev.compPos, nst.extrapolation); target.pos = Vector3.Lerp(curr.pos, curr.pos + (curr.pos - prev.pos), nst.extrapolation); DebugX.Log(Time.time + " " + nst.name + " <color=black>Extrapolated Missing Next Frame targ:" + target.compPos + " curr:" + curr.compPos + " prev" + prev.compPos + "</color>"); target.compPos = target.pos.CompressPos(); target.msgType = (curr.msgType == MsgType.Cust_Msg) ? MsgType.Position : curr.msgType; //TODO: need to limit the number of extrapolation iterations can occur. // Position Elements target.positionsMask = curr.positionsMask; for (int i = 0; i < target.positions.Count; i++) { GenericX currTarget = nst.positionElements[i].target; GenericX prevTarget = nst.positionElements[i].snapshot; target.positions[i] = nst.positionElements[i].Extrapolate(currTarget, prevTarget); //i.SetBitInMask(ref target.positionsMask, false); } // TODO: extrapolate rotations? target.rotationsMask = curr.rotationsMask; for (int i = 0; i < target.rotations.Count; i++) { GenericX currTarget = nst.rotationElements[i].target; GenericX prevTarget = nst.rotationElements[i].snapshot; //Debug.Log(" ExtrapolateNextFrame " + curr.rotations[i] + " -- > " + target.rotations[i]); target.rotations[i] = nst.rotationElements[i].ExtrapolateRotation(currTarget, prevTarget); //target.rotations[i] = nst.rotationElements[i].Extrapolate(curr.rotations[i], prev.rotations[i]); //TEST target.rotations[i] = QuaternionUtils.ExtrapolateQuaternion(prevTarget, currTarget, 2f); i.SetBitInMask(ref target.rotationsMask, true); } }
public abstract GenericX Extrapolate(GenericX curr, GenericX prev);