Line Drawing
This addon demonstrate how to use the DataSyncHelpers add-on to create 3D drawings, that later joiner can receive too.
Feature
This add-on creates 3D drawing, made of several line renderers.
Once finished, in the demo scene those drawings can be grabbed and moved.
Those drawing should be edited by only one user. For multi-user editing, please see the TextureDrawing addon add-on, which handle this kind of use cases.
The add-on provide a basic implentation, with no supposition on how a drawer will be used (grabbed, automatic drawing, ...), as well as basic drawers examples relying on grabbing (for actual "pens").
Principle
Drawing points synchronisation
A drawer (NetworkLineDrawer
) will spawn a drawing networked object, with a NetworkLineDrawing
component on it.
The drawer will then request the drawing to store additional lines and points. Those line and points are shared over the network with a ring buffer structure, allowing to store several points that should be transmitted at the same time. This data structure also allows late joiners to receive the whole drawing through Fusion streaming API.
For details on this lossless ring buffer logic, please see the RingBufferLossLessSyncBehaviour class in the DataSyncHelpers add-on
Drawing interpolation
The NetworkLineDrawing
will then ask a LineDrawing
to actually creates the line renderers and fill them.
The NetworkLineDrawing
applies the following drawing logic during Render
.
For the state authority (the drawing user), all added drawing points are drawn immediatly
For remote users, we determine the amount of points to display by interpolating the point count between the point counts in the 2 (from and to) ticks around the current interpolation time.
This is done with the NetworkBehaviour
's TryGetSnapshotsBuffers
method (See Network Buffer documentation), which provides the from and to data, as well as the current alpha progression between the 2.
C#
if (lastDrawnPoint < (drawingPoints.Count - 1))
{
if (Object.HasStateAuthority || drawPointsOnProxiesAsSoonAsAvailable) {
DrawAllPoints();
}
else
{
bool proxyDrawingAuthorized = lossRanges.Count == 0;
// Find the from and to state of the drawing data (byte array),
// find the entry count in each state (from drawing point count, to drawing point count),
// and interpolate the current max point to draw with the interpolation alpha (between from and to point count)
if (proxyDrawingAuthorized && TryGetSnapshotsBuffers(out var fromBuffer, out var toBuffer, out var alpha))
{
var reader = GetArrayReader<byte>(nameof(Data));
var toData = reader.Read(toBuffer);
var fromData = reader.Read(fromBuffer);
RingBuffer.PositionInfo fromPositionInfo = RingBuffer.PositionInfo.ExtractPositionInfo(fromData);
var fromGlobalByteIndex = fromPositionInfo.totalData - 1;
RingBuffer.PositionInfo toPositionInfo = RingBuffer.PositionInfo.ExtractPositionInfo(toData);
var toGlobalByteIndex = toPositionInfo.totalData - 1;
var fromGlobalIndex = RingBuffer.EntryIndexAtDataSourcePosition<LineDrawingPoint>(fromGlobalByteIndex);
var toGlobalIndex = RingBuffer.EntryIndexAtDataSourcePosition<LineDrawingPoint>(toGlobalByteIndex);
var currentIndex = (int)Mathf.Lerp(fromGlobalIndex, toGlobalIndex, alpha);
DrawPointsUpTo(currentIndex);
}
}
}
Finished handler
When a drawing is finished, the IsFinished
networked var synchronise it to all users.
To be able to display a grabbing handle when the drawing is finished, a NetworkLineDrawing
can have a finishedHandler
gameObject, which is activated only when the drawing is finished.
If this gameObject has a component implementing the INetworkLineDrawingListener
, it is also possible to trigger the DrawingFinished
callback, for instance to adapt the handle position and rotation to the actual finished drawing.
NetworkLineDrawer API
A NetworkLineDrawer
won't draw anything by itself, but its methods can be called by other components or sublcasses to createe the drawing.
StartDrawing
: spawns thrpugh the network runner thedrawingPrefab
prefab, which has to contain aNetworkLineDrawing
componentStopDrawing
: stop the current drawingStartLine(Color color)
: start a line of the provided color. Cannot be changed for this lineAddPoint([Color color], float pressure)
: add a point (changing the color to a new one creates a new line)StopLine()
: forces the next added point to start a new line
LineDrawingPoint data structure
A drawing point (LineDrawingPoint
) contains 2 information: its local position relatively to the drawing origin, and a pressure level (to customize the width of the line at this position).
The data structure is also used for a special case: to describe a new line start. A NEW_LINE_PRESSURE
(aka -1) pressure value describes a new line. In this case, the position Vector3
contains the RGB value of the starting line.
Dependencies
Demo
Demo scenes can be found in the Assets\Photon\FusionAddons\LineDrawing\Demo\
folder.
Please note that to test the DemoLineDrawingMeta
demo scene, the Meta packages must be installed in the project.
Alternatively, you can use the Meta XR integration project (which includes the Meta packages) to test it directly.
Download
This addon latest version is included into the free XR addon project
Supported topologies
- shared mode
Changelog
- Version 2.0.5: Compatibility with DataSyncHelper 2.0.8 (add ByteArraySize attriute usage)
- Version 2.0.4: Fix to provide haptic feedback during 3D draw
- Version 2.0.3: Allow to change inputs for NetworkGrabbableLineDrawer
- Version 2.0.2: Add check to only support MetaGrabbableLineDrawer if the Meta interaction SDK is installed
- Version 2.0.1: Add drawing interpolation
- Version 2.0.0: First release