«2D/3D Paint» - a universal solution for Unity that allows to paint on 2D and 3D objects!
You can also create a modern paint app with incredible features and outstanding performance!
You need to add a prefab, one component, and configure a few parameters to use an asset!
With «2D/3D Paint» you will be able to paint on 2D and 3D components such as
MeshRenderer,
SkinnedMeshRenderer,
SpriteRenderer and
RawImage.
Asset supports Standard, URP, and
HDRP pipelines, works with Input Manager (Old)
and Input System Package (New).
It also works with custom shaders and with VR. No need to add any colliders for painting.
For the correct work, «2D/3D Paint» requires:
First, back up your project.
Next, update the asset to the latest from Asset Store using Package Manager (Windows -> Package
Manager).
The asset has a comparison list of APIs that were changed from the last release. To check it, open the v.3.0_API_Changes.pdf file.
If you have any issues or errors, contact me. I'll be happy to help!
Add a prefab to the scene from path Assets/XDPaint/Prefabs/[XDPaintContainer].prefab. This prefab contains singleton components, let’s look at the parameters of the PaintController component:
«2D/3D Paint» clones the source material and replaces the source texture with the RenderTexture. The asset passes input data from InputController to BasePaintObject to calculate UV coordinates to determine the position of the painting on the texture. When the user interacts with the input, for example, by moving a finger on the touch device, the asset gets the UV position of the previous frame and position of the UV in the current frame. Using two UV-positions, the asset draws brush N-times on the layer texture. The painting takes place on the previously created RenderTexture (Layer) and is stored in GPU memory, which provides high performance.
The asset has two paint modes: Default and Additive. Default mode bakes draw results into Paint Texture each frame, Additive mode provides more accurate color and alpha blending, bakes draw results in Input Texture and bakes it into Layer Texture on Mouse Up event.
«2D/3D Paint» creates a RenderTexture (ARGB32 format) for each Layer. The size of the RenderTexture is
equal to the size of the source texture. If there is no source texture, the size for RenderTexture will
be taken from the PaintManager.Material settings (DefaultTextureWidth and
DefaultTextureHeight fields). For objects such as MeshRenderer
and SkinnedMeshRenderer, the asset uses
ray-surface intersection to determine the intersection of the model triangle with the ray. The use of
models with many vertices can lead to performance loss. Finding and using neighboring triangles for raycasts was implemented to solve this issue.
«2D/3D Paint» creates RenderTexture for each PaintManager:
Active Layer Texture and Active Layer Temp Texture are used for painting,
Input Texture blends into Active Layer Texture /
Active Layer Temp Texture.
Combined Texture / Combined Temp Texture - represents the current drawing results and preview of the brush.
Depending on the selected tool, the count of RenderTextures may vary.
A brush is a texture that renders into Active Layer Texture using brush parameters. When a user changes brush
parameters like Texture, Opacity, Color, or
Hardness, the brush invokes the Render method that renders the brush into RenderTexture.
Brush size value means that brush source texture will be scaled by that value. If you have a brush texture of 100x100px and brush size 0.5, your brush texture will be 50x50px.
Displaying brush size on an image depends on brush texture size, brush size value and source image size. When a user draws a dot with the brush of 100x100px with a brush scale of 1 on a layer of 1000x1000px,
it will fit 10% of the layer width/height.
«2D/3D Paint» has undoing/redoing functionality that stores layers data changes. It can store layer texture changes (save every OnMouseUp event) and layer parameters changes such as name, index, opacity, etc (optional). Note that after layer texture change (paintning on the layer), its state will be saved as texture and consume memory. For example, using a 1024x1024 texture will take 4MB (32 bytes per pixel). Undo/redo functionality can be turned off or limited using States Settings.
«2D/3D Paint» processes input using the InputController class. Asset supports Input Manager (Old) and Input System Package (New). Input can work with the mouse, touch device, pen (tablet stylus like Apple Pen) or VR controller.
For MeshRenderer
and SkinnedMeshRenderer objects, finding the intersection of the ray with the
triangle is used to calculate the UV coordinates. These calculations are performed on the CPU, the time
of which depends on the number of vertices of the model. To avoid high CPU loads, it should be
noted that the more vertices the model contains, the more CPU time it will take to find the
intersection.
To optimize the calculations and drawing lines, a method was added based on the data on neighboring triangles.
In order to draw a line from triangle «A» to triangle «B» the asset uses neighbor's triangles data to find
the entry and exit positions of the triangles. This method significantly improves performance for
objects with many vertices, but can draw a line with inaccuracy for non-convex objects,
because it does not check data about other triangles that may lie «closer» to the camera and close the
verifiable (neighboring) vertices.
Tool - is an instrument for processing layer texture when the user paints on it. The asset has 8 built-in tools: brush, erase, bucket, eyedropper, brush sampler, clone, blur, gaussian blur, and grayscale tool. User can switch between tools to get different drawing results. Let’s look at tools functional:
The asset has a preset system that stores brush parameters in ScriptableObject,
which allows them to be reused with PaintManagers/PaintController. Presets located in the file at: Assets/XDPaint/Resources/XDPaintBrushPresets.asset.
You can save brush parameters by name and apply them to other objects from the list. If you change
the non-custom parameters of brush in PaintManager/PaintController inspector window, data won’t be written
to Presets automatically.
Asset have built-in presets that can be reused or modified. Presets can be re-saved using
PaintController/PaintManager Inspector window or changed by selecting XDPaintBrushPresets asset
in Project window and editing in Inspector window.
Preset can be re-saved to a new preset, for this click on
Preset can be renamed, for this click on button and choose a new name and click on
button.
Preset can be removed, for this click on button and confirm it using the dialogue box.
You can use one brush parameters for all PaintManagers, for that check Use Shared Settings in
PaintController, otherwise, each PaintManager will have unique brush parameters.
To add a new brush, select [XDPaintContainer] GameObject in Hierarchy tab, then in Inspector tab set a unique brush name,
select brush texture, and brush parameters, and save it as preset using button. It also works the same way in
PaintManager component.
A new brush will be added to Brush Presets (XDPaintBrushPresets.asset) and can be chosen in
PaintController/PaintManager components.
Layers Data (layers textures and their parameters) can be stored in ScriptableObject which is called LayersContainer.
LayersContainer contains all data about layers that PaintManager has: layers textures, names, opacity, blending options, masks data, and layers order.
To save layers data to LayersContainer, you can use button in PaintManager component:
After choosing the path, layers data will be saved to LayersContainer asset, layers textures will be saved to the asset folder automatically. LayersContainer asset has an array of LayerData that contains all layers parameters (right part of the image).
If PaintManager has a reference to LayersContainer asset in LayersContainer field, during initialization, it will load layers from the asset:
Asset supports layers masks. Applying masks to a layer is a reversible way to hide part of a layer.
This method allows for more editing flexibility instead of permanently erasing or deleting part of a layer.
Mask in asset represents RenderTexture in R8
format and can be created/set from code or by setting the reference to the
PaintManager component has buttons to handle layers:
Class singleton that stores all PaintManagers. Use shared settings for all PaintManagers when the flag Use Shared Settings is checked.
Otherwise, each PaintManager will use its own settings.
Main public fields, properties, and methods:
public bool OverrideCamera { … }
- whether to override Camera that will be used for raycasts;
public Camera Camera { … }
- reference to the camera;
public bool UseSharedSettings
- whether to use settings of PaintController (Brush,
PaintMode and Tool) for all PaintManagers;
public PaintMode PaintMode { … }
- paint mode;
public PaintTool Tool { … }
- current tool;
public Brush Brush { … }
- return brush instance;
public void RegisterPaintManager(PaintManager paintManager)
- register PaintManager;
public void UnRegisterPaintManager(PaintManager paintManager)
- unregister PaintManager;
public IPaintMode GetPaintMode(PaintMode mode)
- return instance of paint mode;
public PaintManager[] ActivePaintManagers()
- return active PaintManagers;
public PaintManager[] AllPaintManagers()
- return all registered PaintManagers.
Class singleton is a component for user input management.
Main public fields, properties, and methods:
public bool IsVRMode
- whether to use VR mode instead of Mouse / Touch input;
public Transform PenTransform
- Transform of Pen/Hand for painting, used for VR device;
public int MaxTouchesCount
- maximum number of touches for simultaneous drawing;
public Canvas Canvas
- Canvas to ignore raycasts (children objects of Canvas will prevent painting);
public GameObject[] IgnoreForRaycasts
- GameObjects to ignore raycasts (children objects of Canvas that won't prevent painting);
public event Action OnUpdate
- on input update;
public event Action<int, Vector3> OnMouseHover
- mouse hover event on objects;
public event Action<int, Vector3, float> OnMouseDown
- left mouse click event on objects;
public event Action<int, Vector3, float> OnMouseButton
- left mouse button pressing event on objects;
public event Action<int, Vector3> OnMouseUp
- event of releasing the left key of the mouse.
Class singleton is a controller for data checks of ray intersections with the triangles and the
keeper of the data about all available objects to make a raycast checks.
Main public fields, properties, and methods:
public bool UseDepthTexture
- whether to use Depth Texture for checking raycasts.
This is useful in cases when more than one PaintManager is active on the scene, because in this case, it will paint on the nearest PaintManager to the ray origin. Note that if you use SRP, you need to enable DepthTexture in the
URP/HDRP settings;
public void InitObject(IPaintManager paintManager, Component paintComponent, Component renderComponent)
- initializes a new mesh object;
public void DestroyMeshData(IPaintManager paintManager)
- destroys previously created mesh
data for raycasts;
public Triangle Raycast(IPaintManager sender, Ray ray, Vector3 screenPosition)
- check the intersection of the ray
with the triangles objects, returns Triangle of raycast;
public Triangle RaycastLocal(IPaintManager paintManager, Ray ray)
-
check the intersection of the ray with the triangles of the Transform objectTransform, returns Triangle of raycast;
public Triangle NeighborsRaycast(IPaintManager sender, Triangle triangle, Ray ray)
- check
the intersection of the ray with the neighboring triangles of Triangle triangle, returns Triangle of raycast.
The manager for the paint object. It combines the main component for painting on objects,
containing instances of Paint, BasePaintObject.
Main events, fields, properties and methods:
public event Action<PaintManager> OnInitialized
- event of initialization finish, returns
PaintManager instance;
public event Action OnDisposed
- event of the resources disposed of PaintManager;
public GameObject ObjectForPainting
- GameObject of the object to be painted;
public FilterMode FilterMode { ... }
- property of the FilterMode of RenderTextures;
public Brush Brush { ... }
- property of the brush of PaintManager;
public ToolsManager ToolsManager { ... }
- property of the ToolsManager, contains and manages all
the tools for painting;
public PaintTool Tool { ... }
- property of the current paint tool;
public BasePaintObject PaintObject { … }
- property of the painted object;
public IStatesController StatesController { … }
- StatesController instance;
public bool UseSourceTextureAsBackground { … }
- property of using source texture as
background texture for the resulting image;
public bool UseNeighborsVerticesForRaycasts { … }
- property of using neighbors vertices
for raycasts when drawing lines;
public bool HasTrianglesData { … }
- whether component contains any triangles data;
public Triangles[] Triangles { … }
- get/set triangles array;
public bool Initialized { … }
- property of the initialization status of the object;
public int SubMesh { … }
- property of the sub mesh
of the PaintObject mesh;
public int UVChannel { … }
- property of the UV-channel
of the mesh;
public void Init()
- initialize PaintManager. If PaintManger was initialized before, it
will re-create its internal data: RenderTextures, Meshes and Materials;
public void DoDispose()
- destroy all PaintManager created RenderTextures, Meshes and
Materials, restores source material and texture;
public void Render()
- invoke object rendering;
public void SetPaintMode(PaintMode paintMode)
- set paint mode;
public IPaintMode PaintMode
- return paint mode instance;
public void FillTrianglesData(bool fillNeighbors = true)
- fill model data, argument - to
fill data about neighbors triangles;
public void ClearTrianglesData()
- remove filled information about triangles;
public void ClearTrianglesNeighborsData()
- remove filled information about the
neighboring triangles;
public void SetTrianglesContainer(TrianglesContainer trianglesContainerData)
- set triangles data
directly from code using TrianglesContainer ScriptableObject;
public RenderTexture GetPaintTexture()
- return RenderTexture of combined texture of painting;
public RenderTexture GetPaintInputTexture()
- return RenderTexture of input (current frame
dot/line for Default PaintMode or texture that was drawn between mouse down and mouse up events for
Additive PaintMode);
public RenderTexture GetResultRenderTexture()
- return result RenderTexture (combined layers);
public Texture2D GetResultTexture(bool hideBrushPreview = false)
- return the resulting texture (combined layers), argument - whether to hide brush preview.
Note that it should be destroyed as texture creation is definitely a possibility for memory leaks in Unity if you're not disposing of the old ones properly;
public SetLayersData (LayersContainer container)
- set layers data;
public LayerData[] GetLayersData()
- return current layers data;
public void InitBrush()
- initialize brush settings.
Base class for painting on RenderTexture. It can be declared as CanvasRendererPaint,
MeshRendererPaint, or SpriteRendererPaint. The derived classes CanvasRendererPaint, MeshRendererPaint,
and SpriteRendererPaint contain logic to check the painting position based on the data from the
InputController and return the UV texture position for further work according to the base class
logic.
Main public fields, properties, and methods:
public event Action<PointerData> OnPointerHover
- mouse hover event. Arguments: PointerData that contains paint object local position, screen position, UV position, texture position, pressure, finger id;
public event Action<PointerData> OnPointerDown
- mouse down event. Arguments: PointerData that contains paint object local position, screen position, UV position, texture position, pressure, finger id;
public event Action<PointerData> OnPointerPress
- mouse press event. Arguments: PointerData that contains paint object local position, screen position, UV position, texture position, pressure, finger id;
public event Action<PointerUpData> OnPointerUp
- mouse up event. Arguments: PointerUpData that contains screen position, is mouse in object bounds, finger id;
public event Action<DrawPointData> OnDrawPoint
- draw point event, can be used by the developer to obtain data about painting. Arguments: DrawPointData that contains texture position, pressure, finger id;
public event Action<DrawLineData> OnDrawLine
- draw line event, can be used by the developer to obtain data about painting. Arguments: DrawLineData that contains line start texture position, line end texture position, line start pressure, line end pressure, finger id;
public bool IsPainting { … }
- property, whether user is painting;
public bool IsPainted { … }
- property, whether user is painting (in current frame vertices were drawn);
public bool ProcessInput
- whether input processing for current paint object;
public new Camera Camera { … }
- camera property used to determine the position of the painting on the texture;
public void Init(Camera camera, Transform objectTransform, Paint paint, IRenderTextureHelper renderTextureHelper, IStatesController statesController)
- initialization of the object;
public void DoDispose()
- destroy previously created RenderTextures and Meshes;
public void OnMouseHover(int fingerId, Vector3 position, Triangle triangle = null)
- on mouse hover method;
public void OnMouseDown(int fingerId, Vector3 position, float pressure = 1f, Triangle triangle = null)
- on mouse down method;
public void OnMouseButton(int fingerId, Vector3 position, float pressure = 1f, Triangle triangle = null)
- on mouse button method;
public Vector2? GetPaintPosition(int fingerId, Vector3 position, Triangle triangle = null)
- return paint position from screen position if ray intersects an object;
public void OnMouseUp(int fingerId, Vector3 position)
- on mouse up method;
public void DrawPoint(DrawPointData drawPointData)
- draw point from code using DrawPointData;
public void DrawPoint(Vector2 position, float pressure = 1f, int fingerId = 0)
- draw point from code using texture position;
public void DrawLine(DrawLineData drawLineData)
- draw line from code using DrawLineData;
public void DrawLine(Vector2 positionStart, Vector2 positionEnd, float pressureStart = 1f, float pressureEnd = 1f, int fingerId = 0)
- draw line from code using texture positions;
public void FinishPainting(int fingerId = 0)
- force end painting;
public void OnRender()
- render to combined RenderTexture;
public void Render()
- combine all RenderTextures to Combined RenderTexture;
public void ClearTexture(bool writeToUndo = false)
- clear the texture to paint, to display changed data call paintManager.Render() method;
public void RenderToTextureWithoutPreview(RenderTexture resultTexture)
- render to Combined Render Texture without brush preview.
A class that manages states of undo/redo. Contains and manages changed data, the previous object states.
Main public fields, properties, and methods:
public int ChangesCount { ... }
- changes count;
public event Action<RenderTexture> OnClearTextureAction
- event for RenderTexture clear action;
public event Action OnRenderTextureAction
- event for RenderTexture undo/redo, invokes object render;
public event Action OnChangeState
- event to undo/redo;
public event Action OnResetState
- event to undo, used to clear Input Texture;
public event Action OnUndo
- event to undo action performed;
public event Action OnRedo
- event to redo action performed;
public event Action<bool> OnUndoStatusChanged
- event to undo status change, returns flag that user can perform undo action;
public event Action<bool> OnRedoStatusChanged
- event to redo status change, returns flag that user can perform redo action;
public void DoDispose()
- destroy state data;
public void Enable()
- enable undo/redo functionality;
public void Disable()
- disable undo/redo functionality;
public void AddState(Action action)
- add action as a state;
public void AddState(object entity, string property, RenderTexture oldValue, RenderTexture newValue, Texture source)
- add RenderTexture changes states;
public void AddState(object entity, string property, object oldValue, object newValue)
- add property change state;
public void AddState(IList collection, NotifyCollectionChangedEventArgs rawEventArg)
- add list change state;
public void Undo()
- undo action;
public void Redo()
- redo action;
public int GetUndoActionsCount()
- return count of undo actions;
public int GetRedoActionsCount()
- return count of redo actions;
public bool CanUndo()
- return if user can undo action;
public bool CanRedo()
- return if user can redo action;
public void EnableGrouping()
- enable grouping of the actions. All actions will be recorded as one action until DisableGrouping() is invoked;
public void DisableGrouping()
- disable grouping of the actions.
A class that contains layers instances and manages them.
Main public fields, properties, and methods:
public event Action<ObservableCollection<ILayer>, NotifyCollectionChangedEventArgs> OnLayersCollectionChanged
- event on layers collection changed;
public event Action<ILayer> OnLayerChanged
- event for layer OnRenderPropertyChanged action, used for Render object after layer parameters changed;
public event Action<ILayer> OnActiveLayerSwitched
- event to change active layer index;
public event Action<bool> OnCanRemoveLayer
- event to get flag is layer can be removed, as asset requires at least one layer for painting;
public ReadOnlyCollection<ILayer> Layers { ... }
- layers collection;
public ILayer ActiveLayer { ... }
- instance of active layer;
public bool CanDisableLayer { ... }
- can disable layer or not, used to prevent disabling all layers;
public bool CanRemoveLayer { ... }
- can remove the active layer, used to prevent the removal of the last layer;
public bool CanMergeLayers { ... }
- can merge layers. Check if the active layer and layer below are enabled;
public bool CanMergeAllLayers { ... }
- can merge all layers. Check if the active layer and at least one different layer is enabled;
public int ActiveLayerIndex { ... }
- active layer index;
public void DoDispose()
- release all layers data;
public void Init(Texture sourceTexture)
- initialize layers;
public void CreateBaseLayers(Texture sourceTexture, bool useSourceTextureAsBackground)
- create default layers using source texture;
public void SetFilterMode(FilterMode mode)
- set filter mode for layers textures;
public ILayer AddNewLayer()
- create a new layer;
public ILayer AddNewLayer(string name)
- create a new layer with name;
public ILayer AddNewLayer(string name, Texture sourceTexture)
- create a new layer with the name from the source texture;
public void AddLayerMask(ILayer layer, Texture source)
- create a layer mask using source texture;
public void AddLayerMask(ILayer layer)
- create a layer mask;
public void AddLayerMask(Texture source)
- create a layer mask for the active layer using the source texture;
public void AddLayerMask()
- create a layer mask for the active layer;
public void RemoveActiveLayerMask()
- remove active layer mask;
public ILayer GetActiveLayer()
- return active layer;
public void SetActiveLayer(ILayer layer)
- set layer as active;
public void SetActiveLayer(int index)
- set active layer by layer index;
public void SetLayerOrder(ILayer layer, int index)
- set layer order;
public void RemoveActiveLayer()
- remove active layer;
public void RemoveLayer(ILayer layer)
- remove layer;
public void RemoveLayer(int index)
- remove layer by index;
public void MergeLayers()
- merge the active layer with the layer below into the layer below. Invokes when the active layer and layer below are enabled;
public void MergeAllLayers()
- merge all enabled layers into an active layer. Invokes when at least 2 layers are enabled;
public void SetLayerTexture(int index, Texture texture)
- set layer texture.
public Texture2D GetActiveLayerTexture()
- return Texture2D of the active layer. Note that it should be destroyed as texture creation is definitely a possibility for memory leaks in Unity if you're not disposing of the old ones properly;
public Texture2D GetLayerTexture(int layerIndex)
- return Texture2D of the layer by layer index. Note that it should be destroyed as texture creation is definitely a possibility for memory leaks in Unity if you're not disposing of the old ones properly;
A class that contains and manages layer data.
Main public fields, properties, and methods:
public event Action<ILayer> OnLayerChanged
- event for layer properties change;
public Action<Layer> OnRenderPropertyChanged
- event for layer properties change. Invokes when any of the properties that affect the rendering layer are changed;
public bool Enabled { ... }
- enable flag of layer;
public bool CanBeDisabled { ... }
- return flag is layer can be disabled;
public bool MaskEnabled { ... }
- enable flag for mask;
public string Name { ... }
- layer name;
public float Opacity { ... }
- opacity value of the layer;
public Texture SourceTexture { ... }
- layer source texture;
public RenderTexture RenderTexture { ... }
- layer render texture;
public RenderTargetIdentifier RenderTarget { ... }
- render texture identifier;
public Texture MaskSourceTexture { ... }
- mask source texture;
public RenderTexture MaskRenderTexture { ... }
- mask render texture;
public RenderTargetIdentifier MaskRenderTarget { ... }
- mask render texture identifier;
public BlendingMode BlendingMode { ... }
- layer blending mode;
public void Create(string layerName, int width, int height, RenderTextureFormat format, FilterMode filterMode)
- create layer render texture;
public void Create(string layerName, Texture source, RenderTextureFormat format, FilterMode filterMode)
- create layer render texture;
public void Init(CommandBufferBuilder bufferBuilder, Func<bool> canDisableLayer)
- initialize layer;
public void AddMask(RenderTextureFormat format)
- create layer mask;
public void AddMask(Texture maskTexture, RenderTextureFormat format)
- create a layer mask based on texture;
public void RemoveMask()
- remove layer mask;
public void SaveState()
- save layer render texture using StatesController;
public void DoDispose()
- release layer data.
A class that stores and manages painting material data and its parameters.
Main public fields, properties, and methods:
public Material Material { … }
- paint material;
public string ShaderTextureName { … }
- shader texture name for painting;
public int DefaultTextureWidth { … }
- texture width, in case the object doesn't have a source texture;
public int DefaultTextureHeight { … }
- texture height, in case the object doesn't have a source texture;
public int MaterialIndex { … }
- index of material in the object;
public Texture SourceTexture { … }
- source texture;
public void Init(IRenderComponentsHelper renderComponentsHelper)
- paint material initialization;
public void DoDispose()
- destroy previously created materials;
public void RestoreTexture()
- set source texture to material;
public void SetObjectMaterialTexture(Texture texture)
- set new texture of the object;
public void SetPreviewTexture(Texture texture)
- set preview texture;
public void SetPaintTexture(Texture texture)
- set Layer texture to material;
public void SetInputTexture(Texture texture)
- set Input texture to material;
public void SetPaintPreviewVector(Vector4 brushOffset)
- set data to display brush preview.
A class that stores and manages brush material data and its parameters.
Main public fields, properties, and methods:
public string Name { … }
- brush name, must be unique;
public Material Material { … }
- brush material;
public FilterMode FilterMode { … }
- FilterMode of the RenderTexture of the brush;
public Color Color { … }
- brush color;
public Texture SourceTexture { … }
- source texture of the brush;
public RenderTexture RenderTexture { … }
- render texture of the brush;
public float MinSize { … }
- minimal size of the brush;
public float Size { … }
- brush size;
public float RenderAngle { … }
- brush render angle in degrees;
public Quaternion RenderQuaternion { … }
- brush render quaternion;
public float Hardness { … }
- brush hardness;
public bool Preview { … }
- brush preview;
public event Action<Color> OnColorChanged;
- event of changing the brush color;
public event Action<Texture> OnTextureChanged;
- event of changing the texture of the brush;
public event Action<bool> OnPreviewChanged;
- event of changing preview flag of the brush;
public void Init(IPaintMode mode)
- initialize the brush Material, Mesh and RenderTexture;
public void DoDispose()
- destroy previously created RenderTexture, Mesh and Material;
public void SetValues(Brush brush)
- set values for the brush from the other brush;
public void Render()
- render brush into RenderTexture;
public void RenderFromTexture(Texture texture)
- render brush from texture without changing SourceTexture;
public void SetColor(Color colorValue, bool render = true, bool sendToEvent = true)
- set the brush color;
public void SetTexture(Texture texture, bool render = true, bool sendToEvent = true, bool canUpdateRenderTexture = true)
- set the brush texture;
public void SetPaintTool(PaintTool paintTool)
- set current tools for changing shader params;
public void SetPaintMode(IPaintMode mode)
- set paint mode.
A class that contains and manages all tools.
Main public fields, properties, and methods:
public IPaintTool CurrentTool
- return current tool;
public void Init(PaintManager paintManager)
- initialize for PaintManager, subscribe for
PaintManager events;
public void DoDispose()
- release tools resources;
public void SetTool(PaintTool paintTool)
- set tool.
Base class for every tool. Handles the layer texture using input events from PaintManager.
Main public fields, properties, and methods:
public virtual PaintTool Type { … }
- tool type;
protected IPaintData Data
- data that the tool has to process an image. Used to get information about layers, RenderTextures, Brush, painting states;
public virtual bool ShowPreview { … }
- whether to show preview or not;
public virtual bool RenderToLayer { … }
- whether to render to Layer Texture result;
public virtual bool RenderToInput { … }
- whether to render to Input Texture result;
public virtual bool AllowRender { … }
- whether to allow any render or not;
public virtual bool CanDrawLines { … }
- whether to draw lines or not. If this flag is disabled, the user can only draw dots;
public virtual bool ConsiderPreviousPosition { … }
- whether to render brush if previous paint position wasn't changed;
public virtual bool RenderToTextures { … }
- whether to render to any RenderTextures;
public virtual bool DrawPreProcess { … }
- whether to draw pre process data or not;
public virtual bool DrawProcess { … }
- whether to draw process data or not;
public virtual bool BakeInputToPaint { … }
- whether to bake Input texture to Layer texture or not;
public virtual bool ProcessingFinished { … }
- returns if tool finished processing;
public virtual bool RequiredCombinedTempTexture { … }
- returns if tool requires Combined Temp Texture;
public virtual bool IsLayersProcessing { … }
- returns if layers are processing by LayersController or StatesController;
public BasePaintToolSettings BaseSettings
- reference to the base class settings of the tool;
public T Settings
- reference to the settings class of the tool;
public void FillWithColor(Color color)
- fill active layer with color;
public virtual void OnDrawPreProcess(RenderTargetIdentifier combined)
- draw a preprocess method for changing texture before layers are combined into Combined texture;
public virtual void OnDrawProcess(RenderTargetIdentifier combined)
- draw method for combining layers into Combined texture.
Base class for every tool settings, each tool can have derived class.
Main public properties:
public bool CanPaintLines
- whether to draw lines. If this flag is disabled, the user can only draw dots;
public bool DrawOnBrushMoveOnly
- whether to render brush if previous paint position wasn't changed;
Tools parameters can be displayed in the Inspector tab if they have [PaintToolSettings] attribute:
A component to get an average color of texture.
Main public fields and methods:
public PaintManager PaintManager
- PaintManager to get the average color;
public PaintRenderTexture PaintRenderTexture
- texture to check average color;
public bool SkipTransparentPixels
- whether to skip source texture transparent pixels;
public event Action<Color> OnGetAverageColor
- event to get the average color. Invokes when the
user paints;
public void SetAccuracy(int accuracy)
- set sampling accuracy.
AverageColorCalculator gets an average color using the shader «XD Paint/Average Color» that samples
texture. There are two parameters: main texture and accuracy - divider to get samples count. Smaller
quantity means better accuracy, and as a result - more GPU resources will be used. As an example, if
texture has size 2048x1024 and accuracy has a value of 64, it will sample texture 2048 / 64 = 32 times per
horizontal x 1024 / 64 = 16 times per vertical, in sum 32 x 16 = 512 samples. All of this works with GPU,
which provides the best performance.
A component to paint on PaintObject using collisions.
This component gets UV-position on OnCollisionEnter and OnCollisionStay and paints on the object using the brush.
To use this component, add it to an object that will collide with the PaintObject.
Main public fields and methods:
public event Action<PaintManager>, Collision> OnCollide
- event to get painting information, first argument -
PaintManager for painting, second - Collision data;
public Color Color
- color of painting;
public float Pressure
- ratio to scale brush size on painting;
Check if you can undo/redo:
paintManager.StatesController.CanUndo();
paintManager.StatesController.CanRedo();
Subscribe to change state events:
paintManager.StatesController.OnUndoStatusChanged += OnUndoStatusChanged;
paintManager.StatesController.OnRedoStatusChanged += OnRedoStatusChanged;
private void OnUndoStatusChanged(bool canUndo)
{
Debug.Log("Can undo: " + canUndo);
}
private void OnRedoStatusChanged(bool canRedo)
{
Debug.Log("Can redo: " + canRedo);
}
Undo/Redo action:
paintManager.StatesController.Undo();
paintManager.StatesController.Redo();
Remove undo/redo states:
paintManager.StatesController.DoDispose();
Render (repaint) object:
paintManager.Render();
Set default texture size for objects without source texture:
paintManager.Material.DefaultTextureWidth = …;
paintManager.Material.DefaultTextureHeight = …;
Fill layer texture with a color (in this case, the layer texture will be cleared):
paintManager.Tool = PaintTool.Brush;
paintManager.ToolsManager.CurrentTool.FillWithColor(Color.clear);
paintManager.Render();
Set brush size:
paintManager.Brush.Size = value;
Set brush hardness:
paintManager.Brush.Hardness = value;
Change brush color:
var brushColor = paintManager.Brush.Color;
brushColor = new Color(color.r, color.g, color.b, brushColor.a);
paintManager.Brush.SetColor(brushColor);
Set brush opacity:
var color = paintManager.Brush.Color;
color.a = value;
paintManager.Brush.SetColor(color);
Set brush angle:
paintManager.Brush.RenderAngle = value;
Change tool:
if (PaintController.Instance.UseSharedSettings)
{
//in case when PaintController has checked UseSharedSettings
PaintController.Instance.Tool = PaintTool...;
}
else
{
//otherwise:
paintManager.Tool = PaintTool...;
}
Set blur tool parameters:
if (paintManager.ToolsManager.CurrentTool is BlurTool blurTool)
{
blurTool.Settings.Iterations = 3;
blurTool.Settings.BlurStrength = 2f;
blurTool.Settings.DownscaleRatio = 1;
}
Set gaussian blur tool parameters:
if (paintManager.ToolsManager.CurrentTool is GaussianBlurTool gaussianBlurTool)
{
gaussianBlurTool.Settings.KernelSize = 3;
gaussianBlurTool.Settings.Spread = 5f;
}
Enable/Disable input for all PaintManagers:
InputController.Instance.enabled = value;
Enable/Disable input for PaintManager instance:
paintManager.PaintObject.ProcessInput = value;
Getting the average color of PaintManager:
public AverageColorCalculator AverageColorCalculator;
…
AverageColorCalculator.OnGetAverageColor += color => Debug.Log("Average Color: " + color);
Save painting result to file:
var path = … ; //path for texture
var texture2D = paintManager.GetResultTexture();
var pngData = texture2D.EncodeToPNG();
if (pngData != null)
{
File.WriteAllBytes(path, pngData);
}
Destroy(texture2D);
In case you need to create a layer from the existing texture:
var layerName = "SomeLayerName"; var layerTexture = ... //reference to Texture2D, note that the Texture dimensions must be as the source texture paintManager.LayersController.AddNewLayer(layerName, layerTexture); Asset supports drawing from code. To draw in the texture space, invoke one of these methods:
// draw a point where argument - position on the texture
paintManager.PaintObject.DrawPoint(new Vector3(512, 512));
// invoke FinishPainting() when drawing lines are finished. FinishPainting() method also saves the result to undo/redo system.
paintManager.PaintObject.FinishPainting();
// draw a line where arguments - start and end position on the texture
paintManager.PaintObject.DrawLine(new Vector2(400, 612), new Vector2(100, 100));
// invoke FinishPainting() when drawing dots are finished. FinishPainting() method also saves the result to undo/redo system.
paintManager.PaintObject.FinishPainting();
To draw in screen space for MeshRenderer/SkinnedMeshRenderer components you need to invoke these methods:
// screen position in pixels
var screenPos = new Vector3(...);
var ray = Camera.main.ScreenPointToRay(screenPos);
RaycastController.Instance.Raycast(ray, out var triangle);
// then use methods for painting:
paintManager.PaintObject.OnMouseDown(screenPos, 1f, triangle);
paintManager.PaintObject.OnMouseButton(screenPos, 1f, triangle);
paintManager.Render();
paintManager.PaintObject.OnMouseUp(screenPos, 1f, triangle);
To draw in screen space for SpriteRenderer/RawImage
components you need to invoke methods for painting:
// screen position in pixels
var screenPos = new Vector3(…);
// then use methods for painting:
paintManager.PaintObject.OnMouseDown(screenPos);
paintManager.PaintObject.OnMouseButton(screenPos);
paintManager.Render();
paintManager.PaintObject.OnMouseUp(screenPos);
PaintManager can be created from code. Here is an example of creating
PaintManager using this method:
public void AddPaintManager(GameObject objectForPainting, Material material, string shaderTextureName = "_MainTex", TrianglesContainer triangles = null)
{
var paintManager = objectForPainting.AddComponent<PaintManager>();
paintManager.ObjectForPainting = objectForPainting;
paintManager.Material.SourceMaterial = material;
paintManager.Material.ShaderTextureName = shaderTextureName;
if (objectForPainting.GetComponent<MeshRenderer>() != null || objectForPainting.GetComponent<SkinnedMeshRenderer>() != null)
{
paintManager.Triangles = triangles;
}
paintManager.Init();
}
PaintManager can be re-initialized with one line of code. Note that afterward,
previously created resources (RenderTextures, Meshes, Materials) will be re-created:
paintManager.Init();
Change the texture for the painting using code:
public void ChangeTexture(Texture texture)
{
var material = paintManager.Material.SourceMaterial;
material.SetTexture(paintManager.Material.ShaderTextureName, texture);
paintManager.Material.SourceMaterial = material;
paintManager.Init();
}
«2D/3D Paint» supports work with VR. Asset uses Unity XR System and support for any other VR plugins can be easily implemented and require minimal code changes to properly work with VR input devices.
To enable VR support, perform the following steps:
Here are some tips that will help in its configuration and use «2D/3D Paint»: