This document is about: FUSION 2
SWITCH TO

Texture Drawing

Fusion Industries prototyping Addons

This addon provides a solution to synchronize a texture modification.

texture drawing example

Overview

The logic of this add-on is to share drawing points details (position, color, .. see DrawinPoint) between users.

A sample pen is included, drawing point when detecting drawable blocking surfaces (see the BlockingContact addon).

texture drawing overview

State authority

The logic is split between pens, and drawing surfaces, both having network behaviours, and potentially different state authority between a pen and its target drawing:

  • the pen state authority is the grabber of the pen
  • the drawing surface state authority might not be the drawing user, as several users might be drawing at the same time on the surface.

Drawing steps

texture drawing overview

When a new drawing part is added by a pen, first:

  1. the pen detect that a drawing point that should be added to a drawing surface (see TexturePen), and prepare its info (coordinates, ...).
  2. the pen draws a temporary line on the texture, for immediate display (the drawing surface will actually redraw it again once the network communication has occured))
  3. the latest points to draw for a pen are stored in a networked ring buffer on it (see TextureDrawer), so that all users can be aware of its will to add points on the drawing

Then, the drawing surface, on all clients:

  1. detects that a pen has new points for it,
  2. draws them on the actual texture (see TextureSurface).
  3. store these new drawing point in its local cache. See TextureDrawing

Texture drawing

The addon provides two ways to edit a texture:

  • the default solution uses a specific drawing shader, made with Shader Graph, and so only compatible with URP and HDRP render pipelines. This solution is more suitable for high resolution textures.
  • the alternative solution edits the texture using the ProtoTurtle.BitmapDrawing third party solution, which provides a bitmap drawing API.

Shader-based drawing

The LinePainter shader can draw a line on a texture. It contains a subgraph LineSDK determining the distance from a point to the drawn line.

The drawing logic is to:

  • use a render texture in the drawing surface material
  • every time a line needs to be drawn, use Graphics.Blit to feed the shader with the current render texture and line details, and store the resulting results
texture drawing shader logic

Class details

TexturePen

The TexturePen is located on the pen (with a BlockableTip component) and tries to detect a contact with a BlockingSurface component, having also a TextureDrawing component. When a contact is detected, the local list of points to draw is updated. Then for each point, the method AddDrawingPoint() of TextureDrawer is called during the FixedUpdateNetwork().

TextureDrawer

This is subclass of the RingBufferSyncBehaviour class from DataSyncHelpers addon.

It is used to record the drawing points that must be edited on the texture when a contact is detected between the pen and the drawing surface. To do that, the AddDrawingPoint() method is called by the TexturePen to add a DrawingPoint to the list of points that should eventually be placed on the TextureSurface.

When adding a point locally, or when remote players are informed of new points thanks to the RingBufferSyncBehaviour's OnNewEntries call back, it requests TextureDrawing to add a new point.

Note:

  • filling the drawer ring buffer too quickly might lead to data loss (in the current settings, it should not occur in normal network conditions). To prevent this, either throttle the transmission of points with TexturePen.maxPointInsertionPerSeconds, or increase the RingBufferSyncBehaviour.BUFFER_SIZE

TextureDrawing

When adding a new drawing point, if a line was not yet finished for the requesting TextureDrawer, the TextureDrawing creates a line between the previous point and the new one. Several drawers can have drawings in parallel, as a TextureDrawing keeps a cache of the latest point drawn per drawer. The TextureDrawing finally calls the Draw() method on the referenced TextureSurface, to add a point or draw a line.

Late joiners

For late joiners, TextureDrawing uses the Fusion 2 streaming API, to send the complete list of points added from the start.

TextureDrawing subclasses StreamingSyncBehaviour and changes its logic.

While a StreamingSyncBehaviour is built to send data in real time, and then share them to late joiners:

  • here the DrawingPoints data are just stored in a local cache (the real time transmission has already been handled by the TextureDrawer network var)
  • for late joiners, the reception data logic is changed, to mix between the data already received previously through the network var of the TextureDrawers (usually pretty quickly), and the full data cache that takes a few frame to be received.

Merging those data requires a reconciliation, as the last point in the full cache might also have been received first through the TextureDrawer earlier (the same point might be received both through the streaming API providing the full cache backup, and through the TextureDrawer networked var provide real time data), and as those points could be associated to unfinished lines. A simple way to solve this issue is to go through all the points stored in the full cache, and add a line end point for all drawing lines that were not finished upon transmission. The TextureDrawer data being more recent, they will in any case finish any pending lines again.

texture drawing overview

Note:

  • another way to solve this would be to use the full byte count, to detect duplicate points precisely.
  • this approach might lead to different drawing on each client if people draw on the same pixel at the same time. If it is an issue, another approach would be to use a RingBufferLosslessSyncBehaviour subclass instead of a StreamingSyncBehaviour: drawer not having authority on the drawing would draw temporary lines, and upon reception of the TextureDrawing "confirmation", the actual lines would be drawn.

TextureSurface

TextureSurface references the Renderer component and contains the utility methods for textures editing: initialize the texture, change the texture color, draw a point, draw a line. So, this class is not linked to the network part.

It implements the IRenderTextureProvider interface (from the DataSyncHelpers addon). The onRedrawRequired event is raised when the texture has been edited externally, and TextureDrawing subscribes to it to redraw all the point if this happens.

DrawingPoint

This class defines the drawing points of the surface (position, color, pressure and a reference Id). This referenceId used either to store the target TextureDrawing, or the source TextureDrawer. It implements the RingBuffer.IRingBufferEntry interface of the DataSyncHelpers addon.

Dependencies

Demo

A demo scene can be found in the Assets\Photon\FusionAddons\TextureDrawing\Demo\Scenes\ folder.

Download

This addon latest version is included into the Industries addon project

It is also included into the free XR addon project

Supported topologies

  • shared mode

Third party

  • ProtoTurtle.BitmapDrawing, MIT license, https://github.com/ProtoTurtle/UnityBitmapDrawing

Changelog

  • Version 2.0.0: First release
Back to top