holopy3/Assets/Normal/Realtime/Components/RealtimeTransformModel.cs
2020-12-10 15:25:12 +01:00

375 lines
16 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Normal.Realtime.Serialization;
public partial class RealtimeTransformModel {
[RealtimeProperty(1, false)] private Vector3 _position;
[RealtimeProperty(2, false)] private Quaternion _rotation;
[RealtimeProperty(3, false)] private Vector3 _scale = Vector3.one;
[RealtimeProperty(4, false)] private Vector3 _velocity;
[RealtimeProperty(5, false)] private Vector3 _angularVelocity;
//[RealtimeProperty(6, false)] private Vector3 _scaleVelocity;
[Flags]
private enum Flags : uint {
Default = 0,
ShouldExtrapolate = 1 << 0,
UseGravity = 1 << 1,
IsKinematic = 1 << 2,
}
[RealtimeProperty(7, true)] private uint _physicsState = 0;
[RealtimeProperty(8, false)] private double _timestamp;
// Used to signal whether RealtimeTransform should set defaults in SetModel
public bool freshModel { get; private set; }
// TODO: Move this to the common model base class once it's written.
public delegate void RealtimeTransformModelEvent(RealtimeTransformModel model);
public event RealtimeTransformModelEvent willWrite;
public event RealtimeTransformModelEvent didRead;
private bool _didDispatchWillWriteEvent = false;
}
public partial class RealtimeTransformModel : IModel {
// Properties
public UnityEngine.Vector3 position {
get { return _position; }
set { if (!shouldWritePosition) return; if (value == _position) return; _positionShouldWrite = true; _position = value; }
}
public UnityEngine.Quaternion rotation {
get { return _rotation; }
set { if (!shouldWriteRotation) return; if (value == _rotation) return; _rotationShouldWrite = true; _rotation = value; }
}
public UnityEngine.Vector3 scale {
get { return _scale; }
set { if (!shouldWriteScale) return; if (value == _scale) return; _scaleShouldWrite = true; _scale = value; }
}
public UnityEngine.Vector3 velocity {
get { return _velocity; }
set { if (!shouldWriteVelocityMetadata) return; if (value == _velocity) return; _velocityShouldWrite = true; _velocity = value; }
}
public UnityEngine.Vector3 angularVelocity {
get { return _angularVelocity; }
set { if (!shouldWriteVelocityMetadata) return; if (value == _angularVelocity) return; _angularVelocityShouldWrite = true; _angularVelocity = value; }
}
//public UnityEngine.Vector3 scaleVelocity {
// get { return _scaleVelocity; }
// set { if (value == _scaleVelocity) return; _scaleVelocityShouldWrite = true; _scaleVelocity = value; }
//}
public double timestamp {
get { return _timestamp; }
set { if (value == _timestamp) return; _timestampShouldWrite = true; _timestamp = value; }
}
private uint physicsState {
get { return _cache.LookForValueInCache(_physicsState, entry => entry.physicsStateSet, entry => entry.physicsState); }
set { _cache.UpdateLocalCache(entry => { entry.physicsStateSet = true; entry.physicsState = value; return entry; }); }
}
public bool shouldExtrapolate { get { return (physicsState & (uint)Flags.ShouldExtrapolate) != 0; } set { if (value == shouldExtrapolate) return; if (value) physicsState |= (uint)Flags.ShouldExtrapolate; else physicsState &= ~((uint)Flags.ShouldExtrapolate); } }
public bool useGravity { get { return (physicsState & (uint)Flags.UseGravity) != 0; } set { if (value == useGravity) return; if (value) physicsState |= (uint)Flags.UseGravity; else physicsState &= ~((uint)Flags.UseGravity); } }
public bool isKinematic { get { return (physicsState & (uint)Flags.IsKinematic) != 0; } set { if (value == isKinematic) return; if (value) physicsState |= (uint)Flags.IsKinematic; else physicsState &= ~((uint)Flags.IsKinematic); } }
// Previous snapshot
public Vector3 previousPosition { get; private set; }
public Quaternion previousRotation { get; private set; }
public double previousTimestamp { get; private set; }
// Will this model write
public bool ModelPoseChangesToSend() {
return _positionShouldWrite || _rotationShouldWrite || _scaleShouldWrite;
}
public bool shouldWritePosition = false;
public bool shouldWriteRotation = false;
public bool shouldWriteScale = false;
public bool shouldWriteVelocityMetadata = false;
// Ownership
public int ownerID { get { return _metaModel.ownerID; } }
public void RequestOwnership(int clientIndex) {
_metaModel.ownerID = clientIndex;
}
public void ClearOwnership() {
_metaModel.ownerID = -1;
}
// Meta model
private MetaModel _metaModel;
private bool _positionShouldWrite;
private bool _rotationShouldWrite;
private bool _scaleShouldWrite;
private bool _velocityShouldWrite;
private bool _angularVelocityShouldWrite;
//private bool _scaleVelocityShouldWrite;
private bool _timestampShouldWrite;
// Change Cache
private struct LocalCacheEntry {
public bool physicsStateSet;
public uint physicsState;
}
private LocalChangeCache<LocalCacheEntry> _cache;
public RealtimeTransformModel() {
freshModel = true;
_metaModel = new MetaModel();
_cache = new LocalChangeCache<LocalCacheEntry>();
}
// Serialization
enum PropertyID {
Position = 1,
Rotation = 2,
Scale = 3,
Velocity = 4,
AngularVelocity = 5,
ScaleVelocity = 6,
PhysicsState = 7,
Timestamp = 8,
}
public int WriteLength(StreamContext context) {
// Dispatch will write event.
if (!_didDispatchWillWriteEvent) {
// Mark dispatched first in case willWrite triggers a WriteLength() call.
_didDispatchWillWriteEvent = true;
if (willWrite != null) {
try {
willWrite(this);
} catch (Exception exception) {
Debug.LogException(exception);
}
}
}
int length = 0;
if (context.fullModel) {
// Flatten cache
_physicsState = physicsState;
_cache.Clear();
// Meta model
length += WriteStream.WriteModelLength(0, _metaModel, context);
// Write all properties
if (shouldWritePosition)
length += WriteStream.WriteBytesLength((uint)PropertyID.Position, WriteStream.Vector3ToBytesLength());
if (shouldWriteRotation)
length += WriteStream.WriteBytesLength((uint)PropertyID.Rotation, WriteStream.QuaternionToBytesLength());
if (shouldWriteScale)
length += WriteStream.WriteBytesLength((uint)PropertyID.Scale, WriteStream.Vector3ToBytesLength());
if (shouldWriteVelocityMetadata) {
length += WriteStream.WriteBytesLength((uint)PropertyID.Velocity, WriteStream.Vector3ToBytesLength());
length += WriteStream.WriteBytesLength((uint)PropertyID.AngularVelocity, WriteStream.Vector3ToBytesLength());
//length += WriteStream.WriteBytesLength((uint)PropertyID.ScaleVelocity, WriteStream.Vector3ToBytesLength());
}
length += WriteStream.WriteVarint32Length((uint)PropertyID.PhysicsState, _physicsState);
length += WriteStream.WriteBytesLength((uint)PropertyID.Timestamp, sizeof(double));
} else {
// Meta model
length += WriteStream.WriteModelLength(0, _metaModel, context);
// Unreliable properties
if (context.unreliableChannel) {
if (_positionShouldWrite) {
length += WriteStream.WriteBytesLength((uint)PropertyID.Position, WriteStream.Vector3ToBytesLength());
}
if (_rotationShouldWrite) {
length += WriteStream.WriteBytesLength((uint)PropertyID.Rotation, WriteStream.QuaternionToBytesLength());
}
if (_scaleShouldWrite) {
length += WriteStream.WriteBytesLength((uint)PropertyID.Scale, WriteStream.Vector3ToBytesLength());
}
if (_velocityShouldWrite) {
length += WriteStream.WriteBytesLength((uint)PropertyID.Velocity, WriteStream.Vector3ToBytesLength());
}
if (_angularVelocityShouldWrite) {
length += WriteStream.WriteBytesLength((uint)PropertyID.AngularVelocity, WriteStream.Vector3ToBytesLength());
}
///if (_scaleVelocityShouldWrite) {
// length += WriteStream.WriteBytesLength((uint)PropertyID.ScaleVelocity, WriteStream.Vector3ToBytesLength());
//}
if (_timestampShouldWrite) {
length += WriteStream.WriteBytesLength((uint)PropertyID.Timestamp, sizeof(double));
}
} else if (context.reliableChannel) {
LocalCacheEntry entry = _cache.localCache;
if (entry.physicsStateSet)
length += WriteStream.WriteVarint32Length((uint)PropertyID.PhysicsState, entry.physicsState);
}
}
// If the length is zero, a Write event will never get called.
// TODO: Unfortunately this means that if the WriteLength is zero, the event will be dispatched multiple times in a single WriteStream.Serialize() pass.
// If the model changes between those events, serialization will fail...
if (length == 0)
_didDispatchWillWriteEvent = false;
return length;
}
public void Write(WriteStream stream, StreamContext context) {
if (context.fullModel) {
// Meta model
stream.WriteModel(0, _metaModel, context);
// Write all properties
if (shouldWritePosition)
stream.WriteBytes((uint)PropertyID.Position, WriteStream.Vector3ToBytes(_position));
if (shouldWriteRotation)
stream.WriteBytes((uint)PropertyID.Rotation, WriteStream.QuaternionToBytes(_rotation));
if (shouldWriteScale)
stream.WriteBytes((uint)PropertyID.Scale, WriteStream.Vector3ToBytes(_scale));
if (shouldWriteVelocityMetadata) {
stream.WriteBytes((uint)PropertyID.Velocity, WriteStream.Vector3ToBytes(_velocity));
stream.WriteBytes((uint)PropertyID.AngularVelocity, WriteStream.Vector3ToBytes(_angularVelocity));
//stream.WriteBytes((uint)PropertyID.ScaleVelocity, WriteStream.Vector3ToBytes(_scaleVelocity));
}
stream.WriteVarint32((uint)PropertyID.PhysicsState, _physicsState);
stream.WriteBytes((uint)PropertyID.Timestamp, BitConverter.GetBytes(_timestamp));
} else {
// Meta model
stream.WriteModel(0, _metaModel, context);
// Unreliable properties
if (context.unreliableChannel) {
if (_positionShouldWrite) {
stream.WriteBytes((uint)PropertyID.Position, WriteStream.Vector3ToBytes(_position));
_positionShouldWrite = false;
}
if (_rotationShouldWrite) {
stream.WriteBytes((uint)PropertyID.Rotation, WriteStream.QuaternionToBytes(_rotation));
_rotationShouldWrite = false;
}
if (_scaleShouldWrite) {
stream.WriteBytes((uint)PropertyID.Scale, WriteStream.Vector3ToBytes(_scale));
_scaleShouldWrite = false;
}
if (_velocityShouldWrite) {
stream.WriteBytes((uint)PropertyID.Velocity, WriteStream.Vector3ToBytes(_velocity));
_velocityShouldWrite = false;
}
if (_angularVelocityShouldWrite) {
stream.WriteBytes((uint)PropertyID.AngularVelocity, WriteStream.Vector3ToBytes(_angularVelocity));
_angularVelocityShouldWrite = false;
}
//if (_scaleVelocityShouldWrite) {
// stream.WriteBytes((uint)PropertyID.ScaleVelocity, WriteStream.Vector3ToBytes(_scaleVelocity));
// _scaleVelocityShouldWrite = false;
//}
if (_timestampShouldWrite) {
stream.WriteBytes((uint)PropertyID.Timestamp, BitConverter.GetBytes(_timestamp));
_timestampShouldWrite = false;
}
} else if (context.reliableChannel) {
// If we're going to send an update. Push the cache to inflight.
LocalCacheEntry entry = _cache.localCache;
if (entry.physicsStateSet)
_cache.PushLocalCacheToInflight(context.updateID);
if (entry.physicsStateSet)
stream.WriteVarint32((uint)PropertyID.PhysicsState, entry.physicsState);
}
}
// Reset will write event flag.
_didDispatchWillWriteEvent = false;
}
public void Read(ReadStream stream, StreamContext context) {
// Used to signal whether RealtimeTransform should set defaults in SetModel
freshModel = false;
// Remove from in-flight
if (context.deltaUpdatesOnly && context.reliableChannel)
_cache.RemoveUpdateFromInflight(context.updateID);
// Store previous snapshot (used for transform extrapolation)
Vector3 preReadPosition = position;
Quaternion preReadRotation = rotation;
// Loop through each property and deserialize
uint propertyID;
while (stream.ReadNextPropertyID(out propertyID)) {
switch (propertyID) {
case 0:
// Meta model
stream.ReadModel(_metaModel, context);
break;
case (uint)PropertyID.Position: {
_position = ReadStream.Vector3FromBytes(stream.ReadBytes());
_positionShouldWrite = false;
break;
}
case (uint)PropertyID.Rotation: {
_rotation = ReadStream.QuaternionFromBytes(stream.ReadBytes());
_rotationShouldWrite = false;
break;
}
case (uint)PropertyID.Scale: {
_scale = ReadStream.Vector3FromBytes(stream.ReadBytes());
_scaleShouldWrite = false;
break;
}
case (uint)PropertyID.Velocity: {
_velocity = ReadStream.Vector3FromBytes(stream.ReadBytes());
_velocityShouldWrite = false;
break;
}
case (uint)PropertyID.AngularVelocity: {
_angularVelocity = ReadStream.Vector3FromBytes(stream.ReadBytes());
_angularVelocityShouldWrite = false;
break;
}
//case (uint)PropertyID.ScaleVelocity: {
// _scaleVelocity = ReadStream.Vector3FromBytes(stream.ReadBytes());
// _scaleVelocityShouldWrite = false;
// break;
//}
case (uint)PropertyID.PhysicsState: {
_physicsState = stream.ReadVarint32();
break;
}
case (uint)PropertyID.Timestamp: {
// We got a timestamp, cache the previous snapshot's values.
previousPosition = preReadPosition;
previousRotation = preReadRotation;
previousTimestamp = timestamp;
byte[] bytes = stream.ReadBytes();
_timestamp = BitConverter.ToDouble(bytes, 0);
break;
}
default:
stream.SkipProperty();
break;
}
}
if (didRead != null) {
try {
didRead(this);
} catch (Exception exception) {
Debug.LogException(exception);
}
}
}
}