🚀 Getting Started
Get painting in minutes with this step-by-step setup guide.
📦 Installation
Via Unity Package Manager (UPM)
- Open Window → Package Manager in Unity
- Click the + button → Add package from git URL...
- Enter the package URL:
https://github.com/deepwavegame/com.deepwave.simplepainter.git
- Click Add and wait for the import to complete
Via Local Import
- Download the package
.tgzor clone the repository - Open Window → Package Manager
- Click + → Add package from disk...
- Navigate to the
package.jsonfile and select it
SimplePainter requires the Unity Input System package for PaintTriggerRaycast. The Package Manager will prompt you to install it if missing.
🎯 Minimal Scene Setup
Follow these 7 steps to create your first paintable scene:
Step 1 — Create a ChannelDefinition Asset
Right-click in the Project window and select:
Create → Deepwave / Simple Painter / Channel Definition
Set the ShaderProperty to _MainTex to paint on the main albedo texture.
Step 2 — Add PaintSurface + Paintable to Your Mesh
Select your mesh object in the scene and add:
- PaintSurface component
- Paintable component (auto-resolved if on the same GameObject)
Then add your ChannelDefinition to the surface's channel list.
PaintSurface auto-resolves a Paintable from the same GameObject. You can also have multiple Paintable objects sharing one surface for multi-mesh painting.
Step 3 — Add StrokeSampler
Add StrokeSampler to a controller GameObject. Assign a StrokeMethodConfig ScriptableObject (e.g., the included Bezier config).
Step 4 — Add PaintTriggerRaycast
Add PaintTriggerRaycast to the same controller object. Configure the Input Actions for paint and pointer input.
Step 5 — Add StandardBrush
Add StandardBrush to the same controller object. Reference the PaintSurface and add a channel entry for your ChannelDefinition.
Step 6 — Add StandardCommitter
Add StandardCommitter to the same controller object. Reference the PaintSurface.
Step 7 — Press Play! 🎉
Enter Play mode and paint on your mesh!
🔄 Component Relationship Diagram
Tools don't receive events — they poll the surface's StampBatch every frame. The surface clears its batch in LateUpdate after all consumers have processed it.
🌊 Adding Fluid Simulation
To enable fluid simulation on your paintable surface:
// 1. Add a PaintEnvironment to the paintable object
// 2. Replace StandardCommitter with a SimulationPaintCommitter variant
// 3. Assign the PaintEnvironment to the PaintSurface:
paintSurface.PaintEnvironment = environment;
// 4. Enable dynamics on your ChannelDefinition (EnableDynamicsTarget = true)
// 5. Configure PBR channel bindings on the committer
// (primary color channel + secondary smoothness/metallic/normal)
If you're using a SimulationPaintCommitter, make sure the primary ChannelDefinition has EnableDynamicsTarget = true. Without it, the simulation won't have a velocity+mass buffer to work with.
🎨 Multi-Channel PBR Painting
Paint across multiple PBR channels simultaneously:
// Create multiple ChannelDefinition assets:
// Albedo → _MainTex (Color)
// Normal → _BumpMap (Normal)
// Metallic → _MetallicGlossMap (Scalar, mask: R)
// Roughness → _MetallicGlossMap (Scalar, mask: A)
// Add all channels to PaintSurface
// Add matching ToolChannels to StandardBrush
// Toggle channels at runtime:
brush.Channels[0].Intensity = 1.0f; // Albedo ON
brush.Channels[1].Intensity = 0.0f; // Normal OFF
Multiple channels can target the same shader property with different ChannelMask values. For example, Metallic writes to the R channel and Roughness writes to the A channel of _MetallicGlossMap.
🔀 Runtime Switching
Swap configurations and targets at runtime without destroying objects:
// Switch stroke method
strokeSampler.SwitchConfig(bezierConfig);
// Switch paintable target
paintSurface.Switch(otherPaintable);
// Reset / Clear the surface
paintSurface.Reset(); // Restore InitTexture
paintSurface.Clear(); // Wipe to default background
Use strokeSampler.SwitchConfig(newConfig) to change stroke behavior. This avoids mutating shared ScriptableObject assets and lets users revert by selecting another config.