Add NetworkTransform component

This commit is contained in:
Wojciech Figat
2022-10-31 16:31:06 +01:00
parent 5e81d83648
commit 01a7ae8bd4
7 changed files with 272 additions and 5 deletions

View File

@@ -0,0 +1,169 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "NetworkTransform.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Level/Actor.h"
#include "Engine/Networking/NetworkReplicator.h"
#include "Engine/Networking/NetworkStream.h"
PACK_STRUCT(struct Data
{
uint8 LocalSpace : 1;
NetworkTransform::SyncModes SyncMode : 9;
});
static_assert((int32)NetworkTransform::SyncModes::All + 1 == 512, "Invalid SyncModes bit count for Data.");
NetworkTransform::NetworkTransform(const SpawnParams& params)
: Script(params)
{
}
void NetworkTransform::OnEnable()
{
// Register for replication
NetworkReplicator::AddObject(this);
}
void NetworkTransform::OnDisable()
{
// Unregister from replication
NetworkReplicator::RemoveObject(this);
}
void NetworkTransform::Serialize(NetworkStream* stream)
{
// Get transform
Transform transform;
if (const auto* parent = GetParent())
transform = LocalSpace ? parent->GetLocalTransform() : parent->GetTransform();
else
transform = Transform::Identity;
// Encode data
Data data;
data.LocalSpace = LocalSpace;
data.SyncMode = SyncMode;
stream->Write(data);
if ((data.SyncMode & SyncModes::All) == (int)SyncModes::All)
{
stream->Write(transform);
}
else
{
if ((data.SyncMode & SyncModes::Position) == (int)SyncModes::Position)
{
stream->Write(transform.Translation);
}
else if (data.SyncMode & SyncModes::Position)
{
if (data.SyncMode & SyncModes::PositionX)
stream->Write(transform.Translation.X);
if (data.SyncMode & SyncModes::PositionY)
stream->Write(transform.Translation.X);
if (data.SyncMode & SyncModes::PositionZ)
stream->Write(transform.Translation.X);
}
if ((data.SyncMode & SyncModes::Scale) == (int)SyncModes::Scale)
{
stream->Write(transform.Scale);
}
else if (data.SyncMode & SyncModes::Scale)
{
if (data.SyncMode & SyncModes::ScaleX)
stream->Write(transform.Scale.X);
if (data.SyncMode & SyncModes::ScaleY)
stream->Write(transform.Scale.X);
if (data.SyncMode & SyncModes::ScaleZ)
stream->Write(transform.Scale.X);
}
if ((data.SyncMode & SyncModes::Rotation) == (int)SyncModes::Rotation)
{
const Float3 rotation = transform.Orientation.GetEuler();
stream->Write(rotation);
}
else if (data.SyncMode & SyncModes::Rotation)
{
const Float3 rotation = transform.Orientation.GetEuler();
if (data.SyncMode & SyncModes::RotationX)
stream->Write(rotation.X);
if (data.SyncMode & SyncModes::RotationY)
stream->Write(rotation.Y);
if (data.SyncMode & SyncModes::RotationZ)
stream->Write(rotation.Z);
}
}
}
void NetworkTransform::Deserialize(NetworkStream* stream)
{
// Get transform
Transform transform;
if (const auto* parent = GetParent())
transform = LocalSpace ? parent->GetLocalTransform() : parent->GetTransform();
else
transform = Transform::Identity;
// Decode data
Data data;
stream->Read(data);
if ((data.SyncMode & SyncModes::All) == (int)SyncModes::All)
{
stream->Read(transform);
}
else
{
if ((data.SyncMode & SyncModes::Position) == (int)SyncModes::Position)
{
stream->Read(transform.Translation);
}
else if (data.SyncMode & SyncModes::Position)
{
if (data.SyncMode & SyncModes::PositionX)
stream->Read(transform.Translation.X);
if (data.SyncMode & SyncModes::PositionY)
stream->Read(transform.Translation.X);
if (data.SyncMode & SyncModes::PositionZ)
stream->Read(transform.Translation.X);
}
if ((data.SyncMode & SyncModes::Scale) == (int)SyncModes::Scale)
{
stream->Read(transform.Scale);
}
else if (data.SyncMode & SyncModes::Scale)
{
if (data.SyncMode & SyncModes::ScaleX)
stream->Read(transform.Scale.X);
if (data.SyncMode & SyncModes::ScaleY)
stream->Read(transform.Scale.X);
if (data.SyncMode & SyncModes::ScaleZ)
stream->Read(transform.Scale.X);
}
if ((data.SyncMode & SyncModes::Rotation) == (int)SyncModes::Rotation)
{
Float3 rotation;
stream->Read(rotation);
transform.Orientation = Quaternion::Euler(rotation);
}
else if (data.SyncMode & SyncModes::Rotation)
{
Float3 rotation = transform.Orientation.GetEuler();
if (data.SyncMode & SyncModes::RotationX)
stream->Read(rotation.X);
if (data.SyncMode & SyncModes::RotationY)
stream->Read(rotation.Y);
if (data.SyncMode & SyncModes::RotationZ)
stream->Read(rotation.Z);
transform.Orientation = Quaternion::Euler(rotation);
}
}
// Set transform
if (auto* parent = GetParent())
{
if (data.LocalSpace)
parent->SetLocalTransform(transform);
else
parent->SetTransform(transform);
}
}

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Scripting/Script.h"
#include "Engine/Networking/INetworkSerializable.h"
/// <summary>
/// Actor script component that synchronizes the Transform over the network.
/// </summary>
API_CLASS(Namespace="FlaxEngine.Networking") class FLAXENGINE_API NetworkTransform : public Script, public INetworkSerializable
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE(NetworkTransform);
/// <summary>
/// Actor transform synchronization modes (flags).
/// </summary>
API_ENUM(Attributes="Flags") enum class SyncModes
{
// No sync.
None = 0,
// Position X component.
PositionX = 1 << 0,
// Position Y component.
PositionY = 1 << 1,
// Position Z component.
PositionZ = 1 << 2,
// Position XYZ components (full).
Position = PositionX | PositionY | PositionZ,
// Scale X component.
ScaleX = 1 << 3,
// Scale Y component.
ScaleY = 1 << 4,
// Scale Z component.
ScaleZ = 1 << 5,
// Scale XYZ components (full).
Scale = ScaleX | ScaleY | ScaleZ,
// Position X component.
RotationX = 1 << 6,
// Position Y component.
RotationY = 1 << 7,
// Position Z component.
RotationZ = 1 << 8,
// Rotation XYZ components (full).
Rotation = RotationX | RotationY | RotationZ,
// All components fully synchronized.
All = Position | Scale | Rotation,
};
public:
/// <summary>
/// If checked, actor transform will be synchronized in local space of the parent actor (otherwise in world space).
/// </summary>
API_FIELD(Attributes="EditorOrder(10)")
bool LocalSpace = false;
/// <summary>
/// Actor transform synchronization mode (flags).
/// </summary>
API_FIELD(Attributes="EditorOrder(20)")
SyncModes SyncMode = SyncModes::All;
public:
// [Script]
void OnEnable() override;
void OnDisable() override;
// [INetworkSerializable]
void Serialize(NetworkStream* stream) override;
void Deserialize(NetworkStream* stream) override;
};
DECLARE_ENUM_OPERATORS(NetworkTransform::SyncModes);