Paint in 3D


DOCUMENTATION

Thank you for using Paint in 3D ❤️

If you haven't already, please consider writing a review. They really help me out!



How do I upgrade?

First, make sure you back up your project files.

Next, install the latest version.

If for some reason the latest updates breaks your project (e.g. errors in the console), then try deleting the root folder for this asset, and reinstalling it.

If it still doesn't work then go back to your previous version, and let me know what the errors are, so I can fix it.



Is URP/HDRP supported?

Paint in 3D supports all 3 render pipelines (Standard, URP, HDRP) in a seamless way that automatically detects your current project settings, and makes the required modifications to the scene so they look similar.


Better Shaders

This compatibility was made possible using the Better Shaders system (not required).

The Better Shaders asset generates multiple variants of each shader ahead of time, so even if you don't have this asset, you will still be able to have full render pipeline compatibility with Paint in 3D.

BetterShaders (and therefore this asset) currently supports:

Standard Rendering Pipeline from Unity 2019 on

URP7.x in Unity 2019.3LTS

URP10.x in Unity 2020.3LTS

URP12.x in Unity 2021.2

URP14.x in Unity 2022.2->2022.3LTS

HDRP7.x in Unity 2019.3LTS

HDRP10.x in Unity 2020.3LTS

HDRP12.x in Unity 2021.3LTS

HDRP14.x in Unity 2022.2->2022.3LTS

Support for newer versions will appear when it does in this asset.



Lights

Each rendering pipeline handles lighting differently. To make the scenes look consistent, each light has the P3dLight component attached to it, which overrides the light intensity for each pipeline.

If you don't like this behavior then you can set the values (for example IntensityInHDRP) to -1.



Opaque Texture (URP only)

If you want to use the SgtBlackHole feature, then you must enable the Opaque Texture setting in your pipeline settings asset.



Volume (HDRP only)

To make HDRP compatibility seamless, each demo scene contains the P3dSceneManager GameObject, which adds the Volume component in HDRP.

This volume will adjust the scene exposure, disable the visual environment, and disable fog. These are all done to make the scenes look consistent with the other rendering pipelines. If you want to adjust these settings yourself then delete this GameObject, and create your own volume.

However, for your own scenes you may want to remove this volume component and use your own exposure settings.




Are custom shaders/materials supported?

Yes, Paint in 3D is compatible with almost all custom shaders/materials.

The only requirements are that:

1 - The texture you want to paint must be sampled in your shader using the mesh UV data (e.g. uv, uv2, texcoord, texcoord2).

2 - The mesh UV data shouldn't be modified in any significant way (e.g. no tiling or offset).

3 - The mesh positions shouldn't be modified in any significant way.

Keep in mind that shaders that perform minor modifications to the position or UV data will still work, but the paint may appear slightly offset (depending on the magnitude of modification performed). Minor modifications would include parallax mapping shaders, tessellated displacement, heat distortion, etc. If you want to paint these kinds of shaders/materials and notice this offset, then I recommend adding some kind of effect to your object when it gets painted to disguise the offset (e.g. particle effects).

The reason for these requirements is that Paint in 3D applies paint to your mesh using the mesh position and UV data. Paint in 3D doesn't know how your shader is modifying this data, so any modifications the shader performs will result in a discrepancy between where the texture is painted, and where it actually maps to when rendered with your custom shader.

NOTE  By default the P3dPaintableTexture component targets the "_MainTex" texture slot, which is usually the albedo texture. Some custom shaders/materials use a different texture slot like "_BaseMap", "_Albedo", etc. You may have to experiment with different texture slots depending on the shader (you can click the texture slot dropdown to see a list of available textures).


Is VR supported?

Yes, this asset has been developed and tested in VR, and supports both multi-pass & single-pass rendering modes.

Demo scenes like VR HVLP and VR Pen show you how to implement painting using Unity's XR system. While these demo scenes don't have integrations for third party VR systems (e.g. VRTK), integrating them yourself should be easy enough, as the components are designed to work using any system that support inspector events, and thus they shouldn't require any code changes.

Most of the example components and scenes that come with Paint in 3D are designed for mouse/touch inputs. For example the P3dHitScreen component uses mouse and touch data to paint, so it will not work in VR.

To paint in VR, you can use the P3dHitBetween component, which allows you to paint between two Transform points. The Paint Between demo scene shows you how to use it, and you can apply the same approach to your VR tool. Most of the example scenes can be modified to work in VR with this change alone.

Alternatively, the P3dHitNearby component can be used, which will paint anything near the current GameObject. The Asteroid Holes demo scene shows you how this can be used.

Keep in mind that by default both of these components paint continuously with the same pressure/strength. If you want to be able to toggle painting on and off, or associate your VR trigger pressure with paint pressure, then you will have to write a simple script to connect the two together. If you want to be able to preview the painting without applying it (e.g. when not pulling the trigger), then you will have to control the Preview setting yourself.



Can I save my in-game paint to my project?

Yes, while in play mode open the Window/Paint in 3D from the top menu bar.

On the Scene tab you can see a list of paintable objects in your scene. Under each object you can see a list of its paintable materials, and under those you can see a lise of its paintable textures.

If you want to save each texture individually, then click the Export button next to the texture name, and you can choose where to save it in your project. It will then be saved there as a .png file.

If you want to save a copy of the material with all of its paintable textures, then click the Export button next to the material name, and you can choose where to save it in your project. It will then be saved as a material, and all its textures as .png files.

NOTE When saving a material with its textures, the textures will be named like this: %Material_Name%%Texture_Name%.png For example: MyCoolMaterial_MainTex.png

This can also be done in-game from code too, using the P3dPaintableTexture component's GetPngData() method, which you can then save to file, and later load back using the LoadData(byte[]) method.



Is the new InputSystem supported?

Yes, all of the non-VR comonents should work automatically in either input system.

If you're using the VR components then you must manually create a PlayerInput component, bind some VR controls to an input, and use the events to send the CallbackContext information to the P3dVrManager component.

The VR examples use the P3dInputAxis component to send (legacy) input axis data to functions like P3dVrManager.SetLeftTrigger, and you can replace these using the new system.



Getting Started (In-Game)


Step 1 - Make Your Object Paintable

Any GameObject that has a MeshFilter + MeshRenderer or SkinnedMeshRenderer can be painted.

METHOD 1 Add the P3dPaintable component to your GameObject.
METHOD 2 Click the context menu (⋮ icon at the top right) of your MeshRenderer, or SkinnedMeshRenderer component, and select the Make Paintable (Paint in 3D) option.
METHOD 3 Open the Window/Paint in 3D/Paint in 3D to the Scene tab, and click the Make Paintable button.


Step 2 - Make a Texture Paintable

Next, you need to mark which textures you want to paint. Your MeshRenderer or SkinnedMeshRenderer component has the Materials array, and each texture used by these materials can be painted.

METHOD 1 Click the Add Paintable Texture button from the P3dPaintable inspector.
METHOD 2 Add the P3dPaintableTexture component to your GameObject, alongside the P3dPaintable component.
METHOD 3 Open the Window/Paint in 3D to the Scene tab, and click the Preset button next to the material you want to paint, and select the texture preset to paint.

You should now see the P3dPaintableTexture component added to your GameObject.

Inside this component, you can click the dropdown to the right of the Slot setting, allowing you to pick from a list of materials and texture slots.

For most objects, the default 0, _MainTex setting is all you need. But if you have multiple materials or need to do advanced painting you may need to change this. For example, to paint normal maps you may need to choose 0, _BumpMap or similar, the exact texture name depends on the shader/material.

The rest of the settings should be OK at their default values for now.



Step 3 - Make Your Material Clone Itself

The P3dPaintableTexture component takes over the texture slot of the material you specify, which means all other objects using this material will see the same paint. To fix this you must make sure the material is turned into a unique clone before your paintable textures are applied.

METHOD 1 Click the Add Material Cloner button from the P3dPaintable inspector.
METHOD 2 Add the P3dMaterialCloner component to your GameObject, alongside the P3dPaintable component.
NOTE  If your P3dPaintableTexture was added using a Window / Paint in 3D preset, then it should automatically have been given a P3dMaterialCloner component.

You should now see the P3dMaterialCloner component added to your GameObject.

Finally, make sure the P3dMaterialCloner.Index setting matches your P3dPaintableTexture.Slot index. For example, if your slot is 0, _MainTex, then your Index should also be set to 0.

NOTE  The first material is index 0, the second is index 1, etc.
NOTE  You only need to clone each material once, even if you have multiple paintable textures targeting the same material.


Step 4 - Paint It!

Paint in 3D comes with many ways to paint objects.

One simple method is to make a new GameObject, and add the P3dHitScreen and P3dPaintDecal components. These components will automatically work together, allowing you to paint using the mouse (or your finger if you run it on a mobile device).

NOTE  The P3dHitScreen component uses raycasts, so your mesh must have colliders (e.g. MeshCollider) for it to know where to paint.

You can now adjust the P3dPaintDecal settings to your liking. I recommend you at least change the Color or Texture, because the default paint color is white, and if you paint on a white texture then it may look like nothing is happening!

Once done, press play. You should now be able to click on your object in the Game window, and have the decal you paint on your object under the mouse.

For more advanced ways to paint I recommend you go through the demo scenes in the PaintIn3D/Examples folder and see how they're put together!




Getting Started (In-Editor)


Step 0 [Optional] - Make a New Scene

Before you paint objects in-editor, I recommend you make a new scene (e.g. copy+paste your objects into a new scene). This is because configuring your objects for painting requires the scene to be modified (these modifications can easily be removed though).



Step 1 - Open Paint in 3D

To open the main Paint in 3D window, click on Window > Paint in 3D > Paint in 3D from the top menu bar, or select the Paint in 3D tool from the top left tool bar.



Step 2 - Make Objects Paintable

Make sure you're on the Scene tab.

Select at least one GameObject in your scene that you want to paint, and it should appear in the list.

On the right of each object you should see the Make Paintable button, which you can click to configure the GameObject for painting.

NOTE For a GameObject to be eligible for painting it must have the Skinned Mesh Renderer or MeshFilter + MeshRenderer components.


Step 4 - Make Materials Paintable

After you make an object paintable, you must choose which of its materials you want to paint. Below the object you can see a list of its materials, and on the right side you can click the +Preset button to open a list of preset configurations available for this material.

NOTE  If you don't see any presets, then read the documentation HERE.


Step 5 - Export Material & Textures

After you add a material preset, you can see a new Export button next to it. If you click this, then you can save a copy of this material and all its textures to the location you specify.

Once you finish painting you can use this new material with painted textures in your scenes.

NOTE  If you don't want to save the material and all textures then you can click the Export button individually next to each texture.


Step 6 - Open Paint in Editor

To open the main Paint in Editor window, click on Window > Paint in 3D > Paint in Editor from the top menu bar, or select the Paint in Editor tool from the top left tool bar.

NOTE  You must enter play mode to be able to paint.
NOTE  You must paint in the Scene view.

At the top you can see 3 boxes: Tool, Material, and Shape.

If you click the Tool box, then you can choose a paint tool. These control how the paint is applied in the scene (e.g. dots, line, triangles).

If you click the Material box, then you can choose a paint material. These define a set of textures that allow you to paint a type of surface (e.g. brick, dirt, chrome).

If you click the Shape box, then you can choose the paint shape. These change the shape of the material as you paint, giving more interesting edges.



Step 7 - Save

Make sure you're on the Paint in 3D window's Scene tab.

After you've painted your textures, you can click the Re-Export All button at the bottom right. This will automatically save all previously exported textures with the current paint state.

NOTE  If you exit play mode before pressing this, then your paint progress will be lost.
NOTE  If you don't want to save all painted textures, then you can manually click Export next to each individual texture.



UV Data Requirements

For Paint in 3D to work, the mesh you want to paint must have UV data. Not only that, but for best results the UV data shouldn't have overlapping areas (e.g. no mirroring), or exceed the 0..1 range (e.g. no texture tiling).

If your mesh fails any of these requirements, then I recommend you modify the UV data in your favorite 3d modelling program (e.g. Blender).

NOTE  The default Cube mesh that comes with Unity has overlapping UV data. I recommend you use the Separated Cube mesh that comes with Paint in 3D instead.
NOTE  The default Cylinder mesh that comes with Unity has overlapping UV data. I recommend you use the Separated Cylinder mesh that comes with Paint in 3D instead.

What if I don't know how to UV map?

Unity can automatically do it for you. Just keep in mind that doing it manually will allow you to achieve better results.

To automatically generate UV data, you can enable the Generate Lightmap UVs setting in your mesh import settings. This will generate UV data suitable for painting, and they will be stored in the second UV channel.

NOTE  Most shaders (including all default Unity shaders) only read data from the first UV channel, so this new UV data won't be used. To use it, you must either use a shader that reads from the second UV channel instead of the first, or copy the new UV data from the second UV channel to the first.
NOTE  If you want to remove UV seams from this newly generated UV data, then you will have to run the second UV channel through the Seam Fixer tool.


Does Paint in 3D come with shaders that use the second UV channel?

Yes, Paint in 3D comes with several shaders designed to solve this issue.

If you want to keep your original shader/material that uses the first UV channel, then you can use the Paint in 3D / Overlay shader on top of your base material. Paint in 3D already comes with a pre-configured material called Overlay (Second UV Coord), which you can add on top of your base material. Keep in mind you will now be painting the second material (index 1), so you must update your P3dMaterialCloner and P3dPaintableTexture component settings to target the second material slot index (1).

This technique is demonstrated in the UV Requirements / Automatic Overlay demo scene.

NOTE  If your mesh has multiple sub-meshes, then this technique won't work.

If you don't mind replacing your original shader/material, then the Paint in 3D / Solid shader can be used instead. This shader uses the first UV channel for base textures, and then allows you to override the albedo/opacity/smoothness/etc using secondary textures that use the second UV channel.

This technique is demonstrated in the UV Requirements / Automatic Secondary demo scene.

NOTE  The secondary/override textures in this shader use premultiplied alpha blending, so you should change your paint brushes to use the Premultiplied blending mode.
NOTE  You should expand the P3dPaintableTexture component's Advanced menu, and change the Conversion setting to Premultiply.


How do I copy UV data from the second UV channel to the first?

Paint in 3D comes with the Coord Copier tool, which allows you to do this.

To use this tool, select your mesh in the Project window, and click the context menu (⋮icon) button at the top right of the inspector, and select the Coord Copier (Paint in 3D) option.

Your Project window should now contain a Coord Copier (YOUR_MESH_NAME) prefab, and you can click Generate to copy the coords over.

The generated mesh with the copied coords is now placed as a child of the Coord Copier prefab, and you can drag and drop it into your Mesh Filter or Skinned Mesh Renderers.

NOTE  This tool will overwrite the UV data in the first UV channel. If you still need this data, then this solution isn't suitable for you.


Can I use the second UV channel with the Standard shader?

Kind of. Using the following steps you can get it working using the detail texture. Just keep in mind this isn't the best solution, because it doesn't replace the albedo exactly.

Step 1 Select your material that uses the Standard shader, and change the Secondary Maps > UV Set setting to UV1.
Step 2 Select your P3dPaintableTexture component, and change the the Slot setting from _MainTex to _DetailAlbedoMap.
Step 3 Change the Channel setting to Second.
Step 4 Change the Shader Keyword setting to _DETAIL_MULX2.


How can I use the second UV channel with a different shader?

Most shaders only the first UV channel, so you will need to make an alternative shader that uses the second.

For example, instead of using uv_MainTex you use uv2_MainTex, or instead of using texcoord0 : TEXCOORD0, you use texcoord1 : TEXCOORD1.

NOTE  When painting using the second channel, you must set the P3dPaintableTexture > Channel setting to Second.

Paint in 3D comes with a range of shaders that allow you to specify the UV channel used (e.g. P3d Opaque).




Fixing UV Seams

When you make complex 3d models it's inevitable your mesh will have UV seams, where one part of the texture is disconnected from another.

This normally isn't a problem with clever texturing, but when painting it can result in visual seams between these disconnected UV islands (see the seams around the shoulders on the zombie image).


How do we fix them?

To fix these UV seams, Paint in 3D comes with a tool that can convert a normal mesh with UV seams into a fixed mesh without UV seams.

To access the tool, select any mesh in your project, click the context menu icon (⋮ button) at the top right, and select Fix Seams (Paint in 3D).

This creates a seam fixer in your project, and automatically sets the Source mesh to the one you picked.

If you want to see the result of the seam fixing, set the Debug Scale setting to 10 or so.

You can now press the Generate button to generate the fixed mesh.

If it was successful, you should see something like this in your Scene window:

Where the green lines are the original seams, and the blue lines are the new fixed seams. The blue lines should be outside of your original green lines. If your blue lines are overlapping then try reducing the Border setting a little, or the Threshold setting.

NOTE  Fixing seams adds vertices to your mesh. If your mesh was already close to the mesh vertex limit then this tool may not work for you.
NOTE  This tool isn't magic. If your original UV map is laid out poorly then this will won't fix it.


How do we use the fixed mesh?

The generated mesh will be placed as a child of the seam fixer in your Project window.

To use it, just drag and drop it from here into your MeshFilter or SkinnedMeshRenderer that uses this mesh.



Auto Seam Fixer

If the above steps seem too tedious for you, then you can let Paint in 3D automatically do the seam fixing for you.

STEP 1 Select your paintable GameObject.
STEP 2 Find the P3dPaintable component in the inspector window.
STEP 3 Expand the Advanced settings.
STEP 4 Change the UseMesh setting to AutoSeamFix.

Your mesh will now be auto seam fixed just before you paint it for the first time.

NOTE  This technique doesn't allow you to adjust the seam fix settings.
NOTE  This technique has some performance overhead when you first paint your object, because the mesh must be seam fixed before it can be painted.

If either of these caveats is an issue, then I recommend you follow the previous section and manually seam fix the mesh.




Examples Of UV Maps

Below are some examples of different kinds of UV maps.

To view these yourself for any mesh, simply open the cotext menu for a mesh (⋮ button), and select Analyze Mesh.


Example 1

This is an example of what a good UV map looks like.

As you can see, the whole mesh is unwrapped making good use of the texture space. All triangles are within the standard (0..1) boundary, none of the triangles overlap, and there is sufficient spacing/padding between each UV 'island'.



Example 2

This is an example of what a bad UV map looks like.

As you can see, most of the triangles have no UV data, so they cannot be painted. The remaining triangles have UV data, but it's all overlapping, which means painting one area will cause paint to appear in multiple areas, because they share the same pixels.



Example 3

This is another example of what a bad UV map looks like.

As you can see, most of the triangles are out of bounds. This means most of the mesh can't be directly painted. If you paint the remaining areas, the paint will appear on the out of bounds areas due to texture tiling.




Undo & Redo

Paint in 3D supports runtime undo & redo, but to save memory it's disabled by default.

To enable it, you need to follow these steps:


UndoRedo Setting

The first step is to select your P3dPaintableTexture components, and enable the UndoRedo setting.

If you're painting animated objects then you should set this to FullTextureCopy, but for most scenarios you can use LocalCommandCopy.

If you use the FullTextureCopy state mode then you must also set the State Limit setting. A value of 10 means you can undo paint operations 10 times, and then redo them 10 times. If you paint 11 times with this setting then your initial paint state will be deleted, and you will only be able to undo 10 times to your first paint operation.

You can read the P3dPaintableTexture documentation for more details.



Store States

The next step is to find your painting components, and enable the Store States setting.

This setting will automatically call the StoreState method on each P3dPaintableTexture in your scene, letting Paint in 3D know that you're about to paint on them.

For example, the P3dHitScreen component has the StoreStates setting, as well as P3dToggleParticles, and P3dTapThrow.



Buttons

Finally, you can add the P3dUndoAll and P3dRedoAll components to your UI elements, and they will automatically be set up so when you click them they perform an undo or redo operation.

And that's it, your game should now have Undo & Redo paint functionality!



Memory Usage

Keep in mind that when using the FullTextureCopy mode, each undo/redo state is stored as a full texture state. This means that if your texture is 1024x1024 using the RGBA32 format, then each undo/redo state will consume 1024x1024x4 bytes of memory, or 4 megabytes.

If your game requires a lot of undo states and you want to reduce memory usage, then you should use the LocalCommandCopy mode instead, which will store a list of all paint commands (e.g. decal texture + paint orientation). This approach can reduce memory usage a lot, and it allows unlimited undo levels, but performing an undo requires rebuilding the texture from scratch, which can be slow if you have a lot of undo states. It also may not work correctly if your mesh is animated, because the paint will apply to different areas if your mesh has changed.



Manual Undo & Redo

The undo & redo functionality listed above works well for most games, but it's not suitable for all scenarios. To get full control you can manage it yourself using the following methods in the P3dPaintableTexture class:

Call StoreState before you apply paint to your objects.

Call Undo/Redo after, when you want to undo or redo.

These methods can also be accessed from the P3dPaintableTexture inspector context menu (⋮ button at top right), so you can test it from the editor.




Blend Modes

Blend modes allow you to change the way your painted pixels are applied to your textures.

If you're using in-editor painting, then you can right click a material to select it, and change the Blend Mode of the P3dPaintDecal components you wish to modify.

If you're using in-game painting, then you can change the Blend Mode in the painting components (e.g. P3dPaintSphere, P3dPaintDecal).


Alpha Blend

This blend mode increases the alpha channel of your texture, and blends the RGB channels of your texture toward the color of your paint, based on the opacity of your paint.



Alpha Blend Inverse

This blend mode works similar to Alpha Blend, but it applies the blending in reverse.

This allows you to paint 'behind' the current painted areas, or fill them in.



Alpha Blend RGB

This blend mode works similar to Alpha Blend, but the alpha channel is not modified. This is ideal when you want to paint on only the solid parts of an already transparent texture.



Additive

This blend mode works by increasing the RGBA values of your texture based on the paint color.

NOTE  This modifies alpha channel as well. If you don't want this, then set your paint alpha to 0.


Additive Soft

This blend mode works similar to Additive, but the strength of the blending fades based on how bright the texture already is.



Subtractive

This works by decreasing the RGBA values of your texture based on the paint color.

NOTE  This modifies alpha as well, set the alpha to 0 if you don't want this.


Subtractive Soft

This blend mode works similar to Subtractive, but the strength of the blending fades based on how dark the texture already is.



Replace

This blending mode works like Alpha Blend, but the alpha channel of the texture will transition toward the paint alpha channel value, rather than just increase. In effect, this blends all channels toward the paint color at the same rate.

This is useful for painting advanced textures (e.g PBR), where you want to paint a specific alpha channel value.

NOTE  Decal painting normally stores the shape of the decal in the alpha channel, but because this mode allows you to replace all channels, the shape must be stored in a separate Shape texture.


Replace Original

This blending mode works similar to Replace, but instead of transitioning to the paint color, the paint color will be based on the original color of the texture.

This color is defined by the P3dPaintableTexture component's Texture and Color settings.



Replace Custom

This blending mode works similar to Replace Original, but instead of transitioning to the original texture color, you can specify a custom texture and color to transition to.

NOTE  This blending mode ignores mesh UV, so it may produce unexpected results if your meshes have very different UV data.


Multiply Inverse RGB

This blend mode allows you to darken your texture based on the paint color.

To make it easier to use, the blending is inversed. This means your decal should be white where you want the color to be multiplied, and black where you want no change.



Blur

This blend mode allows you to blur your texture based on the paint opacity.

NOTE  This effect works by sampling pixels around the current pixel. This means it's possible for colors outside the mesh to 'bleed' in. To avoid this issue, you must set the P3dPaintableTexture component's Advanced / LocalMask setting. This texture can be created using Paint in 3D if you begin with a black paintable texture, paint it white, and then save that texture to be used as a local mask.


Normal Blend

This blend mode allows you to paint a normal map with another, and both normal maps will combine together.



Normal Replace

This blend mode allows you to paint a normal map on top of another, and the original normal map will be replaced by the new one.



Flow

This blend mode allows you to move the pixels in the texture you're painting. The direction and distance the pixels will move is based on the normal map you specify.

NOTE  This effect works by sampling pixels around the current pixel. This means it's possible for colors outside the mesh to 'bleed' in. To avoid this issue, you must set the P3dPaintableTexture component's Advanced / LocalMask setting. This texture can be created using Paint in 3D if you begin with a black paintable texture, paint it white, and then save that texture to be used as a local mask.



Skinned Mesh Painting

Paint in 3D allows you to paint skinned meshes. The set up is almost the same, where you add the P3dPaintable component alongside the SkinnedMeshRenderer component. However, there are some things to keep in mind.


Colliders

To paint objects in your scene you need to use a hit component like P3dHitScreen, P3dHitParticles, etc. Some of these components like P3dHitNearby will apply in 3D without the need for colliders, but components like P3dHitScreen use Physics.Raycast, which means your paintable objects need colliders.

Normal non-skinned meshes are usually static, so you can just add a MeshCollider. However, skinned meshes usually bend and deform such that you cannot use a MeshCollider, because as soon as it animates, the collider surface will not match the visual mesh, which causes the paint to apply to the wrong area.

While the collider doesn't have to match the visual mesh, it needs to be close enough relative to the size/radius of your paint tool (e.g. P3dPaintDecal's Radius & Scale settings). If you have a complex skinned mesh, this usually means you must add many colliders to your object (e.g. BoxColliders and CapsuleColliders, perhaps one per bone).

However, if your game doesn't require precise painting (e.g. bullet wounds only), then there is one alternative strategy that is simpler to configure that is shown in the "PaintIn3D/Examples/Zombie Blood" demo scene. In this scene, the skinned zombie mesh is given one large BoxCollider, that encapsulates the whole zombie animation cycle. The P3dHitScreen component's RotateTo setting is set to Normal, and RotateTo/Direction is set to RayDirection. The P3dPaintDecal component's Scale.z setting is then set to 15 (the exact value depends on the size of the skinned mesh). With this configuration, the paint is aligned to and stretched along the hit direction, so it goes through the skinned mesh.



Include Scale

Inside the P3dPaintable component is the IncludeScale setting. Depending on your skinned mesh, you may need to enable this setting for the painting to work.

I wasn't able to identify exactly when this setting needs to be enabled or disabled, so it can't be automatically set. I think it has something to do with the software used to export the mesh, or the export settings, so for now you must manually try toggling this if you find your mesh can't be painted even if every other setting looks correct.




Drawing Connected Lines

When painting using components like P3dHitScreen, you may notice your paint can appear as separate dots. This can easily happen if the object you're painting is far away, your paint brush radius is low, or the surface you're painting is at a sharp angle relative to your camera.

This happens because the paint is actually applied as a dot, and to make a continuous line the dots are simply placed close enough together that they no longer appear as dots. However, under certain circumstances this illusion can be broken.

To improve this, the easiest way to is to just lower the P3dHitScreen component's Interval setting. This will increase the amount of dots drawn by reducing the pixel distance between them. However, this carries a performance penalty, and it only makes the issue less apparent, it doesn't solve it.

To solve this issue you want to have each separate dot connect together to form a continuous line, and this can be done by enabling the Advanced / Connect Hits setting. This setting appears on components like P3dHitScreen, P3dHitNearby, and P3dHitBetween. Instead of sending hit point information to paint components, it will instead connect the hits points, and send out hit lines.

NOTE  The lines are connected together from each hit point. Since there are a limited number of hit points, this means it's possible that the lines can skip over complex geometry and look incorrect. To reduce this issue you can reduce the Interval setting, just keep in mind this has a performance penalty.

Overlap Points

When using connected hits, the connected lines are drawn separately, which causes the overlapping corners to receive double the paint. This is an issue with semi-transparent painting, because they will appear stronger than the main line body.

To fix this, you must enable the Advanced / Clip Connected setting, which eliminates this issue in most scenarios.




Painting Multiple Textures

Paint in 3D allows you to paint multiple textures at the same time (e.g. Albedo + Normal).

To set this up you must first add multiple P3dPaintableTexture / Paintable Texture components to your object, and make them target different texture slots in your current material/shader. For example, the first texture could be for the _MainTex slot, and the second texture could be for the _BumpMap slot.

NOTE  The actual texture slot names may be different depending on your shader. Make sure you click the dropdown to see what's available.

Next, you must choose a different paint Group for each component. By default all textures are assigned to group "0: Albedo (RGB) Alpha(A)". If your first texture is albedo then you can keep this value. If your second texture is a normal map then you can change it to group "10: Normal (RGBA)".

Next, you must make multiple paint components (e.g. P3dPaintDecal / Paint Decal), and change their Group setting to match the texture groups you previously set.

You can now paint two textures at the same time! These same steps can be repeated for any number of textures.

NOTE  If you're using a special texture that doesn't have a corresponding group, then you can make your own custom group. Just right click in your Project window, and select Create > Paint In 3D > Group Data, you can then set your own custom group name, and unique Index.
NOTE  If you're painting multiple textures at the same time it's a good idea to make the painting components have the same settings. If you're using paint modifiers, then you can disable the Unique setting, to ensure each modifier picks the same random values.


Advanced Optimization

Although paint in 3D is very optimized, it's still easy to make it run slow, especially on mobile devices. Here are some tips and tricks on how to optimize your scene and maximize performance.


Texture Resolution

The easiest way to optimize painting performance is to reduce the size of your P3dPaintableTexture. If you halve the width & height of your texture, then there are now only 1/4 of the pixels being painted, which means your paint will apply up to 4x faster.

I recommend you reduce the texture resolution as much as possible, until you find the perfect balance between performance and visual quality.

TIP If your object has a high resolution texture that you can't reduce, but your paint doesn't have to be as high detail - you can separate your paint from your high resolution texture. This can be done by painting on a separate (lower resolution) texture, and blending this into your (higher resolution) base texture. This technique can be done using a custom shader with a separate paint texture, or using a transparent paintable material laid on top.


Paint Framerate

Another easy way to optimize your painting is to paint less often.

For example, the P3dHitScreen component has the Interval setting, which allows you to choose the pixel distance between each paint hit. If you set this to 3 pixels and move your mouse/finger 300 pixels in one frame, then this will result in your scene being painted 100 times. If you increase this to 6 pixels, then it will result in the scene being painted 50 times, or a 2x optimization. You can also set the Frequency to OnceEveryFrame, which will paint the scene no more than once per frame.

For example, the P3dHitParticles component by default paints every time a particle hits something. This can be optimized using the Advanced / Skip setting, which allows you to paint once every n times.

NOTE  If you paint less often, then gaps may appear in your paint. If the hit component you're using supports it, you should enable the Advanced / Connect Hits setting, which will fill these gaps in.
NOTE  If you can't or don't want to use connected hits (e.g. you're using paint modifiers, or a component that doesn't support them) you can compensate for the lower painting frequency by increasing the paint radius, or the paint opacity.


Split Meshes

If your object has a large texture and you cannot reduce its resolution, then you can still optimize the painting without a loss in detail. This is done by splitting your mesh up into smaller parts, and setting them up as separate objects. If done correctly, when you paint your object only a small area of it will be painted, and thus less pixels will be processed overall.

The easiest example of this is painting a large wall. Instead of having one wall mesh, you can split it up into a grid of smaller sections. If done correctly you will still have the same overall texture resolution, but when painting you will only every paint a small number of meshes in this grid, and thus your painting performance will be greatly improved.

NOTE  If you split your mesh up then each individual mesh will now be using a smaller area of the UV data. To not waste texture memory you should modify each mesh so the UV data covers the whole texture area, or use the Atlas setup seen in the example scenes so they share the same texture.



Known Issues


_MainTex & URP/HDRP

If you're using URP or HDRP, then you may notice painting _MainTex (Albedo) doesn't work, even though it shows up in the inspector list.

This is because these shaders seem to have a 'fake' _MainTex slot that doesn't do anything.

To fix this, you must instead paint the _BaseMap, which is the 'real' albedo texture.



Galaxy S6 / Mali-T760MP8

This device can incorrectly paint on Android 7 with the OpenGL ES3 graphics API. Previously painted pixels are observed to randomly become transparent and otherwise change.

If you need to support this device:

Step 1 Open the "P3D Decal.cginc" and "P3D Sphere.cginc" files in the PaintCore/Required/Resources folder.
Step 2 In both files, comment out or delete this line: discard;

This should allow you to paint properly again.

NOTE  This modification removes the ability to paint objects with overlapping UVs (e.g. the default Unity cube). Most well designed meshes shouldn't have overlapping UVs though, so this shouldn't be an issue.


VR Tools

While the painting features can work with any VR asset/toolkit, the Paint in 3D VR examples are only configured to work with Unity's legacy XR system.




Can I Create New Paint Presets?

Yes, you can find the existing presets in the PaintCore/Extras/Presets folder.

To make a new one I recommend you duplicate one of the existing ones, and change its settings to your liking.


Paintable Textures

Each preset has one or many P3dPaintableTexture components.

Each paintable texture's Slot setting is automatically set when using the Paint in 3D window, so you can leave it empty.

Each paintable texture must have its own Group setting, which defines the kind of texture it will paint. For example, if you have one paintable texture that targets the "Occlusion (G)" group, then this preset will allow you to paint occlusion textures.

NOTE  To understand how all the other paintable texture settings work I recommend you look at the in-game examples, because the settings are the same and they're more comprehensively covered there.


Paint Groups

For a preset to show in the Paint in 3D window, the shader you want to paint must have textures that match all paintable textures in the preset.

Since shaders can arbitrarily define texture names, you must manually associate a paint group with a shader and its texture slot.

You can find the existing paint groups in the PaintCore/Extras/Groups folder.

The way this works is each paint group defines a list of shaders and texture names associated with the group. In the example image you can see the "Albedo (RGB) Alpha (A)" group, which among others has the "_MainTex@Standard" line. This means the "Standard" shader has the "_MainTex" texture, which is an albedo and alpha texture.

Paint in 3D comes pre-configured with paint groups and associations for most built-in Unity shaders.




Can I Create New Paint Tools? (In-Editor)

Yes, you can find the existing tools in the PaintCore/Extras/Tools folder.

You can duplicate one of the existing ones, and change its settings to your liking.

NOTE  I recommend you look at the in-game examples to understand how most of these settings work, because the settings are the same and they're more comprehensively covered there.


Can I Create New Paint Materials? (In-Editor)

Yes, you can find the existing materials in the PaintCore/Extras/Materials folder.

NOTE  These materials are P3dMaterial materials. These are different from Unity's built-in Materials, though functionally they are similar.
Step 1 Select the material (P3dMaterial) that you want to use as a base.
Step 2 Duplicate the material (Edit / Duplicate), and select it.
Step 3 Rename the duplicated material, and look at its inspector.
Step 4 Drag and drop your textures into the texture slots (e.g. Albedo, Normal).
Step 5 [Optional] If you don't have textures for a particular slot (e.g. Occlusion), then click the "x" button to remove it.
Step 6 [Optional] If you have textures for a slot that don't exist, then drop them into the Add Texture setting.
Step 7 Double click the material prefab to open the prefab editor, and click the "Build Material" button.

Your material should now have its textures replaced, and it should be ready to paint.

NOTE  The texture slots are chosen based on the name of your texture. For example, the albedo texture should have the _Albedo suffix. Other suffixes you can use are _Normal, _Metallic, _Occlusion, _Smoothness, _Alpha, _Emission, _Height, _Specular. These suffixes are defined in the P3dGroup settings, which you can find in the PaintCore/Extras/Groups folder.

To make an icon for your material:

Step 1 Follow the above steps to create a paint material.
Step 2 Open the "PaintInEditor/Examples/Icon Builder (Material)" demo scene.
Step 3 Run the scene, and paint the sphere with the paint material you created earlier.
Step 4 Open your paint material prefab.
Step 5 Click the "Build Icon" button in the paint material inspector.

Your paint material should now have an icon. You may want to play with the scene light brightness to achieve best results.



Can Each Individual Paint Be De/Serialized? (e.g. networking)

Yes, there are two general techniques to do this depending on your requirements.

NOTE  This is an advanced topic and requires C# knowledge.

Paint Hits

Paint hits are the high-level data used to paint your scene (e.g. the P3dHitScreen component sends hit data to components like P3dPaintDecal). Depending on the paint hit type, this just stores the world space position, rotation, seed, and other basic data.

This is the easiest data to send, but since the data is handled in world space and applied to everything in the scene, it's not suitable for moving objects.

To listen for paint hits you can create a new component and add it to the GameObject that has your P3dHit___ component. You can then implement any of the IHit___ interfaces, to get specific hit data. For example, the P3dHitScreen component can send point and line hit data, so you can implement the IHitPoint and IHitLine interfaces.

When a hit occurs, the HandleHitPoint and HandleHitLine and methods from these interfaces will be invoked. You can then store the given parameters to a file, or send it over the network.

NOTE  If your object moves then you may want to transform the position and rotation values to local space before storing/sending it.

Once this data is loaded/received, you can then broadcast it again using code like this:

// Loop through all components that implement IHitPoint

foreach (var hitPoint in GetComponentsInChildren())

{

// Ignore this one so we don't recursively paint

if ((Object)hitPoint != this)

{

// Submit the hit point

hitPoint.HandleHitPoint(preview, priority, pressure, seed, position, rotation);

}

}

NOTE  If your data was converted to local space, make sure you convert it to world space before performing this code.

You can see the P3dPaintMultiplayer.cs code for an example of this.



Paint Commands

Paint commands are the low-level data used to paint each individual paintable texture. These store everything about the paint that was applied (e.g. texture references), so it's not as simple to serialize it all.

To allow for de/serialization of this data, you must first provide unique hash codes to the P3dModel, P3dPaintable, and P3dPaintableTexture components. This is done via the Advanced / Hash setting.

NOTE  If these components are part of a prefab or spawned in-game then you must manually give them new hash values that are consistent with multiple runs on your game, or other network clients.

In addition to these hashes, you must provide hashes for all textures used by your paint commands. For example, if you use decal painting then the decal texture must have a hash.

Since the Texture class is made by Unity and can't be modified, you must assign a hash to a texture using the P3dTextureHash component. You can add this component to your scene, assign the texture, and assign a hash code.

You can now listen for paint commands using the static P3dPaintableTexture.OnAddCommandGlobal event, or the instance OnAddCommand event. These events will give you every paint command for a specific paintable texture.

NOTE  These paint commands are in world space. If your objects move then you should use its SpawnCopyLocal() method, where the Transform argument matches the GameObject the paintable texture is attached to.
NOTE  If you want to store the command and aren't using SpawnCopyLocal(), then you should use SpawnCopy() to get a copy of it.

The paint command can then be stored or sent over the network using JsonUtility serialization, or similar.

Once this data is loaded/received, you can apply it using your paintable texture component's AddCommand() method.

You can see the P3dCommandSerialization.cs code for an example of this.




What Examples Are Included?

This is a list of the in-game example scenes and a description of what it shows.

Basic Setup

This shows you how the Spaceship GameObject can be made paintable. This is done by adding the P3dPaintable, P3dMaterialCloner, and P3dPaintableTexture components. You can then paint the spaceship using the mouse/finger with the Paint GameObject, which has the P3dHitScreen and P3dPaintSphere components. Keep in mind that P3dHitScreen uses raycasts, so your mesh must have a collider to work.

Seam Fixer

This shows you how to remove painting seams from your mesh using the Seam Fixer tool. Seams are caused by gaps/islands in the mesh UV data, which are usually present at the edges of complex meshes. To use the seam fixer, simply click the context menu button (⋮ icon) at the top right of your mesh inspector window. You can then select Fix Seams (Paint in 3D), adjust the settings, and click Generate. You can double click the Spaceship GameObject's MeshFilter.Mesh setting to see it in action.

Auto Seam Fix

This shows you how to automatically fix seams in your mesh without using the Seam Fixer tool. This is done by changing the P3dPaintable component''s Advanced / UseMesh setting to AutoSeamFix. Keep in mind this mode doesn''t give you control over the seam fix settings (the defaults should be suitable for most meshes). Also keep in mind there will be a small performance hit the first time you paint your object when its seam fixed mesh is generated.

Unsuitable UV

This shows you what happens when you paint a mesh that isn''t suitable for painting. For a mesh to be paintable, it shouldn''t have overlapping UV, or UV areas that exceed the 0..1 range (e.g. tiling). Notice the paint appears in unexpected areas on this mesh, because it contains both tiling UV, and overlapping areas.

Cube

This shows you how painting meshes with overlapping UV data can cause unexpected results. The left shows you what painting the standard Unity Cube mesh looks like, and the right shows what painting the Separated Cube mesh that comes with Paint in 3D looks like.

Cylinder

This shows you how painting the standard Unity Cylinder mesh can give unexpected results, and how the Separated Cylinder mesh that comes with Paint in 3D can avoid these issues.

Auto UV

This shows you how a mesh with UV data unsuitable for painting can still be painted. This is done by enabling the Generate Lightmap UVs setting in the mesh import settings. This setting will generate new UV data in the second UV channel/coord, which can be used by custom shaders. The Paint in 3D / Solid shader has this feature if you enable the Use Second UV setting. If you use this configuration then make sure the P3dPaintableTexture.Coord is set to the Second UV channel.

Overlay

This shows you how the Unsuitable UV demo scene can be modified to allow correct painting on top. This is done by generating mesh data for the second UV channel/coord as seen in the Auto UV demo scene, and using a second transparent material layer on top of the base material. This second material uses the Paint in 3D / Overlay shader, which allows you to paint on top of the base material using the second UV channel. If you use this configuration then make sure the P3dPaintableTexture.Coord is set to the Second UV channel.

Override

This shows you an alternative way to paint objects using secondary UV data. The overlay approach requires multiple materials, which may not be suitable for your project. In this scenario, you can use a shader that is specifically designed to allow you to paint on both the first and second UV channels. The P3D Solid shader has the basic albedo/metallic/etc textures that use the first UV channel, but you can also override these using secondary albedo/metallic/etc textures that use the second UV channel. This is done by painting the _AlbedoTex/_MetallicTex/etc, which will override the base texture based on the opacity of the secondary paint. Keep in mind this shader requires you to paint the override textures using the Premultiplied blending mode.

Base Color

This shows you what happens when you paint objects that have materials with different color settings (top row). Notice how the color tint also applies to the paint, because the material color is multiplied with the painted texture color. To make the painted color apply without tinting you can set the material color to white, and instead change the P3dPaintableTexture.Color setting to the base color you want (middle row). You can also set the material color to white, and instead change the default texture (e.g. Albedo texture) to the color you want (bottom row).

Dots

This shows you how decreasing the P3dPaintSphere.Radius setting and/or increasing the P3dHitScreen.Interval setting allows you to draw dotted lines.

Connect Hits

This shows you how the P3dHitScreen component''s Advanced / ConnectHits setting can be enabled, allowing paint dots to be joined together. A similar result can also be achieved using a lower Interval setting, but that has a performance penalty.

Clear Texture

This shows you how the P3dButtonClearAll component can be added to a UI element, which makes it clear the paint of all textures in your scene. This also shows you how you can clear the texture of a specific texture by connecting the OnClick event to the P3dPaintableTexture.Clear method.

Isolate Tools

This shows you how the P3dButtonIsolate component can be added to UI elements. This turns them into buttons that can enable a GameObject, and disable its siblings. This allows you to switch between different painting tools.

Tiled Painting

This shows you how the P3dPaintSphere component''s Advanced / Tile settings can be used to paint seamless textures across your objects. This is done using triplanar mapping.

Blur Painting

This shows you how the paint component''s BlendMode setting can be changed to Blur. This blending mode blurs the pixels of the paintable texture.

Mask

This shows you how to prevent the Blur blending mode from causing the pixels outside the edges of your mesh UV islands from ''bleeding'' into your main texture. This is done by setting the P3dPaintableTexture component''s Advanced / LocalMask texture. This texture can be created using Paint in 3D if you begin with a black paintable texture, paint it white, and then export the painted texture via the main Paint in 3D window.

Generate

This shows you how the P3dPaintableTexture component''s Advanced / LocalMask texture can be automatically generated with a mask suitable for use with the Blur blending mode. This is done by adding the P3dGenerateMask component alongside your P3dPaintableTexture component, and setting its Mesh setting to be the non-seam-fixed (original) version of your mesh. Automatically generating the mask like this means your build size doesn''t increase.

Decal

This shows you how the P3dHitScreen and P3dPaintDecal components can be combined to paint decals. Decals allow you to paint a custom texture on an object at a specific location. The P3dPaintDecal settings give you control over the angle, size, opacity, etc.

Wrapping

This shows you how the P3dPaintDecal component''s Wrapping setting can be used to control how the decal is applied to curved surfaces. When set to 0, the decal is applied using planar projection without any wrapping. When set to 1 the decal is wrapped around curved surfaces.

UV

This shows you how the P3dHitScreen component''s Emit setting can be set to PointsOnUV, allowing you to apply decals directly to your paintable texture in UV space. This is useful when painting complex curved surfaces, because the texture can apply without distortion if your mesh is suitably UV mapped. Keep in mind this mode requires a non-convex MeshCollider for UV data, and for your objects to all have consistent texel sizes. Keep in mind this mode does not support painting between UV seams, and you may get paint bleeding if your UV islands are too close together. This scene also makes use of the Group setting (works like Layers) to avoid cross painting.

Normal

This shows you how the P3dHitScreen component''s Advanced / Normal settings can be used to control the visibility of the decal based on to the the angle between the decal and the surface being painted. This allows you to enable or disable painting of steep angles, behind surfaces, etc.

Normal Front

This shows you how the P3dPaintDecal component''s Advanced / NormalFront setting can be used to force paint to only appear on surfaces that have a surface normal (direction) similar to the surface normal of the hit point (mouse point).

Normal Fade

This shows you how the P3dPaintDecal component''s Advanced / NormalFade setting can be used to control the sharpness of the normal cut-off transition.

Settings

This shows you how the P3dPaintDecal settings can be modified from UI buttons by calling the various functions like IncrementAngle, FlipVertical, etc.

Mirror

This shows you how the Mirror GameObject has the P3dCloneMirror component attached. This automatically mirrors all paint across a 3D plane defined by its Transform position and rotation. If you select the Mirror GameObject then you can see a preview of the mirror plane in the Scene view.

Flip

This shows you how the Mirrored Painting demo scene can be modified to prevent decals from appearing backwards when mirrored. This is done by enabling the P3dCloneMirror.Flip setting, which makes the decals appear the same on both sides.

Multiple

This shows you how mirrors can combine together, creating interesting symmetries.

Collision Painting

This shows you how to apply paint when a Rigidbody collides with something. The P3dTapThrow component can be used to spawn and throw the Ball prefab when you click or tap the screen. The ball then paints the spaceship on impact using the P3dHitCollisions and P3dPaintDecal components.

Destroyer

This shows you how to automatically destroy a GameObject itself when it hits something. This is done by adding the P3dDestroyer component alongside the P3dHitCollisions component.

Spawner

This shows you how to spawn an explosion effect when a Rigidbody hits something. This is done by adding the P3dSpawner component alongside the P3dHitCollisions component. The spawned prefab contains a ParticleSystem, and uses the P3dDestroyAfterTime component to destroy itself.

Between Painting

This shows you how the P3dHitBetween component can be used to paint between two Transform points. The P3dHitBetween.Line can be used to visualize the current beam, and the P3dHitBetween.Point setting can be used to vislize the hit point with particles.

Center Screen

This shows you how to paint everything at the center of the screen directly in front of the camera, like a laser eye beam. This is done using the P3dHitBetween component, which is set to hit between the Main Camera Transform, and the In Front Of Camera Transform, which is a child GameObject placed in front. This will then automatically call P3dPaintSphere on the first hit point it finds between these two points, and paint them red. The camera can then be rotated using the P3dPitchYaw component.

Through Points

This shows you how the P3dHitThrough component can be used to paint trhough two Transform points. The P3dHitThrough.Line setting can be used to visualize the current beam. Unlike P3dHitBetween, this component paints all surfaces between the two points.

Particle Painting

This shows you how the P3dHitParticles component can be added to your ParticleSystem GameObjects. This allows you to paint with components like P3dPaintSphere when particles hit objects in the scene. Keep in mind you must enable collision in your particle system for this to work.

Optimization

This shows you how particle painting can be greatly optimized. By default, the P3dHitParticles component paints once per particle collision. However, this can be incredibly slow if you emit too many particles. To optimize this, you can use the P3dHitParticles component's Advanced / Skip setting, which allows you to paint once every 'Skip' particles. In this scene Skip = 10, which means for every particle collision that would result in a call to P3dPaintSphere, 9 calls are skipped.

Nearby Painting

This shows you how the P3dHitNearby and P3dPaintSphere components can be combined to paint any surfaces within the specified sphere shape.

Throw

This shows you how the P3dTapThrow component can spawn and throw the Ball prefab. This prefab has the P3dHitNearby component, which can paint surfaces near the current Transform position. This effect can be used to create fireballs that pass through objects, and paint them on the way.

Auto Save Load

This shows you how the P3dPaintableTexture.SaveName setting can be used to enable automatic save/load. Paint something, then run the scene again to see it in action. Keep in mind these save names should be unique, so using the same name with multiple prefabs may not work as expected.

Undo Redo

This shows you how the P3dPaintableTexture.UndoRedo setting can be used to store undo and redo states. You can then add the P3dButtonUndoAll and P3dButtonRedoAll components to UI elements to cycle through them. Keep in mind storing of states requires the StoreStates setting to be enabled, this is on components like P3dHitScreen.

Paint Modifiers

This shows you how the paint settings can be randomly adjusted before the paint is applied. This is done by clicking the Add Modifier button inside the paint component (e.g. P3dPaintDecal), and selecting a value to modify (e.g. Angle), and then the type of modification (e.g. Random). Keep in mind these modifiers can be combined together to make even more interesting results.

Gradually Fade

This shows you how to gradually fade a texture back to its original state after you paint it. This is done using the P3dGraduallyFade component, and dragging and dropping the texture you want to have fade into the PaintableTexture setting. You can then set the BlendMode setting to ReplaceOriginal, and it will gradually fade to its original state as defined by the P3dPaintableTexture component''s Texture and Color settings.

Blur

This shows you how the Gradually Fade demo scene can be modified to blur the texture over time.

Normal

This shows you how the Gradually Fade demo scene can be modified to fade a normal map to its original state. This is done by using the Normal Replace blending mode and the DefaultNormal texture. Keep in mind the ReplaceOriginal blending mode can''t be used with normal maps, because the original texture and color are converted into normal maps, and this would cause them to be replaced by their non-converted state.

Painting Holes

This shows you how to paint holes in your paintable objects. This is done using the Subtractive blending mode, which allows you to reduce the alpha/opacity of the pixels in your texture. Keep in mind this requires you to use a shader/material that supports transparency, like the P3d Solid one used here. Keep in mind the underlying mesh still remains, so you can''t paint through the holes you create.

Replace Painting

This shows you how the P3dPaintReplace component can be used to replace every pixel in the currently painting texture. Keep in mind the GameObject with the P3dPaintableTexture must have a collider for this to work, and P3dHitScreen.Emit must be set to PointsOnUV.

Fill Painting

This shows you how the P3dPaintFill component can be used to blend every pixel in the painted texture. This allows you to gradually change it, rather than instantly like P3dPaintReplace does. Keep in mind the GameObject with the P3dPaintableTexture must have a collider for this to work, and P3dHitScreen.Emit must be set to PointsOnUV.

Paint From Invisible

This shows you how to paint an object in from an invisible state. This is done by changing the P3dPaintableTexture.Color setting''s alpha/opacity value to 0, which causes the texture to initialize as invisible. You can then reveal the object by painting the alpha/opacity back in, which is automatically done when using the Alpha Blend blending mode. Keep in mind this requires you to use a transparent material/shader like the Paint in 3D / Solid one used here.

Reveal Original

This shows you how a textured mesh can be hidden, and then revealed by painting on it. This is done by changing the P3dPaintableTexture.Color alpha/opacity value to 0. The texture can then be revealed by painting with the Additive blending mode, with a color of RGBA 0,0,0,1. Keep in mind this requires your mesh to use a transparent (e.g. cutout) shader/material.

Glass Painting

This shows you how to paint on top of a transparent material. This is done using the Alpha Blend blending mode. Notice how the erase tool erases both the paint, and the original dirt.

Override

This shows you how the Glass Painting demo scene can be modified so the paint is applied to secondary albedo and opacity textures that override the base values. Notice how the erase tool only erases the new paint, and keeps the original dirt. This technique also allows you to paint at a lower resolution, but keep the original higher resolution texture, allowing you to optimize performance. Keep in mind this configuration requires you to use a special mater/shader designed to override the base texture values (the Paint in 3D / Alpha shader is used here). Keep in mind the override textures use premultiplied alpha, so you must set your paint tools to use the Premultiplied blending mode (though erasing can use the subtractive blending mode).

Channel Counter

This shows you how the P3dChannelCounter component can be used to count the total pixels in the specified P3dPaintableTexture that exceed the threshold value in each R/G/B/A channel. The P3dChannelCounterText component can then be used to display these in the UI.

Percentage

This shows you how the Channel Counter demo scene can be modified to accurately display how much of a mesh you have painted. This is done by setting the Spaceship GameObject\u2019s P3dChannelCounter.Mesh setting to the original non-seam-fixed mesh. The paint percentage can then be displayed using the P3dChannelCounterText and P3dChannelCounterFill components. To make the paint work with any color this spaceship paint uses a second layer on top, similar to the Paint On Top demo scene.

Color Counter

This shows you how the P3dColorCounter component can be used to count the total pixels in the specified P3dPaintableTexture that are within the threshold color of a P3dColor. The P3dColorCounterText component can then be used to display these in the UI.

Change Counter

This shows you how the P3dChangeCounter component can be used to count how many pixels in the specified texture are different to a reference state. The reference state can be set using the P3dChangeCounter component''s Texture and Color settings.

Paint Remaining

This shows you how the Alpha Blend Inverse blending mode can be used to paint transparent areas of your texture. This gives the effect of painting undernearth your existing paint.

Replace Original

This shows you how the Replace Original blending mode can be used to paint your object back to its original state based on the P3dPaintableTexture component''s Texture and Color settings.

Replace Custom

This shows you how the Replace Custom blending mode can be used. This works similar to Replace Revert, but you can specify custom Texture and Color values per paint tool.

Dynamic Decal

This shows you how to paint decals with dynamically changing content. This is done using a RenderTexture, making a second camera, and changing its TargetTexture setting to the RenderTexture. Keep in mind you must correctly set up your layers and Camera.CullingMask values to keep each camera isolated. Also keep in mind dynamic decals can''t be undone using the P3dPaintabeTexture.State = LocalCommandCopy setting.

Live Painting

This shows you how to create live paint that can be repositioned in game. This is done using the P3dHitNearby component with the Preview setting enabled. You can control the draw order of these decals using the Priority setting.

Apply

This shows you how live paint can be applied to your objects when you press a button. This is done by linking the button's OnClick event to the P3dHitNearby component's ManuallyHitNow function.

Translate

This shows you how the P3dTranslate component can be used to move a live decal around the scene using UI button events that call the P3dTranslate.TranslateX/Y/Z functions.

LOD Painting

This shows you how to optimize painting speed using different LOD meshes for visuals and painting. The Knot_High GameObject is used to render the high poly visual mesh. The Knot_Low GameObject is used for the low poly MeshCollider, and for painting. To make the paint apply on the high poly mesh, the P3dPaintable.OtherRenderers list contains the Knot_High MeshRenderer.

Atlas Painting

This shows you how to paint multiple separate objects that share the same texture. This is done by setting up one paintable object normally, and then dragging and dropping the other objects into the P3dPaintable component's Advanced / OtherRenderers list. You must then add the P3dModel component to the other objects, and drag and drop the first object into the P3dModel.Paintable setting.

Fill Painting

This shows you how to paint filled in polygon shapes. This is done by replacing the P3dHitScreen component with the P3dHitScreenFill component. This causes the polygon shape you draw to be filled in with a grid of points. Keep in mind the points are drawn in screen space, so the resolution depends on the camera distance to the paint surface.

Mask

This shows you how the P3dMask component can be added to the scene. This allows you to define an area where paint will be blocked, allowing you to paint within a specific shape. You can see the location of the mask in the Scene window after you select the mask.

Stretch

This shows you how the P3dMask component''s Stretch setting can be used to extend the edges of the mask without modifying the mask texture. This allows you to more easily block painting across a large area without requiring a high resolution mask texture.

Normal Painting

This shows you how normal maps can be painted. This is done by opening the advanced settings for the P3dPaintableTexture component, and changing the Conversion setting to Normal, as well as changing the Slot to _BumpMap. You can then paint using the Normal Blend blending mode. Keep in mind the normal maps use all four RGBA channels to store data, so you must set the shape of the decal separately with the Shape setting.

Read Color

This shows you how to read the paint pixel color under the mouse/finger. This is done using the P3dReadColor component, which can be added alongside any P3dHit___ component that works using raycasts like P3dHitScreen and P3dHitBetween. Keep in mind the hit component must be set to emit PointsOnUV. The OnColor event is then used to change the UI Image color to the read color.

Event

This shows you how to detect when a specific color has been read, and trigger an action from it. This is done using the P3dReadColorEvent component, which can be added alongside the P3dReadColor component. When you read a color within the Threshold distance of your specified Color, it will trigger the OnColor event, which can be used to perform any action.

Under

This shows you how the P3dReadColor and P3dPaintBetween components can be combined, allowing you to read the color under a specific GameObject, and display that color in the UI.

Simulated Multiplayer

This shows you how the P3dPaintMultiplayer component can be added to your paint tool to simulate painting over a network. To make this work using actual multiplayer, the SimulateNetworkTransmission coroutine must be replaced with actual network transmission code, which will differ depending on the network solution you''re using.

Procedural Setup

This shows you how to set up a GameObject to be paintable from scratch using C# code. See the P3dProceduralSetup script in the PaintIn3D/Examples/Scripts folder for more details.

Triangle Painting

This shows you how to paint the underlying triangles of your mesh. This is done by changing the P3dHitScreen component's Emit setting to TrianglesIn3D.

Reveal Graffiti

This shows you how basic painting can be used to reveal a complex design. This is done using the ReplaceOriginal blending mode on your paint tool, which will revert a P3dPaintableTexture to its current Texture and Color settings. The texture that will be revealed is first hidden using the P3dPaintableTexture.Color setting with 0 alpha. The P3dPaintable.OnActivated event is then used to reset this back to full alpha using the P3dPaintable.SetColor event, with a hex color of #FFFF.

Counter

This shows you how the Graffiti scene can be modified to count the % of the design you have painted. This is done using the P3dChangeCounter component, which monitors the graffiti texture. This component''s MaskTexture is set to read the final graffiti design''s alpha channel so it ignores pixels outside of the design. The Texture setting is then set to the final design, so the currently painted/revealed graffiti texture is compared against the final design.

Draw Angle

This shows you how to make painted decals change direction based on the angle of your mouse/finger as you paint. This is done by changing the P3dHitScreen component''s RelativeTo setting to DrawAngle when RotateTo = Normal. Keep in mind the draw angle isn''t known on the first frame your mouse/finger touches the screen, so the first paint hit is skipped.

Local Mask

This shows you how to mask specific areas of your texture using your mesh UV, rather than being in 3D like with P3dMask. This is done using the P3dPaintableTexture component''s LocalMask setting inside the Advanced settings.

Paint From Code

This shows you how to paint decals under the mouse from code. Check out the PaintIn3D/Examples/Scripts/P3dPaintFromCode.cs code to see how it works!

Flow

This shows you how the Flow blend mode can be used to move the pixels in the texture you paint using a normal map that defines which direction and how far they should move. The maximum distance of the flow samples can be controlled from the Kernel setting.

Gradually

This shows you how to make paint flow across the surface of your object over time. This is done using the P3dGraduallyFade component with BlendMode = Flow. This allows you to set a normal map in the Texture setting that allows you to control how the paint should flow.

Time

This shows you how the Flow / Gradually demo scene can be modified so the paint begins at full speed, but gradually slows down until it stops. This is done by adding a second paintable ''Speed'' texture to your object. Since this speed texture isn''t applied to the material, you must enable the Advanced / IsDummy setting. Like the albedo texture, this speed texture uses P3dGraduallyFade to twist the texture around, but it also has a second P3dGraduallyFade to fade it to black to slow down the speed. The albedo texture''s P3dGraduallyFade.MaskPaintableTexture is then set to this speed texture, which will cause its flow speed to depend on the speed texture''s red channel. You can then paint both the albedo and speed textures at the same time to make the effect work. Keep in mind both of these textures must use a different P3dGraduallyFadeGroup setting.

Liquefy

This shows you how to make a liquefy paint effect, where the painted pixels will move based on the direction you move the mouse/finger when painting. This is done using the Flow blend mode with a normal map pointing up. The flow is rotated using the P3dHitScreen component''s RotateTo / Relative To = DrawAngle setting.

Primitive Painting

This shows you how to configure the four basic primitives to be paintable, so that any paintable pixels that enter them get painted. This is done using a combination of P3dHitNearby and P3dHitThrough components with the P3dPaintSphere and P3dPaintDecal components to continuously paint any pixels that enter the current volume. Keep in mind the P3dPaintDecal.NormalFront setting should be set to 2 to apply paint even if it's applying to steep or back faces.

Line Painting

This shows you how to paint points or lines between the mouse/finger start and end positions. This is done using the P3dHitScreenLine component, which can be combined with the P3dPaintSphere/Decal component. This component''s Frequency setting can be used to control how many and where the points/lines are drawn. The ConnectHits setting can also be used to switch between drawing points and lines. Keep in mind painting lines on complex geometry requires you to paint many points, otherwise they will go through the geometry.

Arrows

This shows you how the P3dHitScreen and P3dHitScreenLine components can be used together to draw lines. This is using a combination of other settings, like Frequency, and setting Rotate To / Relative To to Draw Angle.

Debug Painting

This shows you how the P3dPaintDebug component can be added alongside your other paint components. This will show you where Paint in 3D is applying the paint in the Scene view. This allows you to easily diagnose painting problems. Keep in mind this feature requires you to enable Gizmos.

Delayed Appearance

This shows you how to make paint gradually appear after you paint it, rather than instantly. This is done by configuring two P3dPaintableTexture components as normal. However, the first should have its Group setting changed to an unused group (e.g. DUMMY), and the second should have its Advanced/IsDummy setting enabled to prevent it from applying to the object''s material. A P3dGraduallyFade component is then used on the first paintable texture with the Replace blend mode, and the BlendPaintableTexture set to the second paintable texture. In effect this means your paint will apply to the second paintable texture, and this paint will be gradually copied over to the first. The other settings are just to prevent paint from applying directly to the first paintable texture.

Command Serialization

This shows you how to listen for and store paint commands that get added to any P3dPaintableTexture component in the scene. You can then click a button to reset all paintable textures, and randomly apply one of the recorded paint commands. See the PaintIn3D/Examples/Scripts/P3dCommandSerialization.cs code to see how this works. Keep in mind this is an advanced technique that requires good C# knowledge.

Clip Connected

This shows you how to eliminate the overlap points from transparent painting when using the Advanced / Connect Hits setting. This is done by enabling the Advanced / Clip Connected setting, which blends connected lines together.

Dynamic Mask

This shows you how to make a mask texture at runtime from a camera. This is done by making a new camera with its TargetTexture set to a RenderTexture in your project. A separate Screen Space - Camera UI is used to render to this camera (you could also render 3D objects). The RenderTexture is then added to a P3dMask in the scene. In this example only the Red channel of the mask is used, so the RenderTexture uses the R8 format to save memory.

Initial Tiling

This shows you how to initialize a paintable texture with texture tiling. This is done using the P3dHitNearby component with the PaintIn setting set to Start. The P3dPaintSphere component is then used to apply the paint. This component's Advanced/TileTransform can be set to any GameObject/Transform that has scaling, and you can set the Advanced/TileTexture to your desired texture. When you enter play mode, these components will activate and apply the paint to your scene. Make sure the P3dPaintSphere.Radius setting is large enough to encompass your object. To isolate painting to just one paintable texture you can drag and drop your object into the P3dPaintSphere component's Advanced/TargetModel setting.

Depth Mask

This shows you how to mask paint behind foreground objects. This is done with the P3dRenderDepth component, which can render the scene depth from the perspective of a camera. You can then drag and drop this component into the P3dPaint___ component''s Advanced / DepthMask setting.

Bullet Dents

This shows you how to paint holes and dents on a transparent target object. The bullet mark ignores transparent areas using the Alpha Blend blending mode, with only the RGB channels (1, 1, 1, 0), skipping modifications to the alpha channel.

Car Decals

This shows you how the Undo Redo demo scene can be combined with Sphere and Decal painting so you can paint on a car.

Chalk Board

This shows you how the Seamless Painting and Blur Painting demo scenes can be combined to create a chalkboard you can draw on and erase from in a realistic way.

Dripping Paint

This shows you how the Collision Painting and Flow / Time demo scenes can be combined to create a paint dripping effect that slows to a stop over time. Unlike the Flow / Time demo scene, this uses a downward facing flow texture, so the albedo and speed textures flow down the wall.

Fireball Holes

This shows you how the Nearby Painting / Throw demo scene can be modified to cut holes through an object.

Paint Ball Throwing

This shows you how the Color Counter demo scene can be modified to paint splats of color.

PBR Painting

This shows you how to paint the Albedo, Normal, and PBR (Metallic + AO + Smoothness) textures at the same time using the Group feature. You can assign a texture group using the P3dPaintableTexture.Group setting, and to paint these textures a matching P3dPaintSphere.Group/P3dPaintDecal.Group setting can be used.

Rock Engraving

This shows you how the albedo and normals can be painted at the same time, creating a rock engraving effect.

Rock Throwing

This shows you how two P3dPaintCollisions components can be used with different layer filtering and different Root settings, allowing you to apply different kinds of paint to different objects. The P3dPaintDecal component and P3dPaintableTexture''s GameObject must have matching layers for this to work.

Shield Impacts

This shows you how P3dHitCollisions and P3dPaintSphereTriplanar can be added to a Rigidbody GameObject. This allows them to paint the Spaceship_Shield GameObject with a special impact pattern. The P3dGraduallyFade component is then used to gradually fade the texture back to transparent black, simulating a spaceship shield effect.

Skybox Painting

This shows you how to paint a background skybox without seams or distortion. Notice the skybox mesh is a subdivided cube turned into a sphere with cube mapped UV coordinates. The mesh is then run through the Seam Fixer tool.

Splat Map Painting

This shows you how to paint an RGBA splat map. This is done using the Splat (R, G, B, A) group, with the Replace blending mode. Each paint tool replaces the splat texture with a specific channel color (e.g. 0,1,0,0 for green). It''s also possible to use the Additive & Subtractive blending modes on each channel instead. Keep in mind splat maps require your object to have a special material/shader that implements splat maps.

Spray Paint

This shows you how to paint an object using a spray can that emits paint particles. This is done using the P3dToggleParticles component.

Transparent Object Painting

This shows you how the albedo of a transparent object with overlapping UV data can be painted using auto generated UVs and secondary textures.

VR HVLP

This shows you how PBR spray painting can be implemented in VR using Unity''s XR library. Make sure to enable Virtual Reality Supported in your project settings. Also, make sure to select the VR Manager GameObject, and check to see if each input axis is set up. If you''re using the new InputSystem, then please read the documentation for it.

VR Pen

This shows you how VR pen painting can be implemented in VR using Unity''s XR library. Make sure to enable Virtual Reality Supported in your project settings. Also, make sure to select the VR Manager GameObject, and check to see if each input axis is set up. If you''re using the new InputSystem, then please read the documentation for it.

Wheel Dirt

This shows you how to particle collision painting can be used to apply dirt to a car as its wheel kick up dust.

Zombie Blood

This shows you how to paint dynamic blood decals on an animated SkinnedMeshRenderer GameObject. These decals are painted using the Multiply RGB blending mode, which causes them to stack and get darker. Keep in mind the zombie uses a big BoxCollider, so the decal must extend quite far into the zombie to paint it. This is done using a high P3dPaintDecal.Scale.z setting, and with the P3dHitScreen.Normal = RayDirection setting, to rotate the depth so it faces the paint direction.







Assets

Here's a list of all my other assets, please check them out!

You can also view this list on my Asset Store page.

Lean Touch

Lean Touch

Rapidly develop your game with consistent input across desktop & mobile using Lean Touch. This lightweight asset comes with many modular components, allowing you to customize them to your exact project needs!


Lean Touch+

Lean Touch+

Lean Touch+ is an extension to the popular Lean Touch asset, adding many more example scenes.


Lean Localization

Lean Localization

Lean Localization is a localization library that's designed to be as simple to use as possible for both designers, and programmers.


Lean Pool

Lean Pool

Quickly optimize the performance of your games using Lean Pool. Within minutes you can use this lightweight asset to preload, recycle, and limit the spawning of your prefabs.


Lean Transition

Lean Transition

Quickly polish your games using Lean Transition. This asset allows you to easily tween or animate almost anything in your game, making it transition smoothly.


Lean GUI

Lean GUI

Lean GUI is a colllection of components that extend Unity's GUI system, allowing you to rapidly enhance the user experience (UX) of your game's UI.


Lean GUI Shapes

Lean GUI Shapes

Lean GUI Shapes allows you to quickly add lines, rounded boxes, polygons, and much more to your GUI!


Lean Texture

Lean Texture

Lean Texture allows you quickly modify textures in your project with a range of filters, pack them together into channels, and much more!


Lean Texture+

Lean Texture+

Lean Texture+ is an extension to Lean Texture, adding many new types of texture modification tools!


CW Spaceships - Build & Destroy

CW Spaceships

Build your dream spaceship, and then have fun destroying it!


CW Orbit - Modular Backgrounds

CW Orbit

Take your space game to the next level with this collection of 8K background planets. Just drag and drop them in, and enjoy orbiting around them.


CW Nebula - Modular Backgrounds

CW Nebula

Add stunning nebula backgrounds to your 3D space games with ease.


CW Gas Giant - Modular Backgrounds

CW Gas Giant

Add stunning gas giant backgrounds to your 3D space games with ease.


CW Galaxy - Modular Backgrounds

CW Galaxy

Add stunning galaxy backgrounds to your 3D space games with ease.


CW Silhouette - Modular Backgrounds

CW Silhouette

Add stunning silhouette backgrounds to your 3D space games with ease.


CW Cosmos - Modular Backgrounds

CW Cosmos

Add stunning nebula and galaxy backgrounds to your 3D space games with ease.


Paint in 3D

Paint in 3D

Paint all your objects using Paint in 3D - both in game, and in editor. All features are optimized with GPU accelerated texture painting, so you can enjoy consistent performance, even if you paint your objects one million times!


FLOW

FLOW

FLOW allows you to add large scale interactive fluids to your scene - all highly optimized using GPU acceleration.


Destructible 2D

Destructible 2D

Unlock the full potential of your 2D games using Destructible 2D, this asset allows you to quickly convert all your boring solid sprites into fully destructible ones!


Space Graphics Toolkit

Space Graphics Toolkit

Quickly make the space scene of your dreams using Space Graphics Toolkit. This huge collection of space effects can be customized and combined in any way you like, allowing you to quickly make realistic or fantasy worlds. Each feature has been heavily optimized to run on almost any device and platform.


Space Graphics Planets

Space Graphics Planets

Enhance your space scenes using this large pack of high detail volumetric planets. These planets are finished using the powerful planet features from Space Graphics Toolkit (not required).


Volumetric Audio

Volumetric Audio

Unity sounds only emanate from a single point source. This is great for explosions and footsteps, but quite often you need something more advanced. Volumetric Audio is an easy to use package that allows you to define boxes, spheres, capsules, paths, or meshes that sounds can emanate from.






Versions

3.1.1

Fixed shader error when building for Android,



3.1.0

NOTE  This update includes file structure changes, so before installing you should delete the "PaintCore" folder.

Fixed normal map painting when Normal Map Encoding is set to DXT5nm.
Fixed P3dHitScreen painting bug with connected preview hits.
Fixed P3dHitScreen not showing clip connected preview hits.
Renamed SlidingToolButton to ToolButton.
Adding the P3dHitScreen component will automatically add the P3dPointerMouse/Touch/Pen components.



3.0.3

Moved main build to Unity 2021.3.0f1.
Updated shaders to latest version.
Fixed P3dCloneMirror component when the Flip setting is enabled.
Fixed P3dCoordCopier index counts.
Fixed P3d___Counter components updating when not necessary.
Added P3dRenderDepth component.
Added Advanced/DepthMask setting to P3dPaintSphere component.
Added Advanced/DepthMask setting to P3dPaintDecal component.
Added Depth Mask demo scene.



3.0.2

Updated shaders to latest version.



3.0.1

Fixed P3dHitScreen component's GuiLayers setting.
Fixed P3dHitScreenLine component painting.
Fixed P3dHitScreenFill component painting.



3.0.0

WARNING: To update from an earlier version: back up your project, delete the old Plugins/CW/PaintIn3D folder, and then install the new version.
Moved main build to Unity 2020.3.0f1.
Moved in-editor painting features to Plugins/CW/PaintInEditor folder.
Fixed in-editor triangle painting.
Fixed manual disabling of P3dPaintable components.
Fixed in-editor painting not painting alpha.
Added P3dPointerMouse component.
Added P3dPointerTouch component.
Added P3dPointerPen component.
Added AlphaFromAlbedo setting to P3dMaterial component.
Added Min blending mode.
Added Max blending mode.
Added Initial Tiling demo scene.



2.0.4

Moved main build to Unity 2020.3.0f1.
Fixed in-editor triangle painting.
Fixed manual disabling of P3dPaintable components.
Fixed in-editor painting not painting alpha.
Added AlphaFromAlbedo setting to P3dMaterial component.



2.0.3

Fixed P3dHitBetween when Orientation = WorldUp.
Improved P3dMask component gizmo rendering.
Added NormalReplaceOriginal blending mode.
Added NormalReplaceCustom blending mode.
Fixed P3dGraduallyFade colors being incorrect during transition.
Added Default Format setting to Config tab of the Paint In 3D window.
Added unlit versions of the 4 main example shaders.



2.0.2

Fixed connected hit painting on touch screen devices.
Added Dynamic Mask demo scene.



2.0.1

Removed global shader keywords.
Fixed P3dChannelCounter component setting changes not being reflected.
Fixed P3dChangeCounter component setting changes not being reflected.
Fixed P3dColorCounter component setting changes not being reflected.
Every P3dColorCounter now updates when a P3dColor is enabled or disabled.
Fixed P3dReadColor reading incorrect UV location on the Metal graphics API.
Changed Solid shader's Tiling setting to be a vector.
Changed Alpha shader's Tiling setting to be a vector.



2.0.0

NOTE  This is a massive update. To update you must first back up your project, delete the root PaintIn3D folder, then install the new version.

Changed folder structure to be inside Plugins/CW/PaintIn3D.
Updated inspector code to support third party assets that implement nested inspectors.
Added P3dHitNearby.PaintIn = ManuallyOnly setting.
Added Splat Map Painting demo scene.
Added Clip Connected demo scene.
Added Advanced / ClipConnected to most P3dHit___ components to eliminate connected line overlap.
Fixed texture modifiers not saving textures.



1.11.14

Fixed undo/redo of local commands breaking if the paintable object is moved.
Fixed undo/redo of local commands if you paint while in an undo state.
Fixed undo/redo of local commands when using SaveLoad = Automatic.



1.11.13

Fixed seam fixer with multi submesh meshes that share UV coords between submeshes.
Fixed seam fixer with invertex mesh UV data.
Renamed P3dHitScreen.Offset to NormalOffset.
Added P3dHitScreen.MouseOffset setting.



1.11.12

Fixed paint command order changing when using LocalCommand copy with undo/redo.



1.11.11

Fixed scenario where ConnectHits setting would cause texture to reset on iOS.



1.11.10

Added warning to P3dModel if the mesh is non-readable.
Added warning to P3dPaintable if the mesh is non-readable.



1.11.9

Inverted P3dChangeCounter results to count differing pixels.
Added P3dPaintableTexture.OnAddCommandGlobal static event.
Added P3dPaintableTexture.OnInstanceAdded static event.
Added P3dPaintableTexture.OnInstanceRemoved static event.
Added Command Serialization demo scene.
Added Hash setting to P3dModel component.
Added Hash setting to P3dPaintable component.
Added Hash setting to P3dPaintableTexture component.
Added P3dTextureHash component.
Fixed DUMMY group having preset components.



1.11.8

Added Blur Painting / Mask demo scene.
Added Blur Painting / Mask / Generate demo scene.
Added LocalMask texture sampling to Blur blending mode.
Added LocalMask texture sampling to Flow blending mode.
Added P3dGenerateMask component.
Renamed shader includes to reduce possible clashes with other assets.



1.11.7

Fixed scenarios where a disabled P3dMask would still apply masking.



1.11.6

Fixed P3dHitScreen.TouchOffset setting.
Fixed P3dMask ordering when there are multiple in the scene.
Fixed P3dPaintableTexture inspector warning with clashing slots when IsDummy is set.
Improved P3dPaintableTexture inspector to make it clearer when IsDummy is set.
Added Delayed Appearance demo scene.
Added DUMMY = -1 paint group.



1.11.5

Fixed auto seam fix feature not caching results.



1.11.4

Fixed P3dHitScreen not working on mobile when using new InputSystem package.
Made P3dHitScreen.Key work without mouse click on desktop platforms.
Added P3dHitScreen.RequiredButtons setting.
Replaced P3dHitScreen.Key setting with RequiredKey.
Updated paint modifier code to no longer use custom serialization.



1.11.3

Added P3dHitBetween.Orientation = ThisRotation setting.
Added P3dHitBetween.Orientation = ThisLocalRotation setting.
Added P3dHitBetween.Orientation = CustomRotation setting.
Added P3dHitBetween.Orientation = CustomLocalRotation setting.
Added P3dHitBetween.CustomTransform setting.
Added ClearHitCache and ResetConnections to P3dHitThrough component.
Added ClearHitCache and ResetConnections to P3dHitScreen component.
Added ClearHitCache and ResetConnections to P3dHitScreenLine component.
Added ClearHitCache and ResetConnections to P3dHitNearby component.
Added ClearHitCache and ResetConnections to P3dHitBetween component.
Fixed P3dChannelCounter when Counters list contains null entries.
Fixed P3dColorCounter when Counters list contains null entries.
Fixed P3dChangeCounter when Counters list contains null entries.
Fixed P3dChannelCounter not updating when enabled.
Fixed P3dColorCounter not updating when enabled.
Fixed P3dChangeCounter not updating when enabled.
Combined P3dHitScreenLine.Frequency = Start/End into Once.



1.11.2

Fixed various demo scene UI and description issues.
Fixed in-editor paint point offset.
Fixed in-editor paint applying with left/middle click.
Fixed harmless in-editor painting errors when editing play mode.
Modified P3dColorCounterText to use OnString event rather than UI text.
Modified P3dChannelCounterText to use OnString event rather than UI text.
Modified P3dChangeCounterText to use OnString event rather than UI text.
Added in-editor painting warning when not over the Scene view.
Added in-editor painting warning when the scene contains painting components.



1.11.1

Moved in-editor painting tools back to scene window.
Made in-editor painting tools not require colliders.
Added P3dGraduallyFade.BlendPaintableTexture setting.
Added Scale / Random paint modifier to P3dPaintSphere and P3dPaintDecal.
Fixed P3D / Solid shader's Opacity Override not working.
Fixed P3dPaintableTexture.Resize not updating mips.
Fixed P3dReadColor giving incorrect values in OpenGL.
Fixed connected hits incorrectly joining together on mobile devices.
Renamed P3dGraduallyFade.Texture setting to BlendTexture.
Renamed P3dGraduallyFade.Color setting to BlendColor.



1.10.5

Added P3dSerialization class for easier low-level de/serialization of paint commands.
Modified P3dCommand data to allow for de/serialization.
Added more Paint in 3D window material presets.
Added PaintIn3D window material export button to speed up exporting.



1.10.4

Fixed P3D/Alpha shader error in URP.
Fixed Shield Impacts demo scene.
Updated shaders to use latest BetterShaders version.



1.10.3

Fixed pink materials in URP and HDRP.
Fixed pink materials when reimporting with SRP.



1.10.2

Added Debug Painting demo scene.
Fixed bug where switching scenes would break input.



1.10.1

NOTE  This update changes a lot of features. Back up your project before updating!

Moved main build to Unity 2019.4.12f1.
Added Seam Fixer / Auto Seam Fix demo scene.
Added Auto UV demo scene.
Added Base Color demo scene.
Added Line Painting demo scene.
Added Line Painting / Arrows demo scene.
Added P3dHitScreen.Frequency setting.
Added P3dPaintable.OnActivating event.
Added P3dPaintable.UseMesh advanced setting.
Added P3dHitParticles.Emit setting.
Added P3dHitParticles.Layers setting.
Added P3dHitParticles.Root setting.
Added P3dHitCollisions.PressureMode setting.
In-Editor: Added Build Material As Decal button to materials.
P3dPaintableTexture now shows a warning when trying to paint a Slot with tiling or offset.
Improved inspector organization of all P3dHit___ components.
Improved inspector organization of all P3dDraw___ components.
Reduced Solid shader instruction count.
Reduced Alpha shader instruction count.
Improved VR Pen demo scene setup.
Simplified hierarchy of all demo scenes.
Moved fill painting to new P3dHitScreenFill component.
Fixed P3dBlendMode overlap with narrow inspector.
Replaced example shaders with Better Shaders.



1.9.14

Fixed Texture Pressure modifier errors.
Fixed Texture Random modifier errors.
Made in-editor painting only begin after you switch to the paint tab.
Added Unload Paint Brush button to in-editor paint tab.



1.9.13

Added Gradually Fade / Blur demo scene.
Added Gradually Fade / Normal demo scene.
Added Dip demo scene.
Added ability to bind in-game camera rotate.
Added ability to bind in-game camera pan.
Made in-game camera pan default to left command key on OSX.
Fixed P3dPaintableTexture inspector error when painting meshes with submeshes.



1.9.12

Added Liquefy demo scene.
Added Clear Settings button to in-editor painting Config tab.



1.9.11

Fixed compile error from editor scripts.



1.9.10

Fixed P3dHitScreen.TouchOffset DPI scaling being inverted.
Fixed P3dGroupData shader texture presets for URP and HDRP shaders.
Fixed P3dMirror in some scenarios.



1.9.9

Added Wheel Dirt demo scene.
Fixed P3dHitScreen values persisting through disable/enable.
In-Editor: Simplified P3dMaterial builder tools.
In-Editor: Added Override Angle setting to Paint tab.
In-Editor: Added Override Normal setting to Paint tab.
In-Editor: Added Override Modifiers setting to Paint tab.
In-Editor: Added Override Tiling setting to Paint tab.
In-Editor: Added Override State Limit setting to Scene tab.
In-Editor: Moved camera options to Camera tab.
In-Editor: Added Show Pivot camera option.
In-Editor: Simplified P3dPreset settings.



1.9.8

Added Dripping Paint demo scene.
Added Flow demo scene.
Added Flow / Gradually demo scene.
Added Flow / Time demo scene.
Added Emission to P3D Solid shader.
Added Shift blend mode.
Added P3dPaintableTexture.IsDummy setting to Advanced menu.
Added P3dPaintableTexture.Aniso setting to Advanced menu.
Allowed P3dPaintableTextures to be placed in child GameObjects.
Fixed painting when using per-texture anisotropic filtering above 0.
Fixed P3dHit.Root setting in some scenarios.



1.9.7

Added Local Mask demo scene.
Added Paint From Code demo scene.
Added P3dPaintableTexture.LocalMask setting to Advanced menu.
Renamed P3dMask.Shape setting to .Texture.



1.9.6

Fixed error when seam fixing mesh without UV coords.
Fixed P3dSpawner inspector not drawing.
Fixed P3dMask ignoring paint layers.



1.9.5

Fixed PPtr error when using texture modifiers.
Added P3dHitScreen.RelativeTo = DrawAngle setting.
Fixed painting of static meshes.
Added paint priority sorting for non-preview painting.
Added "Draw Angle" demo scene.



1.9.4

Added multi prefab support to P3dSpawner.
Added Radius setting to P3dSpawner.
Fixed P3dSeamFixer incorrectly upgrading.
Fixed P3dSeamFixer with unlimited bone meshes.
Fixed P3dSeamFixer bug with tiny triangles.
Fixed mesh analysis overlap test when using linear color space.



1.9.3

Fixed texture modifier de/serialization of texture references.
Fixed P3dReader error on devices that don't support async texture reads.
Fixed quick clicks making paint components behave incorrectly.
Disabled component activation context menu option when in edit mode.
Added warning to activated components when in edit mode.
Fixed P3dHitParticles error on first frame.

NOTE  Fixing the paint modifiers required changing their data structure, which means your modifier data is now lost. To update, you must manually recreate your modifiers and their settings. Sorry for the inconvenience!


1.9.2

Fixed P3dHitBetween when using Interval of -1.
Fixed P3dHitThrough when using Interval of -1.
Textures now save and load in sRGB color space only.
Fixed P3dChangeCounterEvent menu entry.
Fixed P3dReadColor in linear color space.
Fixed color painting in linear color space.
Fixed simulated VR controls when interacting with UI.



1.9.1

Fixed P3dPaintableTextureMonitorMask with MaskMesh.
Fixed undo/reco when using P3dHitScreen.
Fixed console warning when using automatic save/load.



1.9.0

Moved main build to Unity 2018.4.13f1.
Added support for the new input system (see documentation).
Fixed Auto Save Load demo scene.
Fixed Bullet Dents demo scene.
Fixed Overlay material to use the P3D Overlay shader.
Fixed P3dHitCache bug with triangles, quads, and coords.



1.8.9

Added Procedural Setup demo scene.
Added Reveal Graffiti demo scene.
Added Reveal Graffiti / Counter demo scene.
Added P3dPaintable.OnActivated event.
Added Triangles draw mode to P3dHitScreen.
Added Triangle painting tool to in-editor painting.
Added material builder tool to in-editor P3dMaterial inspector.
Added icon builder tool to in-editor P3dMaterial inspector.
Added seam fixer option to the ModelImporter inspector.
Added multi mesh support to seam fixer.
Added MaskTexture setting to P3d___Counter components.
Added channel information to P3dGroupData instances.
Added OnCompleted event to all P3d___Counter components.
Fixed seam fixer bug with higher poly meshes.
Fixed console errors that can appear when finishing in-editor painting.
Fixed normal painting on platforms that don't support DXT5nm textures.
Fixed in-editor camera to not longer snap with double left click.
Fixed P3D Overlay shader in SRP.
Fixed P3D Overlay/Alpha opacity in SRP.
Improved Seam Fixer performance.
Simplified P3dPaintable inspector.
Changed default P3dPaintable.Activation to Start.
Renamed P3dHitScreen.Fill to Draw.
Renamed P3d___Counter.Mesh setting to MaskMesh.
Replaced IHitRaycast interface with IHitCoord.
Removed P3dPaintSphere.Paint setting.
Removed P3dPaintDecal.Paint setting.



1.8.8

Updated documentation.
Added Simulated Multiplayer demo scene.
Added Live Painting / Apply demo scene.
Added Live Painting / Translate demo scene.
Fixed PBR Painting demo scene.
Modified P3dHitNearby.ManuallyHitNow() to only use non-preview painting.
Modified P3dHitBetween.ManuallyHitNow() to only use non-preview painting.
Modified P3dHitThrough.ManuallyHitNow() to only use non-preview painting.
Added C# API to P3dModifierList class.
Unlocked P3dPaintableManager default execution order.



1.8.7

Removed P3dPaintableTexture.KeepUnpaintable setting.
Allowed painting of overlapping UV areas.
Fixed painting atlas meshes that are close together.
Added P3dChangeCounter component.
Added P3dChangeCounterEvent component.
Added P3dChangeCounterFill component.
Added P3dChangeCounterText component.
Added Change Counter demo scene.
Added Rock Engraving demo scene.
Added Transparent Object Painting demo scene.
Replaced P3D Opaque shader with P3D Solid.
Replaced P3D Cutout shader with P3D Solid.
Fixed P3dPaintableTexture error when using an invalid slot.
Fixed P3dVrTool.StoreStates setting.
Fixed Undo/Redo not updating mip maps.
Replaced Alpha Blend RGB blending mode with Premultiplied.
Added P3dBlendMode.Channels setting.
Removed splat map examples.
Removed parallax mapping from example shaders.
Added P3dPaintableTexture.Normal setting.
Improved Normal Blend blending mode.
Switch VR examples to left hand for mobile touch compatibility.
Removed VR recenter function.
Fixed bug when P3dPaintSphere would fail to paint on UV.
Fixed bug when P3dPaintDecal would fail to paint on UV.
Fixed bug where paint material overrides wouldn't apply to child paint components.
Added PBR presets for P3D and Standard shaders.
Added extra paint materials.
Added asmdef.



1.8.6

Moved main build to Unity 2018.4.0f1.
Fixed textures not saving on mobile when closing the app.
Fixed Atlas Painting demo scene materials.
Added P3dPaintableTexture.Filter setting.
Added P3dPaintableTexture.WrapU setting.
Added P3dPaintableTexture.WrapV setting.
Improved VR Pen demo scene performance.
Fixed paint drifting issue on some meshes.
Rewrote in-editor painting features.
Added material Preset settings to editor Scene tab.
Renamed P3dMaterialCloner.ResetActivation method to Deactivate.
Added P3dPaintableTexture.Deactivate method.
Tidied up P3dPaintableTexture inspector.
Renamed P3dPaintableTexture.State setting to UndoRedo.
Added P3dPaintableTexture.SaveLoad setting.
Added P3dPaintableTexture.UseExisting setting.
Renamed P3dChannelCounter.SolidR/G/B/A to CountR/G/B/A.
Added P3dColorCounterFill component.
Added P3dColorCounterEvent component.
Added P3dColorCounterEvent component.
Added Read Color demo scene.
Added Read Color / Event demo scene.
Added Read Color / Under demo scene.
Added Mirror / Multiple demo scene.
Fixed Color / Random modifier inspector error.
Removed P3dScatterSpawner component.
Removed P3dDrip component.
Removed P3dMover component.



1.8.5

Fixed P3dCoordCopier mesh not appearing.
Fixed P3dSeamFixer mesh not appearing.
Added coord selection to P3dCoordCopier.
Added Automatic UV demo scene.
Added missing components to documentation.
Moved render pipeline switching tool to root folder.



1.8.4

Added P3dPaintSphere.TileTransition setting.
Added P3dPaintDecal.TileTransition setting.
Added Normal Painting demo scene.



1.8.3

Added P3dHitScreen.Fill setting.
Added P3dHitScreen.FillSpacing setting.
Added P3dMask component.
Added Path Filling demo scene.
Added Mask demo scene.
Added Mask Stretch demo scene.
Removed P3dPaintSphere.RotateTo setting.
Added P3dHitScreen.RotateTo setting.
Added Normal Blend blending mode.
Added Normal Replace blending mode.
Improved P3dConnectablePoints.ConnectHits inspector order.
Fixed P3dHitScreen stopping when reaching HitLimit.
Fixed P3dPaintableTexture.State = LocalCommandCopy bug.
Fixed P3dGraduallyFade bug.
Fixed in-editor painting of skinned meshes.



1.8.2

Improved P3dHitScreen.Spacing behaviour.
Improved connected P3dPaintSphere painting when using scaling.
Improved connected P3dPaintDecal painting when using scaling.
Added P3dChannelCounter.Interval setting.
Added P3dColorCounter.Interval setting.
Added Atlas Painting demo scene.
P3dHitParticles.Pressure setting renamed to PressureMultiplier.
P3dHitParticles.PressureMinDistance setting renamed to PressureMin.
P3dHitParticles.PressureMaxDistance setting renamed to PressureMax.
P3dHitParticles.PressureMode setting added.
Fixed P3dPaintDecal when using Texture modifiers.
Fixed P3dPaintableTexture.State = LocalCommandCopy undo and redo.
Fixed P3dModel painting with different submesh and material indices.



1.8.1

Fixed painting when P3dPaintableTexture.State is set to LocalCommandCopy.
Painting on UV now accounts for P3dPaintableTexture aspect ratio.
Added P3dHitScreen.Normal = CameraDirection setting.
Added P3dPaintableTexture.KeepUnpaintable optimization setting.
Added Decal Hit Normal demo scene.
Added Decal Normal Front demo scene.
Added Decal Normal Fade demo scene.
Added Rock Throwing demo scene.
Improved Car Decals demo scene.
Added workaround for scenario where mip maps would turn off.
Added P3dHitCollisions.Root setting.
Added P3dHitCollisions.SpeedMin setting renamed to ImpactMin.
Added P3dHitCollisions.SpeedPressure setting renamed to ImpactPressure.



1.8.0

Replaced P3dPaintDecal.TileBlend setting with TileOpacity.
Replaced P3dPaintSphere.TileBlend setting with TileOpacity.
In-Game: Fixed shader errors when compiling for PS4.
In-Game: Fixed connected hits persisting even when disabling hit component.
In-Game: Integrated P3dModify___ components into P3dPaint___ components.
In-Game: Added Collision Destroyer demo scene.
In-Game: Added Collision Spawner demo scene.
In-Game: Removed P3dPaintDirectDecal component.
In-Game: Added P3dPaintDecal.Paint setting.
In-Game: Added P3dPaintSphere.Paint setting.
In-Game: Added scene gizmo to P3dPaintSphere.
In-Editor: Fixed error when painting objects with multiple materials.
In-Editor: Added modifiers to Paint tab.
In-Editor: Added opacity setting to Paint tab.
In-Editor: Added angle setting to Paint tab.
In-Editor: Added normal settings to Paint tab.
In-Editor: Added Point mode to Paint tab.
In-Editor: Added Airbrush mode to Paint tab.
In-Editor: Added Triangle mode to Paint tab.
In-Editor: Added texture toggle settings to Paint tab.
In-Editor: Added Camera Light Lock setting to Config tab.







Components

IClone

This interface allows you to define classes that can clone paint points (e.g. mirror).



IHit

All IHit___ interfaces implement this interface so they can all be easily found with GetComponent.



IHitCoord

This interface allows you to make components that can paint points defined by UV coordinates.

NOTE  The rotation argument is in world space, where Quaternion.identity means the paint faces forward on the +Z axis, and up is +Y.


IHitLine

This interface allows you to make components that can paint lines defined by two points.



IHitPoint

This interface allows you to make components that can paint 3D points with a specified orientation.



IHitQuad

This interface allows you to make components that can paint quads defined by a pair of two points.



IHitTriangle

This interface allows you to make components that can paint triangles defined by three points.



P3dBlendMode

This defines the blending mode used by a painting operation.


int
Index

This is the index of the currently selected blending mode.



Color
Color

When using the ReplaceCustom blending mode, this allows you to specify the replacement color.



Texture
Texture

When using the ReplaceCustom blending mode, this allows you to specify the replacement texture.



float
Kernel

When using the Blur or Flow blending modes, this allows you to set the maximum pixel distance of samples.



Vector4
Channels

This allows you to control which channels will be modified by this blending mode.

1,1,1,1 = All channels will be modified.

1,0,0,0 = Only red will be modified.




P3dChangeCounter

This component will check all pixels in the specified paintable texture, compare them to the reference state defined in this component, and tell you how many of them differ by more than the threshold value.


static LinkedList<P3dChangeCounter>
Instances

This stores all active and enabled instances.



float
Threshold

The RGBA values must be within this range of a color for it to be counted.



Texture
Texture

The texture we want to compare change to.

None/null = white.

NOTE  All pixels in this texture will be tinted by the current Color.


Color
Color

The color we want to compare change to.

NOTE  All pixels in the Texture will be tinted by this.


int
Count

The previously counted amount of pixels with a RGBA value difference above the threshold.



float
Ratio

The Count / Total value.



static long
GetTotal
ICollection<P3dChangeCounter> counters = null

The Total of the specified counters.



static long
GetCount
ICollection<P3dChangeCounter> counters = null

The Count of the specified counters.



static float
GetRatio
ICollection<P3dChangeCounter> counters = null

The Ratio of the specified counters.




P3dChannelCounter

This component will total up all RGBA channels in the specified P3dPaintableTexture that exceed the threshold value.


static LinkedList<P3dChannelCounter>
Instances

This stores all active and enabled instances.



float
Threshold

The RGBA value must be higher than this for it to be counted.



int
CountR

The previously counted amount of pixels with a red channel value above the threshold.



int
CountG

The previously counted amount of pixels with a green channel value above the threshold.



int
CountB

The previously counted amount of pixels with a blue channel value above the threshold.



int
CountA

The previously counted amount of pixels with a alpha channel value above the threshold.



float
RatioR

The CountR/Total value, allowing you to easily see how much % of the red channel is above the threshold.



float
RatioG

The CountG/Total value, allowing you to easily see how much % of the green channel is above the threshold.



float
RatioB

The CountB/Total value, allowing you to easily see how much % of the blue channel is above the threshold.



float
RatioA

The CountA/Total value, allowing you to easily see how much % of the alpha channel is above the threshold.



Vector4
RatioRGBA

The RatioR/G/B/A values packed into a Vector4.



static long
GetTotal
ICollection<P3dChannelCounter> counters = null

The Total of the specified counters.



static long
GetCountR
ICollection<P3dChannelCounter> counters = null

The CountR of the specified counters.



static long
GetCountG
ICollection<P3dChannelCounter> counters = null

The CountG of the specified counters.



static long
GetCountB
ICollection<P3dChannelCounter> counters = null

The CountB of the specified counters.



static long
GetCountA
ICollection<P3dChannelCounter> counters = null

The CountA of the specified counters.



static float
GetRatioR
ICollection<P3dChannelCounter> counters = null

The CountR / Total of the specified counters.



static float
GetRatioG
ICollection<P3dChannelCounter> counters = null

The CountG / Total of the specified counters.



static float
GetRatioB
ICollection<P3dChannelCounter> counters = null

The CountB / Total of the specified counters.



static float
GetRatioA
ICollection<P3dChannelCounter> counters = null

The CountA / Total of the specified counters.



static Vector4
GetRatioRGBA
ICollection<P3dChannelCounter> counters = null

The GetCountR/G/B/A / GetTotal of the specified counters stored in a Vector4.




P3dClone

This is the base class for all components that repeat paint commands (e.g. mirroring).


static LinkedList<P3dClone>
Instances

This stores all active and enabled instances in the open scenes.




P3dCloneMirror

This component grabs paint hits and connected hits, mirrors the data, then re-broadcasts it.


bool
Flip

When a decal is mirrored it will appear backwards, should it be flipped back around?




P3dColor

This component allows you to define a color that can later be counted from the P3dColorCounter component.

NOTE  You should put this component its own GameObject, so you can give it a unique name.

Color
Color

The color associated with this component and GameObject name.



static LinkedList<P3dColor>
Instances

This stores all active and enabled instances in the open scenes.



int
Total

This tells you how many pixels this color could be painted on.



int
Solid

This tells you how many pixels this color has been painted on.



float
Ratio

This is Solid/Total, allowing you to quickly see the percentage of paintable pixels that have been painted by this color.




P3dColorCounter

This component will search the specified paintable texture for pixel colors matching an active and enabled P3dColor.


static LinkedList<P3dColorCounter>
Instances

This stores all active and enabled instances.



float
Threshold

The RGBA values must be within this range of a color for it to be counted.



List<Contribution>
Contributions

Each color contribution will be stored in this list.



static long
GetTotal
ICollection<P3dColorCounter> counters = null

The Total of the specified counters.



static long
GetCount
P3dColor color, ICollection<P3dColorCounter> counters = null

The Count of the specified counters.



static float
GetRatio
P3dColor color, ICollection<P3dColorCounter> counters = null

The Ratio of the specified counters.



int
Count
P3dColor color

This tells you how many pixels of the specified color are in the current PaintableTexture.




P3dCommand

This is the base class for all paint commands. These commands (e.g. paint decal) are added to the command list for each P3dPaintableTexture, and are executed at the end of the frame to optimize state changes.


int
Index

This is the original array index, used to stable sort between two commands if they have the same priority.



bool
Preview

Is this preview painting, or real painting?



int
Priority

The draw order priority of this command for this frame.



P3dHashedMaterial
Material

The hash of the Material used to apply this paint command.



int
Pass

The material pass that will be used.



P3dHashedModel
Model

The hash of the Model used to apply this paint command.



int
Submesh

The mesh submesh that will be painted.



P3dHashedTexture
LocalMaskTexture

The LocalMask that will be used when painting.



Vector4
LocalMaskChannel

The channel of the LocalMaskTexture that will be used.




P3dCommon

This class contains some useful methods used by this asset.


static void
SaveBytes
string saveName, byte[] data, bool save = true

This method allows you to save a byte array to PlayerPrefs, and is used by the texture saving system.

If you want to save to files instead then just modify this.



static byte[]
LoadBytes
string saveName

This method allows you to load a byte array from PlayerPrefs, and is used by the texture loading system.

If you want to save to files instead then just modify this.



static bool
SaveExists
string saveName

This method tells if you if there exists save data at the specified save name.



static void
ClearSave
string saveName, bool save = true

This method allows you to clear save data at the specified save name.




P3dCoordCopier

This tool allows you to copy UV1 data into UV0. This is useful if you let Unity automatically generate lightmap UV data for you and you want to use them to paint normally.


Mesh
Source

The original mesh whose UV seams you want to fix.



Coord
First

The coord that will be copied into the first UV channel of the output mesh.



Coord
Second

The coord that will be copied into the second UV channel of the output mesh.



Coord
Third

The coord that will be copied into the third UV channel of the output mesh.



Coord
Fourth

The coord that will be copied into the fourth UV channel of the output mesh.




CwEditorWindow

This is the base class for all Paint in 3D editor windows.



P3dGraduallyFade

This component allows you to fade the pixels of the specified P3dPaintableTexture.


P3dPaintableTexture
PaintableTexture

This allows you to choose which paintable texture will be modified by this component.



float
Threshold

Once this component has accumulated this amount of fade, it will be applied to the PaintableTexture. The lower this value, the smoother the fading will appear, but also the higher the performance cost.



float
Speed

The speed of the fading.

1 = 1 Second.

2 = 0.5 Seconds.



P3dBlendMode
BlendMode

This component will paint using this blending mode.

NOTE  See P3dBlendMode documentation for more information.


Texture
BlendTexture

The texture that will be faded toward.



P3dPaintableTexture
BlendPaintableTexture

The paintable texture that will be faded toward.



Color
BlendColor

The color that will be faded toward.



Texture
MaskTexture

If you want the gradually fade effect to be masked by a texture, then specify it here.



P3dPaintableTexture
MaskPaintableTexture

If you want the gradually fade effect to be masked by a paintable texture, then specify it here.



P3dChannel
MaskChannel

This allows you to specify the channel of the mask.




P3dGroup

This struct allows you to specify a group index with a group dropdown selector.



P3dGroupData

This object allows you to define information about a paint group like its name, which can then be selected using the P3dGroup setting on components like P3dPaintableTexture and P3dPaintDecal.


int
Index

This allows you to set the ID of this group (e.g. 100).

NOTE  This number should be unique, and not shared by any other P3dGroupData.


List<TextureData>
TextureDatas

This allows you to specify the way each channel of this group's pixels are mapped to textures. This is mainly used by the in-editor painting material builder tool.



string
ShaderData

This allows you to specify which shaders and their properties are associated with this group.



static List<P3dGroupData>
CachedInstances

This static property returns a list of all cached P3dGroupData instances.

NOTE  This will be empty in-game.


string
GetName
bool prefixNumber

This method allows you to get the name of the current group, with an optional prefix of the Index (e.g. "100: Albedo").



static string
GetGroupName
int index, bool prefixNumber

This static method calls GetAlias on the P3dGroupData with the specified Index setting, or null.



static P3dGroupData
GetGroupData
int index

This static method returns the P3dGroupData with the specified Index setting, or null.



static void
UpdateCachedInstances

This static method forces the cached instance list to update.

NOTE  This does nothing in-game.



P3dHashedMaterial

This struct can be used to reference a Material by instance or hash for de/serialization.



P3dHashedModel

This struct can be used to reference a Material by instance or hash for de/serialization.

NOTE  To support networking you must modify the P3dSerialization.TryRegister(P3dModel) method to register the model using a hash/id specific to your networking solution.


P3dHashedTexture

This struct can be used to reference a Texture by instance or hash for de/serialization.

NOTE  For the de/serialization to work you must call the P3dSerialization.TryRegister/TryUnregister methods on your textures.


P3dHit

This stores information about a scene point on a mesh. This is usually generated from a RaycastHit, but it can also be filled manually.


Vector3
Position

The world position that was hit.



Vector3
Normal

The world normal that was hit.



Transform
Transform

The Transform that was hit.



int
TriangleIndex

The triangle index that was hit.



float
Distance

The world distance that was hit.



Collider
Collider

The Collider that was hit.




P3dLineConnector

This class allows you to easily create components that can have their paint lines connected together to form quads.


float
HitSpacing

The world space distance between each paint point.

0 = No spacing.



int
HitLimit

When using HitSpacing, this prevents scenarios where something goes wrong and you attempt to paint too many times per frame.



bool
ConnectHits

If you enable this then the hit lines generated by this component will be connected into quads, allowing you to paint continuously.



bool
ClipConnected

If you enable ConnectHits, then each connected quad will overlap with the next. When using semi-transparent painting, this causes the overlap to become double opacity and look obvious. If you enable this setting, then this overlapping area will be removed.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.



void
ResetConnections

If this GameObject has teleported and you have ConnectHits or HitSpacing enabled, then you can call this to prevent a quad being drawn between the previous and current lines.




P3dMask

This component allows you to block paint from being applied at the current position using the specified shape.


Texture
Texture

The mask will use this texture shape.



P3dChannel
Channel

The mask will use pixels from this texture channel.



Vector2
Stretch

If you want the sides of the mask to extend farther out, then this allows you to set the scale of the boundary.

1 = Default.

2 = Double size.



static LinkedList<P3dMask>
Instances

This stores all active and enabled instances in the open scenes.




P3dMaterialCloner

This component allows you to duplicate a material before you paint on it. This is useful if the material is shared between multiple GameObjects (e.g. prefabs).


int
Index

The material index that will be cloned. This matches the Materials list in your MeshRenderer/SkinnedMeshRenderer, where 0 is the first material.



bool
Activated

This lets you know if this component has already been activated and has executed.



void
Activate

This allows you to manually activate this component, cloning the specified material.

NOTE  This will automatically be called from P3dPaintable to clone the material.


void
Deactivate

This reverses the material cloning.




P3dModel

This component marks the current GameObject as being paintable, as long as this GameObject has a MeshFilter + MeshRenderer, or a SkinnedMeshRenderer.

NOTE  To actually paint, the P3dPaintableTexture component must be on a different object.

P3dPaintable
Paintable

The paintable this separate paintable is associated with.



bool
IncludeScale

Transform the mesh with its position, rotation, and scale? Some skinned mesh setups require this to be disabled.



UseMeshType
UseMesh

This allows you to choose how the Mesh attached to the current Renderer is used when painting.

AsIs = Use what is currently set in the renderer.

AutoSeamFix = Use (or automatically generate) a seam-fixed version of the mesh currently set in the renderer.



P3dHash
Hash

The hash code for this model used for de/serialization of this instance.



static LinkedList<P3dModel>
Instances

This stores all active and enabled instances in the open scenes.



void
DirtyMaterials

Materials will give you a cached CachedRenderer.sharedMaterials array. If you have updated this array externally then call this to force the cache to update next them it's accessed.



static List<P3dModel>
FindOverlap
Vector3 position, float radius, int layerMask

This will return a list of all paintables that overlap the specified bounds




P3dModifier

This is the base class for all paint modifiers. To make a paint modifier, simply inherit this class, and implement one of the virtual methods to modify its data.


bool
Preview

Should this modifier apply to preview paint as well?



bool
Unique

Should this modifier use a unique seed?




P3dModifierList

This class maintains a list of P3dModifier instances, and contains helper methods to apply them.

This is used instead of a normal list so the modifiers can be de/serialized with polymorphism.


int
Count

The amount of modifiers in the list.



List<P3dModifier>
Instances

This stores all modifiers in this list.




P3dPaintable

This component marks the current GameObject as being paintable.

NOTE  This GameObject must has a MeshFilter + MeshRenderer, or a SkinnedMeshRenderer.

To actually paint your object, you must also add at least one P3dPaintableTexture component to specify which texture you want to paint.


ActivationType
Activation

This allows you to control when this component actually activates and becomes ready for painting. You probably don't need to change this.



Vector3
BaseScale

If you want the paintable texture width/height to be multiplied by the scale of this GameObject, this allows you to set the scale where you want the multiplier to be 1.



List<Renderer>
OtherRenderers

If this material is used in multiple renderers, you can specify them here. This usually happens with different LOD levels.



UnityEvent
OnActivating

This event will be invoked before this component is activated.



UnityEvent
OnActivated

This event will be invoked after this component is activated.



UnityEvent
OnDeactivating

This event will be invoked before this component is deactivated.



UnityEvent
OnDeactivated

This event will be invoked after this component is deactivated.



bool
Activated

This lets you know if this paintable has been activated.

Being activated means each associated P3dMaterialCloner and P3dPaintableTexture has been Activated.

NOTE  If you manually add P3dMaterialCloner or P3dPaintableTexture components after activation, then you must manually Activate().


HashSet<P3dPaintableTexture>
PaintableTextures

This gives you all P3dPaintableTexture components that have been activated.



void
RemoveComponents

This method will remove all P3dPaintable, P3dMaterialCloner, and P3dPaintableTexture components from this GameObject.



void
ScaleSize
ref int width, ref int height

This will scale the specified width and height values based on the current BaseScale setting.



void
Activate

This allows you to manually activate all attached P3dMaterialCloner and P3dPaintableTexture components.



void
Deactivate

This reverses the material cloning.



void
ClearAll
Color color

This allows you to clear the pixels of all activated P3dPaintableTexture components associated with this P3dPaintable with the specified color.



void
ClearAll
Texture texture, Color color

This allows you to clear the pixels of all activated P3dPaintableTexture components associated with this P3dPaintable with the specified color and texture.



void
Register
P3dPaintableTexture paintableTexture

This allows you to manually register a P3dPaintableTexture.



void
Unregister
P3dPaintableTexture paintableTexture

This allows you to manually unregister a P3dPaintableTexture.




P3dPaintableManager

This component automatically updates all P3dModel and P3dPaintableTexture instances at the end of the frame, batching all paint operations together.


static LinkedList<P3dPaintableManager>
Instances

This stores all active and enabled instances in the open scenes.




P3dPaintableState

This class stores information about a particular paintable texture state. Either a full texture copy, or a list of commands used to draw it.



P3dPaintableTexture

This component allows you to make one texture on the attached Renderer paintable.

NOTE  If the texture or texture slot you want to paint is part of a shared material (e.g. prefab material), then I recommend you add the P3dMaterialCloner component to make it unique.

P3dSlot
Slot

The material index and shader texture slot name that this component will paint.



P3dCoord
Coord

The UV channel this texture is mapped to.



P3dGroup
Group

The group you want to associate this texture with. Only painting components with a matching group can paint this texture. This allows you to paint multiple textures at the same time with different settings (e.g. Albedo + Normal).



UndoRedoType
UndoRedo

This allows you to set how this texture's state is stored, allowing you to perform undo and redo operations.

FullTextureCopy = A full copy of your texture will be copied for each state. This allows you to quickly undo and redo, and works with animated skinned meshes, but it uses up a lot of texture memory.

LocalCommandCopy = Each paint command will be stored in local space for each state. This allows you to perform unlimited undo and redo states with minimal memory usage, because the object will be repainted from scratch. However, performance will depend on how many states must be redrawn, and it may not work well with skinned meshes.



int
StateLimit

The amount of times this texture can have its paint operations undone.



SaveLoadType
SaveLoad

This allows you to control how this texture is loaded and saved.

Manual = You must manually call Save(SaveName) and Load(SaveName) from code.

Automatic = Save(SaveName) is called in Deactivate/OnDestroy, and Load(SaveName) is called in Activate.

SemiManual = You can manually call the Save() and Load() methods from code or editor event, and the current SaveName will be used.



string
SaveName

If you want this texture to automatically save & load, then you can set the unique save name for it here.

NOTE  This name should be unique, so this setting won't work properly with prefab spawning since all clones will share the same SaveName.


int
Width

The base width of the created texture.



int
Height

The base height of the created texture.



Texture
Texture

When activated or cleared, this paintable texture will be given this texture, and then multiplied/tinted by the Color.

None = White.



Color
Color

When activated or cleared, this paintable texture will be given this color.

NOTE  If Texture is set, then each pixel RGBA value will be multiplied/tinted by this color.


RenderTextureFormat
Format

The format of the created texture.



MipType
MipMaps

The useMipMap mode of the created texture.

Auto = Copied from the Texture.



FilterType
Filter

The filterMode of the created texture.

Auto = Copied from the Texture.



AnisoType
Aniso

The anisoLevel of the created texture.

Auto = Copied from the Texture.



WrapType
WrapU

The wrapModeU mode of the created texture.

Auto = Copied from the Texture.



WrapType
WrapV

The wrapModeV of the created texture.

Auto = Copied from the Texture.



ExistingType
Existing

If this component's Texture setting is none/null but the Slot texture is not none/null when this component activates, what should happen?

Ignore = Nothing will happen.

Use = This paintable texture will activate with the Slot texture.

UseAndKeep = The same as Use, but the Slot texture will also be stored in the Texture setting.



ConversionType
Conversion

If you're painting special textures then they may need to be converted before use.

Normal = Convert texture to be a normal map.

Premultiply = Premultiply the RGB values.



Texture
LocalMaskTexture

This allows you to restrict painting to specific areas.

NOTE  This mask texture should match the UV mapping of the texture itself.


P3dChannel
LocalMaskChannel

This allows you to specify which channel from the LocalMaskTexture texture will be used.



string
ShaderKeyword

Some shaders require specific shader keywords to be enabled when adding new textures. If there is no texture in your selected slot then you may need to set this keyword.



P3dHash
Hash

The hash code for this model used for de/serialization of this instance.



bool
IsDummy

If you want to paint a texture but don't want it to be applied to the specified material texture slot, then enable this.



string
Output

When you export this texture as a PNG asset from the in-editor painting window, the exported texture's GUID will be stored here so you can easily re-export it to the same file.



event System.Action<P3dCommand>
OnAddCommand

This event is called after a paint command has been added to this texture. These commands will be executed at the end of the frame.



static event System.Action<P3dPaintableTexture, P3dCommand>
OnAddCommandGlobal

This event is called after a paint command has been added to this texture. These commands will be executed at the end of the frame.



static System.Action<P3dPaintableTexture>
OnInstanceAdded

This is invoked every time a paintable texture component is enabled.



static System.Action<P3dPaintableTexture>
OnInstanceRemoved

This is invoked every time a paintable texture component is disabled.



event System.Action<bool>
OnModified

This event is called after this texture has been painted, allowing you to perform actions like counting the pixels after modification.

Bool = Preview painting.



static LinkedList<P3dPaintableTexture>
Instances

This stores all active and enabled instances in the open scenes.



bool
Activated

This lets you know if this texture is activated and ready for painting. Activation is controlled by the associated P3dPaintable component.



bool
CanUndo

This lets you know if there is at least one undo state this texture can be undone into.



bool
CanRedo

This lets you know if there is at least one redo state this texture can be redone into.



List<P3dPaintableState>
States

This property returns a list of all stored undo/redo states.



int
StateIndex

This tells you which undo/redo state is currently active inside the States list.



Material
Material

This tells you which material this paintable texture is currently stored in.

NOTE  You may have to call UpdateMaterial if this is out of date.


P3dPaintable
Paintable

This quickly gives you the P3dPaintable component associated with this paintable texture.



RenderTexture
Current

This gives you the current state of this paintable texture.

NOTE  This will only exist if your texture is activated.
NOTE  This is a RenderTexture, so you can't directly read it. Use the GetReadableCopy() method if you need to.
NOTE  This doesn't include any preview painting information, access the Preview property if you need to.


RenderTexture
Preview

This gives you the current state of this paintable texture including any preview painting information.



bool
CommandsPending

This lets you know if there are paint commands in this paintable texture's paint stack.



static List<P3dPaintableTexture>
FilterAll
P3dModel model, P3dGroup group

This allows you to get a list of all paintable textures on a P3dModel/P3dPaintable with the specified group.



void
ClearStates

This will clear all undo/redo texture states.



void
StoreState

This will store a texture state so that it can later be undone. This should be called before you perform texture modifications.



void
Undo

This will revert the texture to a previous state, if you have an undo state stored.



void
Redo

This will restore a previously undone texture state, if you've performed an undo.



void
SetColor
string html

This allows you to set the Color based on an HTML style string (#FFF, #FF0055, yellow, red)



void
NotifyOnModified
bool preview

You should call this after painting this paintable texture.



Texture2D
GetReadableCopy
bool convertBack = false

This method returns a Texture2D copy of the current texture state, allowing you to read pixel values, etc.

NOTE  This method can be slow if your texture is large.
NOTE  A new texture is allocated each time you call this, so you must manually delete it when finished.


byte[]
GetPngData
bool convertBack = false

This method returns the current texture state as a PNG byte array.

NOTE  This method can be slow if your texture is large.


byte[]
GetTgaData
bool convertBack = false

This method returns the current texture state as a TGA byte array.

NOTE  This method can be slow if your texture is large.


void
Clear

This method will clear the current texture state with the current Texture and Color values.

NOTE  This component must be activated, and this method will not resize the current texture.


void
Clear
Texture texture, Color tint, bool updateMips = true

This method will clear the current texture state with the specified texture and color.

NOTE  This component must be activated, and this method will not resize the current texture.


void
Replace

This method will replace the current texture state with the current Texture and Color values, including size.

NOTE  This component must be activated


void
Replace
Texture texture, Color tint

This method will resize the current texture state based on the specified texture, and then replace its contents with the specified texture and color.

NOTE  This component must be activated.


bool
Resize
int width, int height, bool copyContents = true

This method will resize the current texture state with the specified width and height.

NOTE  This component must be activated.


void
Save

This method will save the current texture state to PlayerPrefs using the current SaveName.



void
Save
string saveName

This will save the current texture state with the specified save name.



void
Load

This method will replace the current texture state with the data saved at SaveName.



void
Load
string saveName, bool replace = true

This method will replace the current texture state with the data saved at the specified save name.



void
LoadFromData
byte[] data, bool allowResize = true

This method will replace the current texture state with the specified image data (e.g. png).



void
HidePreview

If you last painted using preview painting and you want to hide the preview painting, you can call this method to force the texture to go back to its current state.



static void
HideAllPreviews

This automatically calls HidePreview on all active and enabled paintable textures.



void
ClearSave

This will clear save data with the current SaveName.



static void
ClearSave
string saveName

This will clear save data with the specified save name.



void
UpdateMaterial

If you modified the slot material index, then call this to update the cached material.



void
CopySize

If the current slot has a texture, this allows you to copy the width and height from it.



void
CopyTexture

This copies the texture from the current slot.



void
Activate

This allows you to manually activate this paintable texture.

NOTE  This will automatically be called by the associated P3dPaintable component when it activates.


void
AddCommand
P3dCommand command

This will add a paint command to this texture's paint stack. The paint stack will be executed at the end of the current frame.



void
ExecuteCommands
bool sendNotifications, bool doSort

This allows you to manually execute all commands in the paint stack.

This is useful if you need to modify the state of your object before the end of the frame.




P3dPaintableTextureMonitor

This base class allows you to quickly create components that listen for changes to the specified P3dPaintableTexture.


P3dPaintableTexture
PaintableTexture

This is the paintable texture whose pixels we will count.



float
Interval

This allows you to specify the minimum delay between each texture read in seconds.

0 = Instant.

1 = Once a second.



bool
Async

If you disable this, then the texture will be updated immediately, which may cause slowdown.

NOTE  This isn't supported on all devices.


int
DownsampleSteps

Testing all the pixels of a texture can be slow, so you can pick how many times the texture is downsampled. One downsample = half width & height or 1/4 of the pixels.

NOTE  The pixel totals will be multiplied to account for this downsampling.


event System.Action
OnUpdated

This event is invoked each time this texture monitor updates its pixel counts.



bool
Registered

This will be true after Register is successfully called.



void
Register

This forces the specified P3dPaintableTexture to be registered.



void
Unregister

This forces the specified P3dPaintableTexture to be unregistered.




P3dPaintableTextureMonitorMask

This base class allows you to quickly create components that listen for changes to the specified P3dPaintableTexture.


Mesh
MaskMesh

If you want this component to accurately count pixels relative to a mask mesh, then specify it here.

NOTE  For best results this should be the original mesh, NOT the seam-fixed version.


int
MaskSubmesh

If you have a MaskMesh set, then this allows you to choose which submesh of it will be used for the mask.



Texture
MaskTexture

If you want this component to accurately count pixels relative to a mask texture, then specify it here.



P3dChannel
MaskChannel

This allows you to specify which channel of the MaskTexture will be used to define the mask.



int
Total

The previously counted total amount of pixels.




P3dPointConnector

This class allows you to easily create components that can have their paint points connected together to form lines.


float
HitSpacing

The world space distance between each paint point.

0 = No spacing.



int
HitLimit

When using HitSpacing, this prevents scenarios where something goes wrong and you attempt to paint too many times per frame.



bool
ConnectHits

If you enable this then the hit points generated by this component will be connected into lines, allowing you to paint continuously.



bool
ClipConnected

If you enable ConnectHits, then each connected line will overlap with the next. When using semi-transparent painting, this causes the overlap to become double opacity and look obvious. If you enable this setting, then this overlapping area will be removed.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.



void
ResetConnections

If this GameObject has teleported and you have ConnectHits or HitSpacing enabled, then you can call this to prevent a line being drawn between the previous and current points.




P3dReadColor

This component allows you to read the paint color at a hit point. A hit point can be found using a companion component like: P3dHitScreen, P3dHitBetween.

NOTE  This component only works when you hit a non-convex MeshCollider that has UV data.

P3dGroup
Group

Only the P3dPaintableTexture components with a matching group will be painted by this component.



bool
Preview

Should the color be read during preview painting too?



ReadType
Read

How should the texture be read?

Immediate = The reading method will block until the pixel is fetched from the GPU.

Async = The pixel value will be read after some time, giving you better performance.



Color
Color

The last read color value.



ColorEvent
OnColor

When a color is read, this event will be invoked.

Color = The color that was read.




P3dReader

This class allows you to read the contents of a RenderTexture immediately or async.



P3dRenderDepth

This component renders scene depth to a RenderTexture. This scene depth can be set in a P3dPaint___ component's Advanced/DepthMask setting, which allows you to paint on the first surface in the view of the specified camera.


Camera
SourceCamera

The camera whose depth information will be read.



Matrix4x4
SourceMatrix

The transformation matrix of the camera when the depth texture was generated.



RenderTexture
TargetTexture

The RenderTexture where the depth information will be stored.



int
ResizeAndDownscale

If this is 0, the RenderTexture size will match the viewport. If it's above 0, then the RenderTexture size will be set to the viewport size divided by this value.



float
Bias

The rendered depth must be at least this mant units different from the painted surface for the paint to be masked out.



bool
ReadInStart

Should the scene depth be rendered in Start?



bool
ReadInUpdate

Should the scene depth be rendered every frame in Update?



void
ReadNow

This method will update the TargetTexture with what the SourceCamera currently sees.




P3dSerialization

This class handles the low level de/serialization of different paint in 3D objects to allow for things like networking.


static Dictionary<int, Material>
HashToMaterial

This stores an association between a Material hash code and the Material instance, so it can be de/serialized.



static Dictionary<Material, int>
MaterialToHash

This stores an association between a Material instance and the Material hash code, so it can be de/serialized.



static Dictionary<P3dHash, P3dModel>
HashToModel

This stores an association between a P3dModel hash code and the P3dModel instance, so it can be de/serialized.



static Dictionary<P3dModel, P3dHash>
ModelToHash

This stores an association between a P3dModel instance and the P3dModel hash code, so it can be de/serialized.



static Dictionary<P3dHash, Texture>
HashToTexture

This stores an association between a Texture hash code and the Texture instance, so it can be de/serialized.



static Dictionary<Texture, P3dHash>
TextureToHash

This stores an association between a Texture instance and the Texture hash code, so it can be de/serialized.



static Dictionary<P3dHash, P3dPaintableTexture>
HashToPaintableTexture

This stores an association between a P3dModel hash code and the P3dModel instance, so it can be de/serialized.



static Dictionary<P3dPaintableTexture, P3dHash>
PaintableTextureToHash

This stores an association between a P3dModel instance and the P3dModel hash code, so it can be de/serialized.




P3dSlot

This struct stores a reference to a texture on a GameObject.


int
Index

The material index in the attached renderer.



string
Name

The name of the texture in the specified material.




P3dStateManager

This component allows you to manage undo/redo states on all P3dPaintableTextures in your scene.


static void
StoreAllStates

This method will call StoreState on all active and enabled P3dPaintableTextures.



static void
ClearAllStates

This method will call StoreState on all active and enabled P3dPaintableTextures.



static void
UndoAll

This method will call Undo on all active and enabled P3dPaintableTextures.



static void
RedoAll

This method will call Redo on all active and enabled P3dPaintableTextures.




P3dTextureHash

This component allows you to manually associate a Texture with a hash code so it can be de/serialized.


Texture
Texture

The texture that will be hashed.



P3dHash
Hash

The hash code for the texture.




P3dCommandFill

This class manages the fill painting command.



P3dCommandReplace

This class manages the replace painting command.



P3dCommandReplaceChannels

This class manages the replace channels painting command.



P3dHitBetween

This component raycasts between two points, and fires hit events when the ray hits something.


PhaseType
PaintIn

Where in the game loop should this component hit?



float
Interval

The time in seconds between each raycast.

0 = Every frame.

-1 = Manual only.



Transform
PointA

The start point of the raycast.



Transform
PointB

The end point of the raycast.



float
Fraction

The end point of the raycast.



LayerMask
Layers

The layers you want the raycast to hit.



OrientationType
Orientation

How should the hit point be oriented?

WorldUp = It will be rotated to the normal, where the up vector is world up.

CameraUp = It will be rotated to the normal, where the up vector is world up.

ThisRotation = The current Transform.rotation will be used.

ThisLocalRotation = The current Transform.localRotation will be used.

CustomRotation = The specified CustomTransform.rotation will be used.

CustomLocalRotation = The specified CustomTransform.localRotation will be used.



Camera
Camera

Orient to a specific camera?

None = MainCamera.



Transform
CustomTransform

If you use Orientation = CustomRotation/CustomLocalRotation, this allows you to set the transform.



NormalType
Normal

Which normal should the hit point rotation be based on?



float
Offset

If you want the raycast hit point to be offset from the surface a bit, this allows you to set by how much in world space.



bool
Preview

Should the applied paint be applied as a preview?



int
Priority

This allows you to override the order this paint gets applied to the object during the current frame.



float
Pressure

This allows you to control the pressure of the painting. This could be controlled by a VR trigger or similar for more advanced effects.



EmitType
Draw

This allows you to control the hit data this component sends out.

PointsIn3D = Point drawing in 3D.

PointsOnUV = Point drawing on UV (requires non-convex MeshCollider).

TrianglesIn3D = Triangle drawing in 3D.



Transform
Point

If you want to display something at the hit point (e.g. particles), you can specify the Transform here.



LineRenderer
Line

If you want to draw a line between the start point and the his point then you can set the line here.



P3dPointConnector
Connector

This allows you to connect the hit points together to form lines.



void
ManuallyHitNow

This method will immediately submit a non-preview hit. This can be used to apply real paint to your objects.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.



void
ResetConnections

If this GameObject has teleported and you have ConnectHits or HitSpacing enabled, then you can call this to prevent a line being drawn between the previous and current points.




P3dHitCache

This class stores lists of IHit__ instances, allowing components like P3dHit__ to easily invoke hit events.



P3dHitCollisions

This component can be added to any Rigidbody, and it will fire hit events when it hits something.


EmitType
Emit

This allows you to control the hit data this component sends out.

PointsIn3D = Point drawing in 3D.

PointsOnUV = Point drawing on UV (requires non-convex MeshCollider).

TrianglesIn3D = Triangle drawing in 3D.



float
RaycastDistance

When emitting PointsOnUV or TrianglesIn3D, this setting allows you to specify the world space distance from the hit point a raycast will be fired. This is necessary because collisions by themselves don't provide the necessary information.

NOTE  Performing this raycast has a slight performance penalty.


LayerMask
Layers

This allows you to filter collisions to specific layers.



bool
OnlyUseFirstContact

If there are multiple contact points, skip them?



float
Delay

If this component is generating too many hits, then you can use this setting to ignore hits for the specified amount of seconds.

0 = Unlimited.



OrientationType
Orientation

How should the hit point be oriented?

WorldUp = It will be rotated to the normal, where the up vector is world up.

CameraUp = It will be rotated to the normal, where the up vector is world up.



Camera
Camera

Orient to a specific camera?

None = MainCamera.



bool
Preview

Should the applied paint be applied as a preview?



float
Threshold

If the collision impact speed is below this value, then the collision will be ignored.



PressureType
PressureMode

This allows you to set how the pressure value will be calculated.

Constant = The PressureConstant value will be directly used.

ImpactSpeed = The pressure will be 0 when the collision impact speed is PressureMin, and 1 when the impact speed is or exceeds PressureMax.



float
PressureMin

The impact strength required for a hit to occur with a pressure of 0.



float
PressureMax

The impact strength required for a hit to occur with a pressure of 1.



float
PressureConstant

The pressure value used when PressureMode is set to Constant.



float
PressureMultiplier

The calculated pressure value will be multiplied by this.



float
Offset

If you want the raycast hit point to be offset from the surface a bit, this allows you to set by how much in world space.



int
Priority

This allows you to override the order this paint gets applied to the object during the current frame.



GameObject
Root

Hit events are normally sent to all components attached to the current GameObject, but this setting allows you to override that. This is useful if you want to use multiple P3dHitCollisions components with different settings and results.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.




P3dHitNearby

This component continuously fires hit events using the current Transform position.


PhaseType
PaintIn

Where in the game loop should this component hit?



float
Interval

The time in seconds between each hit.

0 = Every frame.



bool
Preview

Should the applied paint be applied as a preview?



int
Priority

This allows you to override the order this paint gets applied to the object during the current frame.



float
Pressure

This allows you to control the pressure of the painting. This could be controlled by a VR trigger or similar for more advanced effects.



P3dPointConnector
Connector

This allows you to connect the hit points together to form lines.



void
ManuallyHitNow

This method will immediately submit a non-preview hit. This can be used to apply real paint to your objects.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.



void
ResetConnections

If this GameObject has teleported and you have ConnectHits or HitSpacing enabled, then you can call this to prevent a line being drawn between the previous and current points.




P3dHitParticles

This component can be added to any ParticleSystem with collisions enabled, and it will fire hits when the particles collide with something.


EmitType
Emit

This allows you to control the hit data this component sends out.

PointsIn3D = Point drawing in 3D.

PointsOnUV = Point drawing on UV (requires non-convex MeshCollider).

TrianglesIn3D = Triangle drawing in 3D.



float
RaycastDistance

When emitting PointsOnUV or TrianglesIn3D, this setting allows you to specify the world space distance from the hit point a raycast will be fired. This is necessary because particles by themselves don't provide the necessary information.

NOTE  Performing this raycast has a slight performance penalty.


LayerMask
Layers

This allows you to filter collisions to specific layers.



OrientationType
Orientation

How should the hit point be oriented?

WorldUp = It will be rotated to the normal, where the up vector is world up.

CameraUp = It will be rotated to the normal, where the up vector is world up.



Camera
Camera

Orient to a specific camera?

None = MainCamera.



NormalType
Normal

Which normal should the hit point rotation be based on?



float
Offset

If you want the raycast hit point to be offset from the surface a bit, this allows you to set by how much in world space.



int
Skip

If you have too many particles, then painting can slow down. This setting allows you to reduce the amount of particles that actually cause hits.

0 = Every particle will hit.

5 = Skip 5 particles, then hit using the 6th.



bool
Preview

Should the particles paint preview paint?



int
Priority

This allows you to override the order this paint gets applied to the object during the current frame.



PressureType
PressureMode

This allows you to set how the pressure value will be calculated.

Constant = The PressureConstant value will be directly used.

Distance = A value will be calculated based on the distance between this emitter and the particle hit point.

Speed = A value will be calculated based on the hit speed of the particle.



float
PressureMin

This allows you to specify the distance/speed that gives 0.0 pressure.



float
PressureMax

This allows you to specify the distance/speed that gives 1.0 pressure.



float
PressureConstant

The pressure value used when PressureMode is set to Constant.



float
PressureMultiplier

The calculated pressure value will be multiplied by this.



GameObject
Root

Hit events are normally sent to all components attached to the current GameObject, but this setting allows you to override that. This is useful if you want to use multiple P3dHitParticles components with different settings and results.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.




P3dHitScreen

This component will perform a raycast under the mouse or finger as it moves across the screen. It will then send hit events to components like P3dPaintDecal, allowing you to paint the scene.


FrequencyType
Frequency

This allows you to control how often the screen is painted.

PixelInterval = Once every Interval pixels.

ScaledPixelInterval = Like PixelInterval, but scaled to the screen DPI.

TimeInterval = Once every Interval seconds.

OnceOnRelease = When the finger/mouse goes down a preview will be shown, and when it goes up the paint will apply.

OnceOnPress = When the finger/mouse goes down the paint will apply.

OnceEveryFrame = Every frame the paint will apply.



float
Interval

This allows you to set the pixels/seconds between each hit point based on the current Frequency setting.



P3dPointConnector
Connector

This allows you to connect the hit points together to form lines.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.



void
ResetConnections

If this GameObject has teleported and you have ConnectHits or HitSpacing enabled, then you can call this to prevent a line being drawn between the previous and current points.




P3dHitScreenBase

This class contains common code for screen based mouse/finger hit components.


Camera
Camera

Orient to a specific camera?

None = MainCamera.



LayerMask
Layers

The layers you want the raycast to hit.



LayerMask
GuiLayers

Fingers that began touching the screen on top of these UI layers will be ignored.



EmitType
Emit

This allows you to control the hit data this component sends out.

PointsIn3D = Point drawing in 3D.

PointsOnUV = Point drawing on UV (requires non-convex MeshCollider).

TrianglesIn3D = Triangle drawing in 3D (requires non-convex MeshCollider).



RotationType
RotateTo

This allows you to control how the paint is rotated.

Normal = The rotation will be based on a normal direction, and rolled relative to an up axis.

World = The rotation will be aligned to the world, or given no rotation.

ThisRotation = The current Transform.rotation will be used.

ThisLocalRotation = The current Transform.localRotation will be used.

CustomRotation = The specified CustomTransform.rotation will be used.

CustomLocalRotation = The specified CustomTransform.localRotation will be used.



DirectionType
NormalDirection

Which direction should the hit point rotation be based on?



RelativeType
NormalRelativeTo

Based on the normal direction, what should the rotation be rolled relative to?

WorldUp = It will be rolled so the up vector is world up.

CameraUp = It will be rolled so the up vector is camera up.

DrawAngle = It will be rolled according to the mouse/finger movement on screen.



Transform
CustomTransform

This allows you to specify the Transform when using RotateTo = CustomRotation/CustomLocalRotation.



bool
StoreStates

Should painting triggered from this component be eligible for being undone?



int
Priority

This allows you to override the order this paint gets applied to the object during the current frame.




P3dHitScreenFill

This component works like P3dHitScreen, but it will fill in the shape you draw.


float
FillSpacing

This allows you to set the pixel distance between each grid point.

NOTE  The lower you set this, the lower the performance will be.



P3dHitScreenLine

This component will perform a raycast under the mouse or finger as it moves across the screen. It will then send hit events to components like P3dPaintDecal, allowing you to paint the scene.


FrequencyType
Frequency

This allows you to control how many hit points will be generated along the drawn line.

StartAndEnd = Once at the start, and once at the end.

PixelInterval = Once at the start, and then every Interval pixels.

ScaledPixelInterval = Once at the start, and then every Interval scaled pixels.

StretchedPixelInterval = Like ScaledPixelInterval, but the hits are stretched to reach the end.

StretchedScaledPixelInterval = Like ScaledPixelInterval, but the hits are stretched to reach the end.

Once = Once at the specified Position and PixelOffset along the line.



float
Interval

This allows you to set the pixels between each hit point based on the current Frequency setting.



float
Position

When using Frequency = Once, this allows you to set the 0..1 position along the line.



float
PixelOffset

When using Frequency = Once, this allows you to set the pixel offset along the line.



P3dPointConnector
Connector

This allows you to connect the hit points together to form lines.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.



void
ResetConnections

If this GameObject has teleported and you have ConnectHits or HitSpacing enabled, then you can call this to prevent a line being drawn between the previous and current points.




P3dHitThrough

This component constantly draws lines between the two specified points.


PhaseType
PaintIn

Where in the game loop should this component hit?



float
Interval

The time in seconds between each hit.

0 = Every frame.

-1 = Manual only.



Transform
PointA

The start point of the raycast.



Transform
PointB

The end point of the raycast.



OrientationType
Orientation

How should the hit point be oriented?

WorldUp = It will be rotated to the normal, where the up vector is world up.

CameraUp = It will be rotated to the normal, where the up vector is world up.



Camera
Camera

Orient to a specific camera?

None = MainCamera.



float
Pressure

This allows you to control the pressure of the painting. This could be controlled by a VR trigger or similar for more advanced effects.



bool
Preview

Should the applied paint be applied as a preview?



int
Priority

This allows you to override the order this paint gets applied to the object during the current frame.



LineRenderer
Line

If you want to draw a line between the start point and the his point then you can set the line here.



P3dLineConnector
Connector

This allows you to connect the hit points together to form lines.



void
ManuallyHitNow

This method will immediately submit a non-preview hit. This can be used to apply real paint to your objects.



void
ClearHitCache

This component sends hit events to a cached list of components that can receive them. If this list changes then you must manually call this method.



void
ResetConnections

If this GameObject has teleported and you have ConnectHits or HitSpacing enabled, then you can call this to prevent a line being drawn between the previous and current points.




P3dMeshAnalysis

This window allows you to examine the UV data of a mesh. This can be accessed from the context menu (⋮ button at top right) of any mesh in the inspector.



P3dPreset

This component allows you to define a set of P3dPaintableTexture and P3dMaterial components that are configured for a specific set of Materials.


string
Title

This allows you to name this preset.

None/null = The GameObject name will be used.



bool
AddMaterialCloner

Automatically add the P3dMaterialCloner.



static List<P3dPreset>
CachedPresets

This gives you a list of all presets in the project.

NOTE  This is editor-only.


bool
Targets
Shader target

This method returns true if this preset is designed for the specified shader.



void
AddTo
P3dPaintable paintable, Shader shader, int index, int stateLimit

This method applies the preset components to the specified paintable.

NOTE  This is editor-only.



P3dSeamFixer

This tool allows you to convert a normal mesh with UV seams to a fixed mesh without UV seams.

This tool can be accessed from the context menu (⋮ button at top right) of any mesh/model inspector.


Mesh
Source

The original mesh.



Mesh
Output

The mesh with fixed seams.



List<Pair>
Meshes

The meshes we will fix the seams of.



P3dCoord
Coord

The UV channel whose seams will be fixed.



float
Threshold

The threshold below which vertex UV coordinates will be snapped.



float
Border

The thickness of the UV borders in the fixed mesh.



static float
DebugScale

If this is above 0 then Debug.Lines will be output during generation.



void
AddMesh
Mesh mesh

This allows you to add a mesh to the seam fixer.

NOTE  You must later call Generate to seam fix the added meshes.


static void
Generate
Mesh source, Mesh output, P3dCoord coord, float threshold, float border

This static method allows you to fix the seams of the source mesh at runtime.




P3dModifyAngleRandom

This class allows you to randomize the painting angle of the attached component (e.g. P3dPaintDecal).


float
Min

This is the minimum random angle that will be picked.



float
Max

This is the maximum random angle that will be picked.



BlendType
Blend

The way the picked angle value will be blended with the current one.




P3dModifyColorRandom

This class allows you to randomize the painting color of the attached component (e.g. P3dPaintDecal).


Gradient
Gradient

This is the gradient containing all the possible colors. A color will be randomly picked from this.



BlendType
Blend

The way the picked color value will be blended with the current one.




P3dModifyHardnessPressure

This class allows you to change the painting hardness based on the paint pressure.


float
Hardness

The paint component's Hardness value will be modified using this value based on the current Blend setting.



BlendType
Blend

This allows you to control how this new Hardness value will modify the old value in the paint component.

Replace = Transition between [old, new] based on pressure.

Multiply = Transition between [old, old*new] based on pressure.

Increment = Transition between [old, old+new] based on pressure.




P3dModifyHardnessRandom

This class allows you to randomize the painting hardness of the attached component (e.g. P3dPaintDecal).


float
Min

This is the minimum random hardness that will be picked.



float
Max

This is the maximum random hardness that will be picked.



BlendType
Blend

The way the picked hardness value will be blended with the current one.




P3dModifyOpacityPressure

This class allows you to change the painting opacity based on the paint pressure.


float
Opacity

The paint component's Opacity value will be modified using this value based on the current Blend setting.



BlendType
Blend

This allows you to control how this new Opacity value will modify the old value in the paint component.

Replace = Transition between [old, new] based on pressure.

Multiply = Transition between [old, old*new] based on pressure.

Increment = Transition between [old, old+new] based on pressure.




P3dModifyOpacityRandom

This class allows you to randomize the painting opacity of the attached component (e.g. P3dPaintDecal).


float
Min

This is the minimum random opacity that will be picked.



float
Max

This is the maximum random opacity that will be picked.



BlendType
Blend

The way the picked opacity value will be blended with the current one.




P3dModifyPositionRandom

This class allows you to randomize the painting position of the attached component (e.g. P3dPaintDecal).


float
Radius

The position will be offset up to this radius away in world space.




P3dModifyRadiusPressure

This class allows you to change the painting radius based on the paint pressure.


float
Radius

The paint component's Radius value will be modified using this value based on the current Blend setting.



BlendType
Blend

This allows you to control how this new Radius value will modify the old value in the paint component.

Replace = Transition between [old, new] based on pressure.

Multiply = Transition between [old, old*new] based on pressure.

Increment = Transition between [old, old+new] based on pressure.




P3dModifyRadiusRandom

This class allows you to randomize the painting radius of the attached component (e.g. P3dPaintDecal).


float
Min

This is the minimum random radius that will be picked.



float
Max

This is the maximum random radius that will be picked.



BlendType
Blend

The way the picked radius value will be blended with the current one.




P3dModifyScaleRandom

This class allows you to randomize the painting scale of the attached component (e.g. P3dPaintDecal).


Vector3
Min

This is the minimum random scale that will be picked.



Vector3
Max

This is the maximum random scale that will be picked.



BlendType
Blend

The way the picked scale value will be blended with the current one.



bool
Uniform

If you disable this then each x, y, and z value will be scaled separately.




P3dModifyTexturePressure

This class allows you to change the painting texture of the attached component (e.g. P3dPaintDecal) based on the paint pressure.


Texture
Texture

The painting texture will be changed to this.



float
PressureMin

The paint pressure must be at least this value.



float
PressureMax

The paint pressure must be at most this value.




P3dModifyTextureRandom

This class allows you to randomize the painting texture of the attached component (e.g. P3dPaintDecal).


List<Texture>
Textures

A random texture will be picked from this list.




P3dPaintFill

This component implements the fill paint mode, which will modify all pixels in the specified texture in the same way.

This is useful if you want to gradually fade a texture to a specific color.


P3dGroup
Group

Only the P3dPaintableTexture components with a matching group will be painted by this component.



P3dBlendMode
BlendMode

This allows you to choose how the paint from this component will combine with the existing pixels of the textures you paint.

NOTE  See the Blend Mode section of the documentation for more information.


Texture
Texture

The color of the paint.



Color
Color

The color of the paint.



float
Opacity

The opacity of the brush.



float
Minimum

The minimum RGBA value change. This is useful if you're doing very subtle color changes over time.



P3dModifierList
Modifiers

This stores a list of all modifiers used to change the way this component applies paint (e.g. P3dModifyColorRandom).



void
IncrementOpacity
float delta

This method increments Opacity by the specified value.




P3dPaintReplace

This component implements the replace paint mode, which will replace all pixels in the specified texture.


P3dGroup
Group

Only the P3dPaintableTexture components with a matching group will be painted by this component.



Texture
Texture

The texture that will be painted.



Color
Color

The color of the paint.



P3dModifierList
Modifiers

This stores a list of all modifiers used to change the way this component applies paint (e.g. P3dModifyColorRandom).




P3dPaintReplaceChannels

This component implements the replace channels paint mode, which will replace all pixels in the specified textures and channel weights.


P3dGroup
Group

Only the P3dPaintableTexture components with a matching group will be painted by this component.




P3dPointer

This this is the base class for any component that sends pointer information to any P3dHitScreen component.



P3dPointerMouse

This component sends pointer information to any P3dHitScreen component, allowing you to paint with the mouse.


bool
Preview

If you enable this, then a paint preview will be shown under the mouse as long as the RequiredKey is not pressed.



List<KeyCode>
Keys

This component will paint while any of the specified mouse buttons or keyboard keys are held.




P3dPointerPen

This component sends pointer information to any P3dHitScreen component, allowing you to paint with a pen.


bool
Preview

If you enable this, then a paint preview will be shown under the pen as long as the tip is not pressed.



float
Offset

If you want the paint to appear above the pen, then you can set this number to something positive.




P3dPointerTouch

This component sends pointer information to any P3dHitScreen component, allowing you to paint with a touchscreen.


float
Offset

If you want the paint to appear above the finger, then you can set this number to something positive.




P3dButtonClearAll

This component allows you to perform the Clear action. This can be done by attaching it to a clickable object, or manually from the ClearAll method.


bool
ClearStates

When clearing a texture, should its undo states be cleared too?




P3dButtonRedoAll

This component allows you to perform the Redo All action. This can be done by attaching it to a clickable object, or manually from the RedoAll method.


void
RedoAll

If you want to manually trigger RedoAll, then call this function.




P3dButtonUndoAll

This component allows you to perform the Undo All action. This can be done by attaching it to a clickable object, or manually from the RedoAll method.


void
UndoAll

If you want to manually trigger UndoAll, then call this function.




P3dChangeCounterEvent

This component allows you to perform an event when the specified P3dChangeCounter instances are painted a specific amount.


List<P3dChangeCounter>
Counters

This allows you to specify the counters that will be used.

None = All active and enabled counters in the scene.



Vector2
Range

This paint ratio must be inside this range to be considered inside.



bool
Inside

This tells you if the paint ratio is within the current Range.



UnityEvent
OnInside

This event will be called on the first frame Inside becomes true.



UnityEvent
OnOutside

This event will be called on the first frame Inside becomes false.



float
Ratio

This tells you the current paint ratio of the specified Color, where 0 is no paint, and 1 is fully painted.




P3dChangeCounterFill

This component fills the attached UI Image based on the total amount of pixels that have been painted in the specified P3dChangeCounterFill components.


List<P3dChangeCounter>
Counters

This allows you to specify the counters that will be used.

Zero = All active and enabled counters in the scene.



bool
Inverse

Inverse the fill?




P3dChangeCounterText

This component will output the total pixels for the specified team to a UI Text component.


List<P3dChangeCounter>
Counters

This allows you to specify the counters that will be used.

Zero = All active and enabled counters in the scene.



bool
Inverse

Inverse the Count and Percent values?



int
DecimalPlaces

This allows you to set the amount of decimal places when using the percentage output.



string
Format

This allows you to set the format of the team text. You can use the following tokens:

{TOTAL} = Total amount of pixels that can be painted.

{COUNT} = Total amount of pixel that have been painted.

{PERCENT} = Percentage of pixels that have been painted.



StringEvent
OnString

The color count will be output via this event.




P3dChannelCounterEvent

This component allows you to perform an event when the specified P3dChannelCounter instances are painted a specific amount.


List<P3dChannelCounter>
Counters

This allows you to specify the counters that will be used.

None = All active and enabled counters in the scene.



ChannelType
Channel

This allows you to choose which channel will be output to the UI Text.



Vector2
Range

This paint ratio must be inside this range to be considered inside.



bool
Inside

This tells you if the paint ratio is within the current Range.



UnityEvent
OnInside

This event will be called on the first frame Inside becomes true.



UnityEvent
OnOutside

This event will be called on the first frame Inside becomes false.



float
Ratio

This tells you the current paint ratio of the specified Channel, where 0 is no paint, and 1 is fully painted.




P3dChannelCounterFill

This component fills the attached UI Image based on the total amount of opaque pixels that have been painted in all active and enabled P3dChannelCounter components in the scene.


List<P3dChannelCounter>
Counters

This allows you to specify the counters that will be used.

Zero = All active and enabled counters in the scene.



ChannelType
Channel

This allows you to choose which channel will be output to the UI Image.



bool
Inverse

Inverse the fill?




P3dChannelCounterText

This component allows you to output the totals of all the specified pixel counters to a UI Text component.


List<P3dChannelCounter>
Counters

This allows you to specify the counters that will be used.

Zero = All active and enabled counters in the scene.



ChannelType
Channel

This allows you to choose which channel will be output to the UI Text.



bool
Inverse

Inverse the Count and Percent values?



int
DecimalPlaces

This allows you to set the amount of decimal places when using the percentage output.



string
Format

This allows you to set the format of the team text. You can use the following tokens:

{TOTAL} = Total amount of pixels that can be painted.

{COUNT} = Total amount of pixel that have been painted.

{PERCENT} = Percentage of pixels that have been painted.



StringEvent
OnString

The color count will be output via this event.




P3dColorCounterEvent

This component allows you to perform an event when the specified P3dColorCounter instances are painted a specific amount.


List<P3dColorCounter>
Counters

This allows you to specify the counters that will be used.

None = All active and enabled counters in the scene.



P3dColor
Color

This allows you to set which color will be handled by this component.



Vector2
Range

This paint ratio must be inside this range to be considered inside.



bool
Inside

This tells you if the paint ratio is within the current Range.



UnityEvent
OnInside

This event will be called on the first frame Inside becomes true.



UnityEvent
OnOutside

This event will be called on the first frame Inside becomes false.



float
Ratio

This tells you the current paint ratio of the specified Color, where 0 is no paint, and 1 is fully painted.




P3dColorCounterFill

This component fills the attached UI Image based on the total amount of pixels that have been painted in the specified P3dColorCounter components.


List<P3dColorCounter>
Counters

This allows you to specify the counters that will be used.

Zero = All active and enabled counters in the scene.



P3dColor
Color

This allows you to set which color will be handled by this component.



bool
Inverse

Inverse the fill?




P3dColorCounterText

This component will output the total pixels for the specified color to the OnString event.


List<P3dColorCounter>
Counters

This allows you to specify the counters that will be used.

Zero = All active and enabled counters in the scene.



P3dColor
Color

This allows you to set which color will be handled by this component.



bool
Inverse

Inverse the Count and Percent values?



int
DecimalPlaces

This allows you to set the amount of decimal places when using the percentage output.



string
Format

This allows you to set the format of the team text. You can use the following tokens:

{TOTAL} = Total amount of pixels that can be painted.

{COUNT} = Total amount of pixel that have been painted.

{PERCENT} = Percentage of pixels that have been painted.



StringEvent
OnString

The color count will be output via this event.




P3dPaintDebug

This component allows you to debug hit points into the Scene tab. Hit points will automatically be sent by any P3dHit___ component on this GameObject, or its ancestors.


Color
Color

The color of the debug.



float
Duration

The duration of the debug.



float
Size

The size of the debug.




P3dSpawner

This allows you to spawn a prefab at a hit point. Hit points will automatically be sent by any P3dHit___ component on this GameObject, or its ancestors.


List<GameObject>
Prefabs

A random prefab from this list will be spawned.



float
Radius

The spawned prefab will be randomly offset by a random point within this radius in world space.



Vector3
Velocity

If the prefab contains a Rigidbody, it will be given this velocity in local space.



float
OffsetNormal

The spawned prefab will be offset from the hit point based on the hit normal by this value in world space.



Vector3
OffsetWorld

The spawned prefab will be offset from the hit point based on this value in world space.



void
Spawn

Call this if you want to manually spawn the specified prefab.




P3dActionOnEnable

This component invokes the Action event when this component is enabled.


UnityEvent
Action

The event that will be invoked.




P3dCommandSerialization

This component shows you how to listen for and store paint commands added to any P3dPaintableTexture component in the scene.

This component can then reset each paintable texture, and randomly apply one of the recorded paint commands.

NOTE  For a paint command to be able to be de/serialized, all P3dModel, P3dPaintable, P3dPaintableTexture, and Texture instance associated with the paint command must be registered with a unique hash code that will be the same across all application runs and clients.
NOTE  The hash codes for the P3d___ components can be set using the Advanced/Hash setting, but the Texture instances must be done separately using either the P3dTextureHash component, or manual calls to P3dSerialization.TryRegister(texture) before you attempt to de/serialize a paint command.

bool
Listening

Should this component listen for added commands?



void
Clear

This method will pool and clear all commands.



void
RebuildRandomCommand

This method will clear all paintable textures, and apply one random paint command that was recorded.




P3dDestroyAfterTime

This component automatically destroys this GameObject after some time.


float
Seconds

If this component has been active for this many seconds, the current GameObject will be destroyed.

-1 = DestroyNow must be manually called.




P3dDestroyer

This component automatically destroys the specified GameObject when sent a hit point. Hit points will automatically be sent by any P3dHit___ component on this GameObject, or its ancestors.


GameObject
Target

This GameObject will be destroyed.




P3dDragPitchYaw

This component adds basic Pitch/Yaw controls to the current GameObject (e.g. camera) using mouse or touch controls.


Transform
Tools

Rotation will be active if all of these tools are deactivated.



KeyCode
Key

The key that must be held for this component to activate on desktop platforms.

None = Any mouse button.



LayerMask
GuiLayers

Fingers that began touching the screen on top of these UI layers will be ignored.



float
Pitch

The target pitch angle in degrees.



float
PitchSensitivity

The speed the pitch changed relative to the mouse/finger drag distance.



float
PitchMin

The minimum value of the pitch value.



float
PitchMax

The maximum value of the pitch value.



float
Yaw

The target yaw angle in degrees.



float
YawSensitivity

The speed the yaw changed relative to the mouse/finger drag distance.



float
Dampening

How quickly the rotation transitions from the current to the target value (-1 = instant).




P3dInputAxis

This component allows you to convert input axis values to a boolean event. This can be used to map VR buttons to other components.


string
AxisName

The name of the input axis in the Project Settings.



int
AxisIndex

The index of the input axis in the Project Settings.




P3dPaintFromCode

This component shows you how to paint from code.



P3dPaintMultiplayer

This component listens for point and line painting events. It then simulates transmitting them over a network with a delay, and then painting the received data.


float
Delay

This allows you to specify the simulated delay between painting across the network in seconds.




P3dProceduralSetup

This component can be added to an empty GameObject, and it will set it up with a procedurally generated quad that is ready for painting.


Material
Material

The Material applied to the renderer.



float
Size

The size of the generated quad in local space.




P3dReadColorEvent

This component allows you to perform an event when the attached P3dReadColor component reads a specific color.


Color
Color

This color we want to detect.



float
Threshold

The RGBA values must be within this range of a color for it to be counted.



ColorEvent
OnColor

When the expected color is read, this event will be invoked.

Color = The expected color.




P3dRotate

This component allows you to rotate the current Transform.


Space
Space

This allows you to set the coordinate space the movement will use.



Vector3
PerSecond

The position will be incremented by this each second.




P3dSpiral

This component moves the current Transform in a spiral pattern.



P3dTapThrow

This component will spawn and throw Rigidbody prefabs from the camera when you tap the mouse or a finger.


KeyCode
Key

The key that must be held for this component to activate on desktop platforms.

None = Any mouse button.



LayerMask
GuiLayers

Fingers that began touching the screen on top of these UI layers will be ignored.



GameObject
Prefab

The prefab that will be thrown.



float
Speed

The speed that the object will be thrown at.



bool
StoreStates

Should painting triggered from this component be eligible for being undone?




P3dToggleParticles

This component enables or disables the specified ParticleSystem based on mouse or finger presses.


LayerMask
GuiLayers

Fingers that began touching the screen on top of these UI layers will be ignored.



KeyCode
Key

The key that must be held for this component to activate.

None = Any mouse button or finger.



ParticleSystem
Target

The particle system that will be enabled/disabled based on mouse/touch.



bool
StoreStates

Should painting triggered from this component be eligible for being undone?




P3dToggleScript

This component allows you to enable/disable the target component while the specified key is held down.


KeyCode
Key

The key that must be held for this component to activate.

None = Any mouse button or finger.



MonoBehaviour
Target

The component that will be enabled or disabled.



bool
StoreStates

Should painting triggered from this component be eligible for being undone?




P3dTranslate

This component allows you to move the current Transform using editor events (e.g. UI buttons).


Space
Space

This allows you to set the coordinate space the movement will use.



float
Multiplier

The movement values will be multiplied by this before use.



float
Damping

If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value.

-1 = Instantly change.

1 = Slowly change.

10 = Quickly change.



Vector3
PerSecond

The position will be incremented by this each second.



void
TranslateX
float magnitude

This method allows you to translate along the X axis, with the specified value.



void
TranslateY
float magnitude

This method allows you to translate along the Y axis, with the specified value.



void
TranslateZ
float magnitude

This method allows you to translate along the Z axis, with the specified value.



void
Translate
Vector3 vector

This method allows you to translate along the specified vector.



void
TranslateWorld
Vector3 vector

This method allows you to translate along the specified vector in world space.




P3dVrManager

This component attached the current GameObject to a tracked hand.


// KeyCode
RecenterKey

This key allows you to reset the VR orientation.



float
GrabDistance

The default distance in world space a hand must be to grab a tool.



KeyCode
SimulatedLeftTrigger

This key allows you to simulate a left hand VR trigger.



KeyCode
SimulatedLeftGrip

This key allows you to simulate a left hand VR grip.



KeyCode
SimulatedRightTrigger

This key allows you to simulate a right hand VR trigger.



KeyCode
SimulatedRightGrip

This key allows you to simulate a right hand VR grip.



Vector3
SimulatedTilt

When simulating a VR tool, it will be offset by this Euler rotation.



Vector3
SimulatedOffset

When simulating a VR tool, it will be offset by this local position.



float
SimulatedDistanceMax

When simulating a VR tool, it will be moved away from the hit surface by this.



Vector3
SimulatedEyeOffset

The simulated left VR eye will be offset this much.



float
SimulatedNormalInfluence

When simulating a VR tool, this will control how much the hit surface normal influences the tool rotation.



static LinkedList<P3dVrManager>
Instances

This stores all active and enabled instances in the open scenes.




P3dVrTool

This component attached the current GameObject to a tracked hand.


XRNode
Node

The XR node this GameObject will follow.



bool
StoreStates

Should painting triggered from this component be eligible for being undone?



Vector3
LocalOffset

This tool will be offset by this vector in local space.



Vector3
SimulatedOffset

When simulating a VR tool, it will be offset by this local position.



Vector3
SimulatedKeyOffset

The SimulatedOffset value will be offset by this when the simulated key is held.



float
SimulatedDampening

This allows you to control the speed of the simulated transform changes.



void
Drop

This will drop the current tool.



void
DropAndGrabNextTool

This will drop the current tool and grab the next in the scene.



static List<P3dVrTool>
GetTools
XRNode node

This method allows you to find the tool currently on the specified node.



static void
DropAllTools
XRNode node

This method allows you to drop all tools on the specified node.




Index

Thank you for using Paint in 3D ❤️

How do I upgrade?

Is URP/HDRP supported?

Are custom shaders/materials supported?

Is VR supported?

Can I save my in-game paint to my project?

Is the new InputSystem supported?

Getting Started (In-Game)

Getting Started (In-Editor)

UV Data Requirements

Fixing UV Seams

Examples Of UV Maps

Undo & Redo

Blend Modes

Skinned Mesh Painting

Drawing Connected Lines

Painting Multiple Textures

Advanced Optimization

Known Issues

Can I Create New Paint Presets?

Can I Create New Paint Tools? (In-Editor)

Can I Create New Paint Materials? (In-Editor)

Can Each Individual Paint Be De/Serialized? (e.g. networking)

What Examples Are Included?



Assets

Lean Touch

Lean Touch+

Lean Localization

Lean Pool

Lean Transition

Lean GUI

Lean GUI Shapes

Lean Texture

Lean Texture+

CW Spaceships

CW Orbit

CW Nebula

CW Gas Giant

CW Galaxy

CW Silhouette

CW Cosmos

Paint in 3D

FLOW

Destructible 2D

Space Graphics Toolkit

Space Graphics Planets

Volumetric Audio



Versions

3.1.1

3.1.0

3.0.3

3.0.2

3.0.1

3.0.0

2.0.4

2.0.3

2.0.2

2.0.1

2.0.0

1.11.14

1.11.13

1.11.12

1.11.11

1.11.10

1.11.9

1.11.8

1.11.7

1.11.6

1.11.5

1.11.4

1.11.3

1.11.2

1.11.1

1.10.5

1.10.4

1.10.3

1.10.2

1.10.1

1.9.14

1.9.13

1.9.12

1.9.11

1.9.10

1.9.9

1.9.8

1.9.7

1.9.6

1.9.5

1.9.4

1.9.3

1.9.2

1.9.1

1.9.0

1.8.9

1.8.8

1.8.7

1.8.6

1.8.5

1.8.4

1.8.3

1.8.2

1.8.1

1.8.0



Components

IClone

IHit

IHitCoord

IHitLine

IHitPoint

IHitQuad

IHitTriangle

P3dBlendMode

P3dChangeCounter

P3dChannelCounter

P3dClone

P3dCloneMirror

P3dColor

P3dColorCounter

P3dCommand

P3dCommon

P3dCoordCopier

CwEditorWindow

P3dGraduallyFade

P3dGroup

P3dGroupData

P3dHashedMaterial

P3dHashedModel

P3dHashedTexture

P3dHit

P3dLineConnector

P3dMask

P3dMaterialCloner

P3dModel

P3dModifier

P3dModifierList

P3dPaintable

P3dPaintableManager

P3dPaintableState

P3dPaintableTexture

P3dPaintableTextureMonitor

P3dPaintableTextureMonitorMask

P3dPointConnector

P3dReadColor

P3dReader

P3dRenderDepth

P3dSerialization

P3dSlot

P3dStateManager

P3dTextureHash

P3dCommandFill

P3dCommandReplace

P3dCommandReplaceChannels

P3dHitBetween

P3dHitCache

P3dHitCollisions

P3dHitNearby

P3dHitParticles

P3dHitScreen

P3dHitScreenBase

P3dHitScreenFill

P3dHitScreenLine

P3dHitThrough

P3dMeshAnalysis

P3dPreset

P3dSeamFixer

P3dModifyAngleRandom

P3dModifyColorRandom

P3dModifyHardnessPressure

P3dModifyHardnessRandom

P3dModifyOpacityPressure

P3dModifyOpacityRandom

P3dModifyPositionRandom

P3dModifyRadiusPressure

P3dModifyRadiusRandom

P3dModifyScaleRandom

P3dModifyTexturePressure

P3dModifyTextureRandom

P3dPaintFill

P3dPaintReplace

P3dPaintReplaceChannels

P3dPointer

P3dPointerMouse

P3dPointerPen

P3dPointerTouch

P3dButtonClearAll

P3dButtonRedoAll

P3dButtonUndoAll

P3dChangeCounterEvent

P3dChangeCounterFill

P3dChangeCounterText

P3dChannelCounterEvent

P3dChannelCounterFill

P3dChannelCounterText

P3dColorCounterEvent

P3dColorCounterFill

P3dColorCounterText

P3dPaintDebug

P3dSpawner

P3dActionOnEnable

P3dCommandSerialization

P3dDestroyAfterTime

P3dDestroyer

P3dDragPitchYaw

P3dInputAxis

P3dPaintFromCode

P3dPaintMultiplayer

P3dProceduralSetup

P3dReadColorEvent

P3dRotate

P3dSpiral

P3dTapThrow

P3dToggleParticles

P3dToggleScript

P3dTranslate

P3dVrManager

P3dVrTool