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.



Video Tutorials

In addition to the written documentation here, you can also find tutorial videos HERE.



Getting Started


Step 1

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

METHOD 1 Add the CwPaintableMesh 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.

Once the CwPaintableMesh component has been added, you can click the Analyze Mesh button to see if your mesh is suitable for painting.



Step 2

Next, you need to pick which textures you want to paint.

Your MeshRenderer or SkinnedMeshRenderer component has the Materials array, which usually has one material. Each material is rendered using a shader, and each shader has a list of textures that it uses. Most shaders have the Albedo/Color texture, but some more advanced shaders can have a Normal Map texture, Emission texture, etc.

METHOD 1 Click the Add Paintable Texture button from the CwPaintableMesh component inspector.
METHOD 2 Add the CwPaintableMeshTexture component to your GameObject, alongside the CwPaintableMesh component.

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

Inside this component, you can see the Slot setting. Next to this setting is a dropdown arrow button which you can click, and this allows you to pick from a list of all textures in your renderer.

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.

NOTE  The URP/HDRP Lit shaders use the 0, _BaseMap texture slot for the albedo. The _MainTex slot is a fake slot only used for backwards compatibility‽

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



Step 3

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

One simple method is to make a new GameObject, and add the CwHitScreen and CwPaintDecal 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 CwHitScreen component uses raycasts, so your mesh must have colliders (e.g. MeshCollider) for it to know where to paint.

You can now adjust the CwPaintDecal 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!



Presets

To save time, you can skip Step 1 & 2 by using the Presets system.




Mesh Analysis

When you make an object paintable, you add the CwPaintableMesh component.

Inside this component inspector is the Analyze Mesh button.

If you click this, then it will open the Mesh Analysis window with the current mesh you're trying to paint.


Paintable UV Maps

If your mesh is suitable for painting, it should look something like this.

As you can see, there are few overlapping triangles, few out of bounds triangles, few triangles with no UV, and the texture space is fairly well utilized.



Unsuitable UV Maps

If your mesh is NOT suitable for painting, it will look something like this.

As you can see, there are large areas of overlap in red, or many out of bounds triangles, or many triangles with no UV map.

In any of these scenarios, your mesh will need to be UV unwrapped before you can paint it.

NOTE  The Cube and Cylinder meshes that come with Unity are examples of meshes with unsuitable UVs, because they contain a lot of overlap.


Mesh Fixer

It's best to manually UV unwrap your meshes in your favorite 3D modeling software (e.g. Blender), because you will have full control over how it is unwrapped and can therefore give priority to certain areas and perhaps make better use of the texture space.

However, Paint in 3D comes with a tool to perform this UV unwrap for you.

To use it, click the Fix button at the top right of the Mesh Analysis window, and read the next tutorial section HERE.




Mesh Fixer

Paint in 3D comes with the Mesh Fixer tool, which allows you to UV Unwrap your mesh so it can be painted, and/or fix UV seams.

Method 1  Inside your paintable object's CwPaintableMesh component, click the AnalyzeMesh button, and click the Fix button at the top right.
Method 2  Find your mesh or model prefab in the Project window, right click it, and select the Create/CW/Paint in 3D/Mesh Fixer option.
Method 3  Right click in the Project window, select the Create/CW/Paint in 3D/Mesh Fixer option, and click the Meshes / Add button.

This will add a Mesh Fixer asset to your project.

Next to your mesh, you can click the Analyze Old button to see what the current non-fixed UV map looks like.


Coord

This setting allows you to choose which UV channel/coordinate you want to fix. For most meshes this should be set to First.



Recenter UV

For a mesh to be paintable, the UV data must be within the standard 0..1 range on the X and Y axes. However, some assets or mesh workflows put the meshes in slightly different ranges like 1..2 or 2..3, typically across the X axis.

When the UVs are recentered, it detects this scenario, and shifts the UVs to the typical 0..1 range.

NOTE  This setting is always on.
NOTE  If your mesh has UVs that have tiling and go well beyond the expected range (e.g. 3..9), then recentering will not work.


Generate UV

If you enable this setting, then your mesh will have entirely new UV data generated.



Fix Seams

If you enable this setting, then the UV island edges of your mesh will be extended so they can be painted without seams appearing.

NOTE  This will increase the amount of vertices/triangles in your mesh.


Generate

Once you're happy with your mesh fixer settings, you can click the Generate button.

This will make duplicates of all meshes in the Meshes list, process them based on your settings, and then save them as child assets of the mesh fixer.

Next to your mesh, you can click the Analyze New button to see what the new fixed UV map looks like.



Swap Meshes

If your scene contains CwPaintableMesh components that still use the old non-fixed meshes, then they will show up at the bottom of the mesh fixer under the SWAP MESHES list, allowing you to quickly update them.



Remap Texture

If you enable GenerateUV, then your meshes will have their UV data completely replaced with an entirely new layout.

This is fine if your meshes will then be painted from scratch. However, if your meshes originally had a texture, then those textures must be remapped to the new UV data.

To remap them, simply drag and drop your textures into the Remap Texture field one at a time. This will reveal a list of meshes you can use for the remapping, and once chosen you will be prompted to save the texture.

This remapping feature is very useful with low-poly style meshes, which are typically colored using an atlas style color palette, where the UV data for each triangle is flattened to a point on a specific color.




Secondary UV

If your mesh UV data isn't paintable, then using the Mesh Fixer is usually the best approach. However, in some scenarios you may want to keep the original mesh UV data, but still want to be able to paint your mesh.

In this scenario, another approach is to paint using the second channel of UV data. The second channel of UV data is usually used for lightmapping, but lightmap UVs are also perfect for painting.

To automatically generate this secondary UV data, you can enable the Generate Lightmap UVs setting in your model/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 CwPaintableMeshTexture 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 CwPaintableMeshTexture component's Advanced menu, and change the Conversion setting to Premultiply.


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 CwPaintableMeshTexture 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 CwPaintableMeshTexture > Channel setting to Second.

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




Presets

To make an object paintable, you must add and configure the CwPaintableMesh and CwPaintableMeshTexture components.

However, this can be time confusing to do if you have many objects, or a complex configuration.

To speed things up, Paint in 3D comes with the presets system.


Step 1

Select the Window/CW/Paintable Objects menu option.

This will open the Paintable Objects window.



Step 2

You can now select some GameObjects in the Scene or Hierarchy window.

Any GameObject or child GameObject you select that has a MeshFilter + MeshRenderer or SkinnedMeshRenderer will appear as a list in this window.



Step 3

Next to each GameObject will be the Make Paintable button.

If you click this, it will list all possible presets for this Renderer and Material's Shader type.

You can then choose one.



Custom Shader

Paint in 3D comes with presets for most of Unity's built-in shaders. However, if you've made your own shaders, or you have another asset with custom shaders, then you will have to add them to an existing preset.

All presets can be found in the Plugins/CW/PaintIn3D/Extras/Presets folder.

For example, the Basic - AA preset allows you to paint the _MainTex, which is usually the albedo (+ optional Alpha/Opacity). If you want to add your custom shader to this preset, then you can add it to the Shader Paths list.

If you're unsure what your shader is called, or want to add multiple shaders to this preset, then you can click the Find Shaders button, which will find all shaders in the current project that have all textures used by the preset (in this case of the Basic - AA preset, all shaders with _MainTex will be listed).



Custom Preset

If the presets that come with Paint in 3D aren't suitable for your project, then feel free to right click in the Project window, and select the Create/CW/Paint in 3D/Preset option.

This will add a new prefab to your project with the CwPreset, MeshFilter, MeshRenderer, and CwPaintableMesh components attached.

You must then set the ShaderPaths to include your shader, add all the CwPaintableMeshTexture components you want for this preset, and configure their Slot settings.

You can now use this preset from the Make Paintable button in the Paintable Objects window.




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.


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 Mesh (Paint in 3D).

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

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.

NOTE  If your scene contains any CwPaintableMesh/CwPaintableMeshAtlas components using the original non-fixed mesh, then they will be listed at the bottom of the Mesh Fixer asset under the SWAP MESHES heading.


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 CwPaintableMesh 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.




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 CwPaintableMeshTexture 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 CwPaintableMeshTexture 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 CwPaintableMeshTexture in your scene, letting Paint in 3D know that you're about to paint on them.

For example, the CwHitScreen component has the StoreStates setting, as well as CwToggleParticles, and CwTapThrow.



Buttons

Finally, you can add the CwUndoAll and CwRedoAll 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 CwPaintableMeshTexture 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 CwPaintableMeshTexture 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 CwPaintDecal 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. CwPaintSphere, CwPaintDecal).


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 CwPaintableMeshTexture 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 CwPaintableMeshTexture 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 CwPaintableMeshTexture 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.



Colliders

By default, Paint in 3D performs all painting using the visual mesh of your paintable objects, and doesn't inherently require colliders.

For example, the CwHitNearby and CwHitThrough components send hit data to paint components like CwPaintDecal using positions you manually specify, so colliders aren't required at all.

However, components like CwHitScreen and CwHitParticles perform raycasts to find hit point information, which means your paintable objects need colliders.

Since the paint is applied using the visual mesh, your colliders don't need to match your visual meshes, so for optimization purposes they can be much lower resolution.

However, depending on the size/radius of your paint brush, your colliders do have to be within a certain distance of the visual mesh.

For example, if you're painting your objects using the CwPaintSphere component with a total Size/Radius of 10 meters (units), then the maximum distance between your collider surface and visual mesh surface must always be below 10 meters, otherwise there will be areas you cannot paint.

TIP  You can add the CwPaintDebug component alongside your CwHit___ component, and it will show you in the Scene window where the paint hit data is being sent to.


Skinned Mesh Painting

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


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. CwPaintDecal'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 CwHitScreen component's RotateTo setting is set to Normal, and RotateTo/Direction is set to RayDirection. The CwPaintDecal 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 CwPaintableMesh 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 CwHitScreen, 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 CwHitScreen 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 CwHitScreen, CwHitNearby, and CwHitBetween. 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 CwPaintableMeshTexture 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. CwPaintDecal / 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 CwPaintableMeshTexture. 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 CwHitScreen 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 CwHitParticles 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.




Paintable Presets

Setting up an object to be paintable can be time consuming, so to speed things up Paint in 3D comes with the Preset system.

To use a preset, you can click the Window/CW/Paintable Objects menu option, which will open the Paintable Objects window.

If you select a MeshFilter + MeshRenderer or SkinnedMeshRenderer GameObject, then it will be listed in this window's Scene tab.

Next to each selected object, you will see the Make Paintable button. If you click this button, then you will see a list of presets you can apply to your object.

You can find all built-in presets in the PaintIn3D/Extras/Presets folder.

As you can see, each preset has a list of shader paths that it applies to, as well as a list of CwPaintableMesh and CwPaintableMeshTexture components pre-configured for those shader types.

Feel free to add your own custom shader paths to these presets, or to create your own presets if you have different texture types.



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 CwLight 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 CwSceneManager 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 CwPaintableMeshTexture 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 CwHitScreen component uses mouse and touch data to paint, so it will not work in VR.

To paint in VR, you can use the CwHitBetween 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 CwHitNearby 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/CW/Paintable Objects 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 CwPaintableMeshTexture 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 CwVrManager component.

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



Can I paint in the editor's Scene window?

No, Paint in 3D is only designed for painting in the editor's Game window in play mode or in playable builds.

If you want to be able to paint in the editor's Scene view without play mode, you must get the separate Paint in Editor asset.

NOTE  If you purchased Paint in 3D prior to the release of version 4.0.0 (December 2023), then you will be given a free copy of this separate Paint in Editor asset. This is because Paint in 3D used to include in-editor painting features, and some customers are only interested in that so it's not fair to remove it for them.


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 CwHitScreen component sends hit data to components like CwPaintDecal). 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 CwHit___ component. You can then implement any of the IHit___ interfaces, to get specific hit data. For example, the CwHitScreen 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 CwPaintMultiplayer.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 CwPaintableMeshAtlas, CwPaintableMesh, and CwPaintableMeshTexture 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 CwTextureHash 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 CwPaintableTexture.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 CwCommandSerialization.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 CwPaintableMesh and CwPaintableMeshTexture components. You can then paint the spaceship using the mouse/finger with the Paint GameObject, which has the CwHitScreen and CwPaintSphere components. Keep in mind that CwHitScreen 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 Mesh 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 mesh fixer, simply click the context menu button (⋮ icon) at the top right of your mesh inspector window. You can then select Fix Mesh (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 CwPaintableMesh 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 CwPaintableMeshTexture.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 CwPaintableMeshTexture.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 CwPaintableMeshTexture.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 CwPaintSphere.Radius setting and/or increasing the CwHitScreen.Interval setting allows you to draw dotted lines.

Connect Hits

This shows you how the CwHitScreen 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 CwButtonClearAll 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 CwPaintableMeshTexture.Clear method.

Isolate Tools

This shows you how the CwButtonIsolate 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 CwPaintSphere 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 CwPaintableMeshTexture 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 CwPaintableMeshTexture 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 CwGenerateMask component alongside your CwPaintableMeshTexture 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 CwHitScreen and CwPaintDecal components can be combined to paint decals. Decals allow you to paint a custom texture on an object at a specific location. The CwPaintDecal settings give you control over the angle, size, opacity, etc.

Wrapping

This shows you how the CwPaintDecal 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 CwHitScreen 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 CwHitScreen 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 CwPaintDecal 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 CwPaintDecal component''s Advanced / NormalFade setting can be used to control the sharpness of the normal cut-off transition.

Settings

This shows you how the CwPaintDecal 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 CwCloneMirror 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 CwCloneMirror.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 CwTapThrow 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 CwHitCollisions and CwPaintDecal components.

Destroyer

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

Spawner

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

Between Painting

This shows you how the CwHitBetween component can be used to paint between two Transform points. The CwHitBetween.Line can be used to visualize the current beam, and the CwHitBetween.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 CwHitBetween 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 CwPaintSphere on the first hit point it finds between these two points, and paint them red. The camera can then be rotated using the CwPitchYaw component.

Through Points

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

Particle Painting

This shows you how the CwHitParticles component can be added to your ParticleSystem GameObjects. This allows you to paint with components like CwPaintSphere 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 CwHitParticles 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 CwHitParticles 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 CwPaintSphere, 9 calls are skipped.

Nearby Painting

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

Throw

This shows you how the CwTapThrow component can spawn and throw the Ball prefab. This prefab has the CwHitNearby 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 CwPaintableMeshTexture.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 CwPaintableMeshTexture.UndoRedo setting can be used to store undo and redo states. You can then add the CwButtonUndoAll and CwButtonRedoAll 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 CwHitScreen.

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. CwPaintDecal), 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 CwGraduallyFade 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 CwPaintableMeshTexture 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 CwPaintReplace component can be used to replace every pixel in the currently painting texture. Keep in mind the GameObject with the CwPaintableMeshTexture must have a collider for this to work, and CwHitScreen.Emit must be set to PointsOnUV.

Fill Painting

This shows you how the CwPaintFill component can be used to blend every pixel in the painted texture. This allows you to gradually change it, rather than instantly like CwPaintReplace does. Keep in mind the GameObject with the CwPaintableMeshTexture must have a collider for this to work, and CwHitScreen.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 CwPaintableMeshTexture.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 CwPaintableMeshTexture.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 CwChannelCounter component can be used to count the total pixels in the specified CwPaintableMeshTexture that exceed the threshold value in each R/G/B/A channel. The CwChannelCounterText 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 CwChannelCounter.Mesh setting to the original non-seam-fixed mesh. The paint percentage can then be displayed using the CwChannelCounterText and CwChannelCounterFill 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 CwColorCounter component can be used to count the total pixels in the specified CwPaintableMeshTexture that are within the threshold color of a CwColor. The CwColorCounterText component can then be used to display these in the UI.

Change Counter

This shows you how the CwChangeCounter 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 CwChangeCounter 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 CwPaintableMeshTexture 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 CwPaintableMeshTexture.State = LocalCommandCopy setting.

Live Painting

This shows you how to create live paint that can be repositioned in game. This is done using the CwHitNearby 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 CwHitNearby component's ManuallyHitNow function.

Translate

This shows you how the CwTranslate component can be used to move a live decal around the scene using UI button events that call the CwTranslate.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 CwPaintableMesh.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 CwPaintableMesh component's Advanced / OtherRenderers list. You must then add the CwPaintableMeshAtlas component to the other objects, and drag and drop the first object into the CwPaintableMeshAtlas.Parent setting.

Fill Painting

This shows you how to paint filled in polygon shapes. This is done by replacing the CwHitScreen component with the CwHitScreenFill 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 CwMask 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 CwMask 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 CwPaintableMeshTexture 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 CwReadColor component, which can be added alongside any CwHit___ component that works using raycasts like CwHitScreen and CwHitBetween. 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 CwReadColorEvent component, which can be added alongside the CwReadColor 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 CwReadColor and CwPaintBetween 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 CwPaintMultiplayer 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 CwProceduralSetup 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 CwHitScreen 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 CwPaintableMeshTexture to its current Texture and Color settings. The texture that will be revealed is first hidden using the CwPaintableMeshTexture.Color setting with 0 alpha. The CwPaintableMesh.OnActivated event is then used to reset this back to full alpha using the CwPaintableMesh.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 CwChangeCounter 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 CwHitScreen 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 CwMask. This is done using the CwPaintableMeshTexture 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/CwPaintFromCode.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 CwGraduallyFade 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 CwGraduallyFade to twist the texture around, but it also has a second CwGraduallyFade to fade it to black to slow down the speed. The albedo texture''s CwGraduallyFade.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 CwGraduallyFadeGroup 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 CwHitScreen 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 CwHitNearby and CwHitThrough components with the CwPaintSphere and CwPaintDecal components to continuously paint any pixels that enter the current volume. Keep in mind the CwPaintDecal.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 CwHitScreenLine component, which can be combined with the CwPaintSphere/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 CwHitScreen and CwHitScreenLine 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 CwPaintDebug 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 CwPaintableMeshTexture 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 CwGraduallyFade 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 CwPaintableMeshTexture 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/CwCommandSerialization.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 CwMask 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 CwHitNearby component with the PaintIn setting set to Start. The CwPaintSphere 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 CwPaintSphere.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 CwPaintSphere component's Advanced/TargetModel setting.

Depth Mask

This shows you how to mask paint behind foreground objects. This is done with the CwRenderDepth component, which can render the scene depth from the perspective of a camera. All paint components with the Advanced / FindDepthMask enabled (it''s enabled by default) will then be depth masked.

Expanding Paint

This shows you how to make paint that expands or rotates over time. This is done using the CwHitScreen and CwSpawner components, which will spawn a prefab under the mouse/finger when you click/tap the screen. The spawned prefab has the CwHitNearby and CwPaintDecal components, which make the spawned prefab paint every frame/Update. The prefab also has the CwPaintAction component, which is used to call the CwPaintDecal.MultiplyScale or CwPaintDecal.IncrementAngle functions to animate the paint. Finally, the CwDestroyAfterTime component is used to destroy the prefab after some time.

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.

Clean Dirt

This shows you how to apply dirt to an object using the P3D/Solid shader''s albedo override texture. You can then clear the paint by changing your brush to the Replace blending mode, with a color of transparent black (RGBA = 0000). The CwColorCounter/Fill/Event components can then be used to show how much has been cleaned, and show the "CLEAN" text when it''s below 2%.

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 CwPaintableMeshTexture.Group setting, and to paint these textures a matching CwPaintSphere.Group/CwPaintDecal.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 CwPaintCollisions components can be used with different layer filtering and different Root settings, allowing you to apply different kinds of paint to different objects. The CwPaintDecal component and CwPaintableTexture''s GameObject must have matching layers for this to work.

Shield Impacts

This shows you how CwHitCollisions and CwPaintSphere can be added to a Rigidbody GameObject. This allows them to paint the Spaceship_Shield GameObject with a special impact pattern. The CwGraduallyFade 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 CwToggleParticles 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 CwPaintDecal.Scale.z setting, and with the CwHitScreen.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

Spaceships - Build & Destroy

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


Modular Backgrounds

Modular Backgrounds

Unlock a universe of visual possibilities with Modular Backgrounds. Simply drag and drop these graphics into the background of your scenes.


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!


Paint in 2D

Paint in 2D

Paint all your sprites with Paint in 2D. With incredible performance on mobile, WebGL, and much more!


Paint in Editor

Paint in Editor

Paint in Editor unlocks the ability to paint objects in your scene - great for making small tweaks, or even creating entirely new texture sets!


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

4.1.4

Fixed Mesh Fixer tool when fixing seams of high poly meshes.
Updated shaders to latest version.



4.1.3

Added Car Decals Advanced demo scene.
Added Clean Dirt demo scene.
Added HasRead property to all Cw___Counter components.
Fixed Cw___CounterEvent components firing on the first frame before the counters have updated.



4.1.2

Added Show setting to example shaders to change backface culling.
Added Multitap setting to CwRenderDepth component to remove edge seams.
Rewrote MeshFixer's FixSeams setting when fixing meshes with multiple UV sets.
Updated shaders to latest version.



4.1.1

Added CwPaintableTexture.ClearCommand method.
Added CwPaintSphere.MultiplyOpacity method.
Added CwPaintSphere.IncrementRadius method.
Added CwPaintSphere.IncrementScale method.
Added CwPaintDecal.MultiplyOpacity method.
Added CwPaintDecal.IncrementRadius method.
Added CwPaintDecal.IncrementScale method.



4.1.0

Updated CW/Common code to latest version.
Updated shaders to latest version.



4.0.5

Added WaitUntilNotPainting setting to all Cw___Counter components.
Added ReadAtStart setting to all Cw___Counter components.
Enabled manual editing of CwChannelCounter component's Total and CountR/G/B/A values.
Enabled manual editing of CwChangeCounter component's Total and Count values.
Rewrote non-async texture reading to be spread across multiple frames.
Added ReadPixelsBudget setting to CwPaintableManager component.
Moved most hit components from PaintCore to PaintIn3D.
Added Advanced/AutoStoreState setting to CwPaintableSpriteTexture component.
Undo/Redo states are now only created if you actually paint something (when a hit command is sent to a texture).
Added Expanding Paint demo scene.



4.0.4

Fixed depth mask precision on some devices.



4.0.3

Fixed potential CwPaintableMeshTexture error when creating paintable objects from script.
Added Advanced/MaterialApplication setting to CwPaintableMesh component.
Removed TargetTexture setting from CwRenderDepth component (it's now auto generated).
Fixed CwDepthMask not working in some scenarios.
Replaced CwPaintSphere component's DepthMask setting with FindDepthMask toggle.
Replaced CwPaintDecal component's DepthMask setting with FindDepthMask toggle.
Added Advanced/FindMask setting to CwPaintDecal component.
Added Advanced/FindMask setting to CwPaintSphere component.



4.0.2

Disabled Mesh Fixer asset's Fix Seams setting by default.
Showed Mesh Fixer asset's Recenter UV setting.
Fixed Mesh Fixer asset's naming when fixing non-imported meshes.
Fixed Mesh Fixer asset's Remap feature.



4.0.1

Added Fix button to Mesh Analysis window.
Fixed Fill painting error.



4.0.0

WARNING: This is a massive update that includes file structure changes, so before installing you should delete the "PaintCore" and "PaintIn3D" and "PaintInEditor" folders.
Removed in-editor painting features (customers prior to the release of this version can download the new Paint in Editor asset for free).
Renamed all scripts to have Cw prefix instead of P3d prefix.
Renamed Paint in 3D window to Paintable Objects.
Renamed P3dPaintable to P3dPaintableMesh.
Renamed P3dPaintableTexture to P3dPaintableMeshTexture.
The Material Cloner is no longer required.
The Seam Fixer tool has been replaced with the Mesh Fixer tool.
The new Mesh Fixer implements UV unwrapping.
The new Mesh Fixer can fix meshes that go outside the 0..1 UV range.
The Preset system is now much simpler and intuitive to use.



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.







Components

CwActionOnEnable

This component invokes the Action event when this component is enabled.


UnityEvent
Action

The event that will be invoked.




CwBlendMode

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.




CwButtonClearAll

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?




CwButtonRecolor

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.


Renderer
TargetRenderer

The renderer whose color will be changed.



int
TargetMaterial

The material index in the target renderer.



string
TargetProperty

The material property that will be changed.



ApplyType
Apply

How should the new color be applied to the target renderer?



void
Recolor

If you want to manually trigger Recolor, then call this function.




CwButtonRedoAll

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.




CwButtonUndoAll

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.




CwChangeCounter CwPaintableTextureMonitorMask

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<CwChangeCounter>
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.



bool
HasRead

This will return true once this counter has updated at least once.



static long
GetTotal
ICollection<CwChangeCounter> counters = null

The Total of the specified counters.



static long
GetCount
ICollection<CwChangeCounter> counters = null

The Count of the specified counters.



static float
GetRatio
ICollection<CwChangeCounter> counters = null

The Ratio of the specified counters.




CwChangeCounterEvent

This component allows you to perform an event when the specified CwChangeCounter instances are painted a specific amount.


List<CwChangeCounter>
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.




CwChangeCounterFill

This component fills the attached UI Image based on the total amount of pixels that have been painted in the specified CwChangeCounterFill components.


List<CwChangeCounter>
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?




CwChangeCounterText

This component will output the total pixels for the specified team to a UI Text component.


List<CwChangeCounter>
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.




CwChannelCounter CwPaintableTextureMonitorMask

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


static LinkedList<CwChannelCounter>
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.



bool
HasRead

This will return true once this counter has updated at least once.



static long
GetTotal
ICollection<CwChannelCounter> counters = null

The Total of the specified counters.



static long
GetCountR
ICollection<CwChannelCounter> counters = null

The CountR of the specified counters.



static long
GetCountG
ICollection<CwChannelCounter> counters = null

The CountG of the specified counters.



static long
GetCountB
ICollection<CwChannelCounter> counters = null

The CountB of the specified counters.



static long
GetCountA
ICollection<CwChannelCounter> counters = null

The CountA of the specified counters.



static float
GetRatioR
ICollection<CwChannelCounter> counters = null

The CountR / Total of the specified counters.



static float
GetRatioG
ICollection<CwChannelCounter> counters = null

The CountG / Total of the specified counters.



static float
GetRatioB
ICollection<CwChannelCounter> counters = null

The CountB / Total of the specified counters.



static float
GetRatioA
ICollection<CwChannelCounter> counters = null

The CountA / Total of the specified counters.



static Vector4
GetRatioRGBA
ICollection<CwChannelCounter> counters = null

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




CwChannelCounterEvent

This component allows you to perform an event when the specified CwChannelCounter instances are painted a specific amount.


List<CwChannelCounter>
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.




CwChannelCounterFill

This component fills the attached UI Image based on the total amount of opaque pixels that have been painted in all active and enabled CwChannelCounter components in the scene.


List<CwChannelCounter>
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?




CwChannelCounterText

This component allows you to output the totals of all the specified pixel counters to a UI Text component.


List<CwChannelCounter>
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.




CwClone

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


static LinkedList<CwClone>
Instances

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




CwCloneMirror CwClone

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?




CwColor

This component allows you to define a color that can later be counted from the CwColorCounter 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<CwColor>
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.




CwColorCounter CwPaintableTextureMonitorMask

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


static LinkedList<CwColorCounter>
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.



bool
HasRead

This will return true once this counter has updated at least once.



static long
GetTotal
ICollection<CwColorCounter> counters = null

The Total of the specified counters.



static long
GetCount
CwColor color, ICollection<CwColorCounter> counters = null

The Count of the specified counters.



static float
GetRatio
CwColor color, ICollection<CwColorCounter> counters = null

The Ratio of the specified counters.



int
Count
CwColor color

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




CwColorCounterEvent

This component allows you to perform an event when the specified CwColorCounter instances are painted a specific amount.


List<CwColorCounter>
Counters

This allows you to specify the counters that will be used.

None = All active and enabled counters in the scene.



CwColor
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.




CwColorCounterFill

This component fills the attached UI Image based on the total amount of pixels that have been painted in the specified CwColorCounter components.


List<CwColorCounter>
Counters

This allows you to specify the counters that will be used.

Zero = All active and enabled counters in the scene.



CwColor
Color

This allows you to set which color will be handled by this component.



bool
Inverse

Inverse the fill?




CwColorCounterText

This component will output the total pixels for the specified color to the OnString event.


List<CwColorCounter>
Counters

This allows you to specify the counters that will be used.

Zero = All active and enabled counters in the scene.



CwColor
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.




CwCommand

This is the base class for all paint commands. These commands (e.g. paint decal) are added to the command list for each CwPaintableTexture, 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.



CwHashedMaterial
Material

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



int
Pass

The material pass that will be used.



CwHashedModel
Model

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



int
Submesh

The mesh submesh that will be painted.



CwHashedTexture
LocalMaskTexture

The LocalMask that will be used when painting.



Vector4
LocalMaskChannel

The channel of the LocalMaskTexture that will be used.




CwCommandDecal CwCommand

This class manages the decal painting command.


void
SetShape
Quaternion rotation, Vector3 size, float angle

This method allows you to set the shape and rotation of the decal.

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



CwCommandFill CwCommand

This class manages the fill painting command.



CwCommandReplace CwCommand

This class manages the replace painting command.



CwCommandReplaceChannels CwCommand

This class manages the replace channels painting command.



CwCommandSerialization

This component shows you how to listen for and store paint commands added to any CwPaintableTexture 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 CwModel, CwPaintable, CwPaintableTexture, 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 Cw___ components can be set using the Advanced/Hash setting, but the Texture instances must be done separately using either the CwTextureHash component, or manual calls to CwSerialization.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.




CwCommandSphere CwCommand

This class manages the sphere painting command.



CwCommon

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.




CwCoordCopier

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.




CwDestroyAfterTime

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.




CwDestroyer

This component automatically destroys the specified GameObject when sent a hit point. Hit points will automatically be sent by any CwHit___ component on this GameObject, or its ancestors.


GameObject
Target

This GameObject will be destroyed.




CwDilate

This class allows you to easily dilate the specified mesh texture.



CwDragPitchYaw

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).




CwGraduallyFade

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


CwPaintableTexture
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.



CwBlendMode
BlendMode

This component will paint using this blending mode.

NOTE  See CwBlendMode documentation for more information.


Texture
BlendTexture

The texture that will be faded toward.



CwPaintableTexture
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.



CwPaintableTexture
MaskPaintableTexture

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



CwChannel
MaskChannel

This allows you to specify the channel of the mask.




CwGroup

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



CwGroupData

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


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 CwGroupData.


static List<CwGroupData>
CachedInstances

This static property returns a list of all cached CwGroupData 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 CwGroupData with the specified Index setting, or null.



static CwGroupData
GetGroupData
int index

This static method returns the CwGroupData 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.



CwHashedMaterial

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



CwHashedModel

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

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


CwHashedTexture

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 CwSerialization.TryRegister/TryUnregister methods on your textures.


CwHit

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.




CwHitBetween

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.



CwPointConnector
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.




CwHitCache

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



CwHitCollisions

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 CwHitCollisions 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.




CwHitNearby

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.



CwPointConnector
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.




CwHitParticles

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 CwHitParticles 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.




CwHitPointers

This this is the base class for hit screen components that receive data from CwPointer___ components.


LayerMask
GuiLayers

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




CwHitScreen CwHitScreenBase

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 CwPaintDecal, 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.



CwPointConnector
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.




CwHitScreenBase CwHitPointers

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.



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.



float
NormalOffset

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.




CwHitScreenFill CwHitScreen

This component works like CwHitScreen, 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.



CwHitScreenLine CwHitScreenBase

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 CwPaintDecal, 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.



CwPointConnector
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.




CwHitThrough

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.



CwLineConnector
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.




CwInputAxis

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.




CwLineConnector

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.




CwMask

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.



CwChannel
Channel

The mask will use pixels from this texture channel.



bool
Invert

By default, opaque/white parts of the mask are areas you can paint, and transparent/black parts are parts that are masked. Invert this?



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<CwMask>
Instances

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




CwMaterialCloner

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).

NOTE  This component is no longer required, and by default will not activate. To use it, you must change your CwPaintableMesh component's Advanced/MaterialApplication setting to ClonerAndTextures.

int
Index

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



string
ShaderKeyword

If this shader needs specific keywords enabled, you can add them here.



List<External>
Externals

The cloned material also belongs to these external GameObjects. This is used with texture atlasing.



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 CwPaintable to clone the material.


void
Deactivate

This reverses the material cloning.




CwMeshAnalysis

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.



CwMeshFixer

This tool allows you to process any mesh so that it can be painted.

The fixed meshes will be placed as a child of this tool in your Project window.

To use the fixed mesh, drag and drop it into your MeshFilter or SkinnedMeshRenderer.

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 fixed mesh.



List<Pair>
Meshes

The meshes we will fix.



CwCoord
Coord

The UV channel whose seams will be fixed.



bool
GenerateUV

Generate UV data for the meshes?



float
AngleError

Maximum allowed angle distortion (0..1).



float
AreaError

Maximum allowed area distortion (0..1).



float
HardAngle

This angle (in degrees) or greater between triangles will cause seam to be created.



float
PackMargin

How much uv-islands will be padded.



bool
FixOverflow

If UV data is shifted out of the 0..1 range (e.g. 3..4), it will be wrapped back to 0..1.

However, if these wrapped triangles still go outside the 0..1 range, should they be wrapped to the other side so it can still be fully painted?



bool
FixSeams

Fix the seams of the meshes?



float
Border

The thickness of the UV borders in the fixed mesh.



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, bool generateUV, bool fixOverflow, bool fixSeams, CwCoord coord, float border

This static method allows you to fix any mesh at runtime.

NOTE  The generateUV setting is only available in the editor.



CwMeshModel CwModel

This is the base code for the CwPaintableMesh and CwPaintableMeshAtlas components.


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.



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.




CwModel

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 CwPaintableTexture component must be on a different object.

CwHash
Hash

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



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.



static LinkedList<CwModel>
Instances

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



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

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



void
ScaleSize
ref int width, ref int height

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




CwModifier

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?




CwModifierList

This class maintains a list of CwModifier 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<CwModifier>
Instances

This stores all modifiers in this list.




CwModifyAngleRandom CwModifier

This class allows you to randomize the painting angle of the attached component (e.g. CwPaintDecal).


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.




CwModifyColorRandom CwModifier

This class allows you to randomize the painting color of the attached component (e.g. CwPaintDecal).


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.




CwModifyHardnessPressure CwModifier

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.




CwModifyHardnessRandom CwModifier

This class allows you to randomize the painting hardness of the attached component (e.g. CwPaintDecal).


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.




CwModifyOpacityPressure CwModifier

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.




CwModifyOpacityRandom CwModifier

This class allows you to randomize the painting opacity of the attached component (e.g. CwPaintDecal).


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.




CwModifyPositionRandom CwModifier

This class allows you to randomize the painting position of the attached component (e.g. CwPaintDecal).


float
Radius

The position will be offset up to this radius away in world space.




CwModifyRadiusPressure CwModifier

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.




CwModifyRadiusRandom CwModifier

This class allows you to randomize the painting radius of the attached component (e.g. CwPaintDecal).


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.




CwModifyScaleRandom CwModifier

This class allows you to randomize the painting scale of the attached component (e.g. CwPaintDecal).


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.




CwModifyTexturePressure CwModifier

This class allows you to change the painting texture of the attached component (e.g. CwPaintDecal) 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.




CwModifyTextureRandom CwModifier

This class allows you to randomize the painting texture of the attached component (e.g. CwPaintDecal).


List<Texture>
Textures

A random texture will be picked from this list.




CwPaintableManager

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


static LinkedList<CwPaintableManager>
Instances

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



int
ReadPixelsBudget

If the current GPU doesn't support async texture reading, this setting allows you to limit how many pixels are read per frame to reduce lag.



static event System.Action<object>
OnBeginPainting

This event is called before a component paints something, where 'object' is a reference to the component or link that will do the painting.

NOTE  If this object paints multiple times, this event will only be called once at the start of that paint series. For example, if you drag a finger to paint then the each finger down will invoke this.


static object
LastPaintingObject

Before a component submits paint hits/commands, it will register the 'object' reference to track the painting.

NOTE  An object can potentially paint many times, and OnBeginPainting will be invoked before.


static void
MarkActivelyPainting

If a component is currently painting, it can call this method.




CwPaintableMesh CwMeshModel

This component marks the current GameObject as being paintable.

NOTE  This GameObject must have the MeshFilter + MeshRenderer, or SkinnedMeshRenderer component.
NOTE  If your mesh is part of a texture atlas, then you can use the CwPaintableMeshAtlas component on all the other atlas mesh GameObjects.

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.



MaterialApplicationType
MaterialApplication

This allows you to specify how the paintable textures will be applied to this paintable object.

PropertyBlock = Using MaterialSetPropertyBlock feature.

ClonerAndTextures = Using (Optional) CwMaterialCloner and Material.SetTexture calls.



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
IsActivated

This lets you know if this paintable has been activated.

Being activated means each associated CwMaterialCloner and CwPaintableTexture has been Activated.

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


HashSet<CwPaintableMeshTexture>
PaintableTextures

This gives you all CwPaintableTexture components that have been activated with this paintable mesh.



void
RemoveComponents

This method will remove all CwPaintableMesh and CwPaintableTexture and CwMaterialCloner components from this GameObject.



void
Activate

This allows you to manually activate all attached CwMaterialCloner and CwPaintableTexture components.



void
Deactivate

This reverses the material cloning.



void
ClearAll
Color color

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



void
ClearAll
Texture texture, Color color

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



void
Register
CwPaintableMeshTexture paintableTexture

This allows you to manually register a CwPaintableTexture.



void
Unregister
CwPaintableMeshTexture paintableTexture

This allows you to manually unregister a CwPaintableTexture.




CwPaintableMeshAtlas CwMeshModel

If you want to paint a mesh that is part of a texture atlas, except for the main mesh, you can add this component to all other GameObjects that are part of the texture atlas. You can then set the Parent setting to the main mesh.

NOTE  This GameObject must have the MeshFilter + MeshRenderer, or SkinnedMeshRenderer component.
NOTE  This GameObject should NOT have any CwPaintableMeshTexture components. Those should only be on the main (Parent) paintable mesh GameObject.

CwPaintableMesh
Parent

The paintable mesh this atlas mesh is associated with.




CwPaintableMeshTexture

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 CwMaterialCloner component to make it unique.


CwPaintableObjects

This window shows you all paintable objects in the scene, and it also allows you to make objects paintable from all paintable presets in your project.

You can open this window from the Window/CW/Paintable Objects menu.



CwPaintableState

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



CwPaintableTextureMonitor

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


CwPaintableTexture
PaintableTexture

This is the paintable texture whose pixels we will count.



bool
WaitUntilNotPainting

Should this counter only read when you're not currently painting?

NOTE  You can control this by manually calling CwPaintableManager.MarkActivelyPainting() in Update.


float
Interval

This allows you to specify the minimum delay between when your texture is painted, and when the data is read.

0 = As fast as possible.

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.


bool
ReadAtStart

If you enable this, then the reader will update as soon as it starts. If not, you must manually populate it with default data.



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 CwPaintableTexture to be registered.



void
Unregister

This forces the specified CwPaintableTexture to be unregistered.




CwPaintableTextureMonitorMask CwPaintableTextureMonitor

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


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.



CwChannel
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.




CwPaintAction

This component performs an action every time a paint hit is received. Hit points will automatically be sent by any CwHit___ component on this GameObject, or its ancestors.


UnityEvent
Action

The event that will be invoked.




CwPaintDebug

This component allows you to debug hit points into the Scene tab. Hit points will automatically be sent by any CwHit___ 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.




CwPaintDecal

This allows you to paint a decal at a hit point. Hit points will automatically be sent by any CwHit___ component on this GameObject, or its ancestors.


LayerMask
Layers

Only the CwPaintable___ GameObjects whose layers are within this mask will be eligible for painting.



CwPaintableMeshAtlas
TargetModel

If this is set, then only the specified CwPaintable___ will be painted, regardless of the layer setting.



CwGroup
Group

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



CwPaintableTexture
TargetTexture

If this is set, then only the specified CwPaintableTexture will be painted, regardless of the layer or group setting.



CwBlendMode
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 decal that will be painted.



Texture
Shape

This allows you to specify the shape of the decal. This is optional for most blending modes, because they usually derive their shape from the RGB or A values. However, if you're using the Replace blending mode, then you must manually specify the shape.



CwChannel
ShapeChannel

This allows you specify the texture channel used when sampling Shape.



Color
Color

The color of the paint.



float
Opacity

The opacity of the brush.



float
Angle

The angle of the decal in degrees.



Vector3
Scale

This allows you to control the mirroring and aspect ratio of the decal.

1, 1 = No scaling.

-1, 1 = Horizontal Flip.



float
Radius

The radius of the paint brush.



float
Hardness

This allows you to control the sharpness of the near+far depth cut-off point.



float
Wrapping

This allows you to control how much the decal can wrap around uneven paint surfaces.



float
NormalFront

This allows you to control how much the paint can wrap around the front of surfaces.

For example, if you want paint to wrap around curved surfaces then set this to a higher value.

NOTE  If you set this to 0 then paint will not be applied to front facing surfaces.


float
NormalBack

This works just like Normal Front, except for back facing surfaces.

NOTE  If you set this to 0 then paint will not be applied to back facing surfaces.


float
NormalFade

This allows you to control the smoothness of the normal cut-off point.



Texture
TileTexture

This allows you to apply a tiled detail texture to your decals. This tiling will be applied in world space using triplanar mapping.



Transform
TileTransform

This allows you to adjust the tiling position + rotation + scale using a Transform.



float
TileOpacity

This allows you to control the triplanar influence.

0 = No influence.

1 = Full influence.



float
TileTransition

This allows you to control how quickly the triplanar mapping transitions between the X/Y/Z planes.



bool
FindMask

If your scene contains a CwMask, should this paint component use it?



bool
FindDepthMask

If your scene contains a CwRenderDepth, should this paint component use it?



CwModifierList
Modifiers

This stores a list of all modifiers used to change the way this component applies paint (e.g. CwModifyColorRandom).



void
FlipHorizontal

This method will invert the scale.x value.



void
FlipVertical

This method will invert the scale.y value.



void
IncrementAngle
float degrees

This method increments the angle by the specified amount of degrees, and wraps it to the -180..180 range.



void
MultiplyOpacity
float multiplier

This method multiplies the Opacity by the specified value.



void
IncrementOpacity
float delta

This method increments the Opacity by the specified value.



void
MultiplyRadius
float multiplier

This method multiplies the Radius by the specified value.



void
IncrementRadius
float delta

This method increases the Radius by the specified value.



void
MultiplyScale
float multiplier

This method multiplies the Scale by the specified value.



void
IncrementScale
float multiplier

This method increases the Scale by the specified value.



void
HandleHitPoint
bool preview, int priority, float pressure, int seed, Vector3 position, Quaternion rotation

This method paints all pixels at the specified point using the shape of a decal.



void
HandleHitLine
bool preview, int priority, float pressure, int seed, Vector3 position, Vector3 endPosition, Quaternion rotation, bool clip

This method paints all pixels between the two specified points using the shape of a decal.



void
HandleHitTriangle
bool preview, int priority, float pressure, int seed, Vector3 positionA, Vector3 positionB, Vector3 positionC, Quaternion rotation

This method paints all pixels between three points using the shape of a decal.



void
HandleHitQuad
bool preview, int priority, float pressure, int seed, Vector3 position, Vector3 endPosition, Vector3 position2, Vector3 endPosition2, Quaternion rotation, bool clip

This method paints all pixels between two pairs of points using the shape of a decal.



void
HandleHitCoord
bool preview, int priority, float pressure, int seed, CwHit hit, Quaternion rotation

This method paints the scene using the current component settings at the specified CwHit.




CwPaintFill

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.


CwGroup
Group

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



CwBlendMode
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.



CwModifierList
Modifiers

This stores a list of all modifiers used to change the way this component applies paint (e.g. CwModifyColorRandom).



void
IncrementOpacity
float delta

This method increments Opacity by the specified value.




CwPaintFromCode

This component shows you how to paint from code.



CwPaintMultiplayer

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.




CwPaintReplace

This component implements the replace paint mode, which will replace all pixels in the specified texture.


CwGroup
Group

Only the CwPaintableTexture 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.



CwModifierList
Modifiers

This stores a list of all modifiers used to change the way this component applies paint (e.g. CwModifyColorRandom).




CwPaintReplaceChannels

This component implements the replace channels paint mode, which will replace all pixels in the specified textures and channel weights.


CwGroup
Group

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




CwPaintSphere

This allows you to paint a sphere at a hit point. Hit points will automatically be sent by any CwHit___ component on this GameObject, or its ancestors.


LayerMask
Layers

Only the CwPaintable___ GameObjects whose layers are within this mask will be eligible for painting.



CwGroup
Group

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



CwMeshModel
TargetModel

If this is set, then only the specified model will be painted, regardless of the layer setting.



CwPaintableTexture
TargetTexture

If this is set, then only the specified CwPaintableTexture will be painted, regardless of the layer or group setting.



CwBlendMode
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.


Color
Color

The color of the paint.



float
Opacity

The opacity of the brush.



float
Angle

The angle of the paint in degrees.

NOTE  This is only useful if you change the Scale.x/y values.


Vector3
Scale

By default this component paints using a sphere shape, but you can override this here to paint an ellipsoid.

NOTE  When painting an ellipsoid, the orientation of the sphere matters. This can be controlled from the CwHit__ component settings.


float
Radius

The radius of the paint brush.



float
Hardness

The hardness of the paint brush.



Texture
TileTexture

This allows you to apply a tiled detail texture to your decals. This tiling will be applied in world space using triplanar mapping.



Transform
TileTransform

This allows you to adjust the tiling position + rotation + scale using a Transform.



float
TileOpacity

This allows you to control the triplanar influence.

0 = No influence.

1 = Full influence.



float
TileTransition

This allows you to control how quickly the triplanar mapping transitions between the X/Y/Z planes.



bool
FindMask

If your scene contains a CwMask, should this paint component use it?



bool
FindDepthMask

If your scene contains a CwRenderDepth, should this paint component use it?



CwModifierList
Modifiers

This stores a list of all modifiers used to change the way this component applies paint (e.g. CwModifyColorRandom).



void
IncrementAngle
float degrees

This method increments the angle by the specified amount of degrees, and wraps it to the -180..180 range.



void
MultiplyOpacity
float multiplier

This method multiplies the Opacity by the specified value.



void
IncrementOpacity
float delta

This method increments the Opacity by the specified value.



void
MultiplyRadius
float multiplier

This method multiplies the Radius by the specified value.



void
IncrementRadius
float delta

This method increases the Radius by the specified value.



void
MultiplyScale
float multiplier

This method multiplies the Scale by the specified value.



void
IncrementScale
float multiplier

This method increases the Scale by the specified value.



void
HandleHitPoint
bool preview, int priority, float pressure, int seed, Vector3 position, Quaternion rotation

This method paints all pixels at the specified point using the shape of a sphere.



void
HandleHitLine
bool preview, int priority, float pressure, int seed, Vector3 position, Vector3 endPosition, Quaternion rotation, bool clip

This method paints all pixels between the two specified points using the shape of a sphere.



void
HandleHitTriangle
bool preview, int priority, float pressure, int seed, Vector3 positionA, Vector3 positionB, Vector3 positionC, Quaternion rotation

This method paints all pixels between three points using the shape of a sphere.



void
HandleHitQuad
bool preview, int priority, float pressure, int seed, Vector3 position, Vector3 endPosition, Vector3 position2, Vector3 endPosition2, Quaternion rotation, bool clip

This method paints all pixels between two pairs of points using the shape of a sphere.



void
HandleHitCoord
bool preview, int priority, float pressure, int seed, CwHit hit, Quaternion rotation

This method paints the scene using the current component settings at the specified CwHit.

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



CwPointConnector

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.




CwPointer

This this is the base class for any component that sends pointer information to any CwHitScreen component.



CwPointerMouse CwPointer

This component sends pointer information to any CwHitScreen 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.




CwPointerPen CwPointer

This component sends pointer information to any CwHitScreen 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.




CwPointerTouch CwPointer

This component sends pointer information to any CwHitScreen 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.




CwPreset

This component allows you to define a set of CwPaintableTexture and CwMaterial 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.



List<string>
ShaderPaths

This preset is designed to work with the specified shaders.



static List<CwPreset>
CachedPresets

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

NOTE  This is editor-only.


void
AddTo
Renderer root

This method applies the preset components to the specified paintable.

NOTE  This is editor-only.



CwProceduralSetup

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.




CwReadColor

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

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

CwGroup
Group

Only the CwPaintableTexture 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.




CwReadColorEvent

This component allows you to perform an event when the attached CwReadColor 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.




CwReader

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



CwRemap

This class allows you to remap a texture from one UV layout to a different UV layout.



CwRenderDepth

This component renders scene depth to a RenderTexture. This scene depth can be set in a CwPaint___ 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.



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
Multitap

If you enable this, then the depth mask will be sampled multiple times per pixel, allowing you to paint both foreground and background objects with little to no edge seams.



bool
ReadInStart

Should the scene depth be rendered in Start?



bool
ReadInUpdate

Should the scene depth be rendered every frame in Update?



static LinkedList<CwRenderDepth>
Instances

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



void
ReadNow

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




CwRotate

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.




CwSerialization

This class handles the low level de/serialization of different paint 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<CwHash, CwModel>
HashToModel

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



static Dictionary<CwModel, CwHash>
ModelToHash

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



static Dictionary<CwHash, Texture>
HashToTexture

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



static Dictionary<Texture, CwHash>
TextureToHash

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



static Dictionary<CwHash, CwPaintableTexture>
HashToPaintableTexture

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



static Dictionary<CwPaintableTexture, CwHash>
PaintableTextureToHash

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




CwSlot

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.




CwSpawner

This allows you to spawn a prefab at a hit point. Hit points will automatically be sent by any CwHit___ 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.




CwSpiral

This component moves the current Transform in a spiral pattern.



CwStateManager

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


static void
StoreAllStates

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

NOTE  This should be called before you perform manual changes to paintable textures.


static void
PotentiallyStoreAllStates

This method should be called if you're about to send paint hits that might apply paint to objects. If so, StoreState will be called on all active and enabled CwPaintableTextures



static void
ClearAllStates

This method will call ClearStates on all active and enabled CwPaintableTextures.



static void
UndoAll

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



static void
RedoAll

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




CwTapThrow

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?




CwTextureHash

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.



CwHash
Hash

The hash code for the texture.




CwToggleParticles

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?




CwToggleScript

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?




CwTranslate

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.




CwUndoAction

This component performs an action every time you undo/redo/etc.


bool
PreUndoAll

Listen for CwStateManager.UndoAll calls?



bool
PreRedoAll

Listen for CwStateManager.RedoAll calls?



UnityEvent
Action

The event that will be invoked.




CwVrManager

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<CwVrManager>
Instances

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




CwVrTool

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<CwVrTool>
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.




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 IHit

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 IHit

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



IHitPoint IHit

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



IHitQuad IHit

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



IHitTriangle IHit

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



Index

Thank you for using Paint in 3D ❤️

How do I upgrade?

Video Tutorials

Getting Started

Mesh Analysis

Mesh Fixer

Secondary UV

Presets

Fixing UV Seams

Undo & Redo

Blend Modes

Colliders

Skinned Mesh Painting

Drawing Connected Lines

Painting Multiple Textures

Advanced Optimization

Known Issues

Paintable Presets

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?

Can I paint in the editor's Scene window?

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+

Spaceships - Build & Destroy

Modular Backgrounds

Paint in 3D

Paint in 2D

Paint in Editor

FLOW

Destructible 2D

Space Graphics Toolkit

Space Graphics Planets

Volumetric Audio



Versions

4.1.4

4.1.3

4.1.2

4.1.1

4.1.0

4.0.5

4.0.4

4.0.3

4.0.2

4.0.1

4.0.0

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



Components

CwActionOnEnable

CwBlendMode

CwButtonClearAll

CwButtonRecolor

CwButtonRedoAll

CwButtonUndoAll

CwChangeCounter

CwChangeCounterEvent

CwChangeCounterFill

CwChangeCounterText

CwChannelCounter

CwChannelCounterEvent

CwChannelCounterFill

CwChannelCounterText

CwClone

CwCloneMirror

CwColor

CwColorCounter

CwColorCounterEvent

CwColorCounterFill

CwColorCounterText

CwCommand

CwCommandDecal

CwCommandFill

CwCommandReplace

CwCommandReplaceChannels

CwCommandSerialization

CwCommandSphere

CwCommon

CwCoordCopier

CwDestroyAfterTime

CwDestroyer

CwDilate

CwDragPitchYaw

CwGraduallyFade

CwGroup

CwGroupData

CwHashedMaterial

CwHashedModel

CwHashedTexture

CwHit

CwHitBetween

CwHitCache

CwHitCollisions

CwHitNearby

CwHitParticles

CwHitPointers

CwHitScreen

CwHitScreenBase

CwHitScreenFill

CwHitScreenLine

CwHitThrough

CwInputAxis

CwLineConnector

CwMask

CwMaterialCloner

CwMeshAnalysis

CwMeshFixer

CwMeshModel

CwModel

CwModifier

CwModifierList

CwModifyAngleRandom

CwModifyColorRandom

CwModifyHardnessPressure

CwModifyHardnessRandom

CwModifyOpacityPressure

CwModifyOpacityRandom

CwModifyPositionRandom

CwModifyRadiusPressure

CwModifyRadiusRandom

CwModifyScaleRandom

CwModifyTexturePressure

CwModifyTextureRandom

CwPaintableManager

CwPaintableMesh

CwPaintableMeshAtlas

CwPaintableMeshTexture

CwPaintableObjects

CwPaintableState

CwPaintableTextureMonitor

CwPaintableTextureMonitorMask

CwPaintAction

CwPaintDebug

CwPaintDecal

CwPaintFill

CwPaintFromCode

CwPaintMultiplayer

CwPaintReplace

CwPaintReplaceChannels

CwPaintSphere

CwPointConnector

CwPointer

CwPointerMouse

CwPointerPen

CwPointerTouch

CwPreset

CwProceduralSetup

CwReadColor

CwReadColorEvent

CwReader

CwRemap

CwRenderDepth

CwRotate

CwSerialization

CwSlot

CwSpawner

CwSpiral

CwStateManager

CwTapThrow

CwTextureHash

CwToggleParticles

CwToggleScript

CwTranslate

CwUndoAction

CwVrManager

CwVrTool

IClone

IHit

IHitCoord

IHitLine

IHitPoint

IHitQuad

IHitTriangle