@linenum
The paint is applied in 3D to your mesh from where it hits your colliders, so if the gap between your collider and your cloth mesh surface is greater than the radius of your paint brush, then paint won’t appear. You can add the CwPaintDebug component to see where the paint is applying.
If you can’t make your collider more accurate for performance reasons then look at the Zombie Blood demo scene. In this scene the paint is rotated to match the camera, and the paint is elongated to go through the whole zombie.
@SixDayStudio
Paint is applied as soon as fingers touch the screen, but two finger gestures require some time before both fingers touch down. So unless you touch both fingers at exactly the same time, you will always apply some paint.
To fix this you must either undo paint applied before your gesture is detected, or change your control scheme to isolate gestures from painting like having a paint tool the user must select.
Hi, we’ve had Paint In 3D in our project and we seem to be getting a lot of issues with git with your asset. It seems like the project hash keeps changing on its own. This happens without even refreshing anything in the project, just by simply recompiling certain scripts. The line ending also keeps changing.
Hope you’re doing well! I’ve been really enjoying the Paint in 3D addon, particularly how it allows placing decals on virtually any surface. However, I’m experiencing some performance issues and would appreciate your guidance.
Project Overview:
Game contains 15-20 destructible objects (always in player view)
Each object shatters into ~100 fragments on average
Each fragment uses “CWPaintableMeshTexture” (512x512 resolution)
BaseMap slot configuration
“CWPaintableMesh” set to “OnFirstUse” for bullet hole decals
Current Performance:
Without Paint in 3D: 100 FPS in-editor with 18 destructible objects
With Paint in 3D (few dozen active fragments): 15-17 FPS
Reduced CWPaintableTexture resolution: No significant improvement
I really appreciate your work on this addon - it’s greatly simplified the implementation of effects like bullet holes and blood stains on various surfaces. Thank you for your time, and I look forward to your response!
At start:
Active Paintable Objects: 461.00
Active Paintable Textures: 461.00
Total Memory (MB): 829.93
Frame Time (ms): 29.07
FPS: 34.40
On exiting play mode after a minute:
Active Paintable Objects: 461.00
Active Paintable Textures: 461.00
Total Memory (MB): 918.59
Frame Time (ms): 30.73
FPS: 32.54
TLDR: Each fragment loads it’s own 512x512 texture duplicate, instead of using one big texture to paint, which was quite obvious but i though it would sort itself out. Also what is the memory leak about? Going to do some more testing.
Alright, after some more time i realized that the issue is already solved for me, via CWPaintableMeshAtlas… except it isn’t. Fragments check all the boxes like:
The parent is properly set up with both CwPaintableMesh and CwPaintableMeshTexture
The parent reference is correctly assigned
The parent is activated
The fragments share the same material and UV space
And yet when adding CWPMesh and CWPMeshTexture to the first fragment, and adding Mesh Atlas to every other fragment, while setting the parent field reference to the first fragment, all fragments except the parent fragment cannot receive paint. Am i doing something wrong?
I overlooked needing to assign fragments to the “OtherRenderers” list. After implementing the atlas setup, FPS rose to around 30. I’ve used most options - what other optimization techniques could I try, or was Paint in 3D just not made to handle such amount of paintable objects?
As a last resort, I manually optimized each object to minimize the number of fragments, achieving a total of 26 fragments per object. After reconfiguring everything—including atlas painting and activating objects on first use—the framerate improved to about 50 FPS with 9 destructible objects, even when all fragments are activated. However, this is still far from the goal of 15–20 objects running at 70–80 FPS.
What I don’t get is why rendering a paintable object is three times more expensive, both in shadow casting and opaque object rendering?
I’ve also considered an optimization technique where a paintable object is deactivated when not in use and reactivated when needed. This seems viable since many fragments, after being shot once, are rarely interacted with again. Could this work? Based on the “OnFirstUse” option in the CWPaintableMesh component, it seems possible, but I haven’t been able to figure out how to implement it.
After giving it some thought, I’ve decided to stick with the regular URP projected decal setup for static meshes while keeping Paint in 3D decals for skinned meshes. It’s unfortunate that Paint in 3D doesn’t support batching, though understandable, but it is what it is.
Still, thanks again for this fantastic addon! I wouldn’t have been able to apply decals to skinned meshes without it.
Hey, we are using CW Painter and need to count pixels of a specific color on a painted texture. Currently, we retrieve the texture pixels and count them manually. However, for 2048x2048 textures, this causes Unity to freeze. Do you have any ideas?
Hi
Whenever I handle prefabs with a CwChangeCounter, the serialized “cooldown” field of the base class CwPaintableTextureMonitor changes and clutters version control with these changes.
Questions: Does the CwChangeCounter need to have the [ExecuteInEditMode] attribute? Does the “cooldown” field need to be serialized?
@globichopf
Thanks for pointing this out. Indeed, this value probably doesn’t need to be serialized, and the component probably doesn’t need to execute in edit mode. I’ll do some more testing!
@lenrox823
The CwColorCounter component is designed to do this, and it can use downscaled textures to speed up performance. If you want to do it yourself without any performance impact then you must use an AsyncGPUReadback (if supported by your GPU) to get your texture over time, then you must count the pixels in a separate thread e.g. using Unity’s Job system.
Paint in Editor is such a powerful tool it is such a waste we cannot choose a mirror axis for our brushstrokes. Would immediately replace a lot of other tools in my current workflow.