I’m displaying a preview of the decal above the mesh in 3D space where the decal should stamp and its rotation of how it should look when stamped. However, I can’t get the stamped decal rotation part to work correctly because the UV island rotates itself (as suggested in the PaintStroke() comment). Do you know how to get the BrushDecalRotation to compensate for mesh UV islands that may be rotated?
Seeing as how the decal is always oriented the same way as the UV map gives me hope that there’s a way. For example this arrow decal on the helmet:
There has to be some way to get the UV map’s current rotation at a given point (where I want to stamp) in the containing triangle’s coordinate system (normal, bitangent, tangent). I’m not good at this kind of math but I feel like there should be a way. If I cast three rays into the triangle and find those world space coordinates and UV coordinates, there has to be some kind of mapping between the coordinate systems that I can use, right? Any ideas?
@**DanimalsOnParade **- One workaround I can think of is to let the preview itself be driven by the plugin and have the user control BrushDecalRotation (for the preview decal). When the user confirms the decal placement you will have obtained the correct rotation offset needed for BrushDecalRotation and can use that in a second pass (for stamping the actual decal) and it should look right.
The first task will be to switch between a “preview mode” and “normal mode” in your material, where the preview mode uses an additional Don UV node that is overlayed on top of the regular material. The next challenge will be to “animate” the decal preview every tick instead of “painting over” with it (default behavior). To achieve that you can supply a custom render brush (instanced from the default M_PaintBrush_Regular), but with blend mode set to “Opaque” so that it will effectively overwrite (and animate) the position of the preview decal each tick (a brute-force alternative is to clear the render target for your preview layer for each new preview pose, but that’s not elegant).
Re: your rotation mapping idea - I’m not sure, though I can say it wouldn’t work for skeletal meshes because UV coord/mesh data is optimized out for them (that’s incidentally the reason why the “fast-path” / collision UV workflow is only supported for static meshes)
A systemic way to address this would be to somehow implement seamless decal/text projection, which would make all image projection UV-independent and thus make rotation a non-issue.I haven't found a way to do this yet.
If anyone reading this knows how to ***"project an image seamlessly across a mesh at a desired world location"***, I would dearly love to know :)
I actually thought of a similar idea of having another mesh slightly larger than the original with a transparent texture except for where the decal is painted and then animate it every tick by clearing the render target. I like your idea more but I am already using all 3 layers of UV0 (paint, decal, and text). Is it possible to have more than 3 layers for the UV0 workflow? Can I make a Don_Mesh_Paint_UV0_Layer3, Don_Mesh_Paint_UV0_Layer4, and so on? That’s probably the workaround method that I will take on confirming how to make a 4th layer on UV0. I could always put the decal and text on the same layer, freeing up the third default layer to use for the preview, but I kind of like having them separate if possible.
Edit:
I think I figured out how to make new layers. Just make a new folder in DonMeshPaintingContext/Materials/UvFunctions/MeshSpace for Layer3, Layer4, etc. and then duplicate the existing materials from one of the existing layers and move them into the new folder and change the Tex Object parameter name in the Materials.
Your proposed method of previewing is working great on decals! I can rotate them freely using the arrow keys before stamping in the same rotation as the preview.
The rotation on the PaintText node doesn’t seem to be working. Regardless of what number is fed into the Rotation variable on the blueprint node, the text isn’t rotating. Might be a bug?
**Scope: **Text rotation does not work while stamping text, the Rotation parameter is not applied. This was a regression that popped up in a recent build.
Patch Instructions:
Migrate the plugin to your project folder (see this post for details, you only need Steps 1-4)
Open the file <YourProject>/Plugins/DonMeshPainting/Source/DonMeshPainting/Private/DonMeshPaintingHelper.cpp
Go to Line 54 and replace that entire line with the following snippet: FDonPaintStroke stroke = FDonPaintStroke(Hit.ImpactPoint, 1.f, Rotation, Color, Hit.FaceIndex, strokeParams, Text, Style);
Build your project from Visual Studio, the plugin will be recompiled along with it and now carries the fix!
[HR][/HR]
Hotfix Marketplace ETA: As with the previous hotfix, I am unable to submit this to the marketplace until they officially clear the current backlog of updates for this plugin.
If you are a plugin user who isn’t familiar with modifying plugins from Visual Studio, just drop a support email (link) for assistance with installing this patch.
[HR][/HR]
Thanks again to @**DanimalsOnParade **for reporting this
Fun-fact: Both hotfixes so far have occurred in the same file, in the same line of code
How would I go about making an eraser paint brush for decals? I don’t want to clear the whole render target, I just want to be able to erase whatever part of the decal is under the paint brush. I would expect to be able to paint to decal layer with all black color and white on the alpha but that doesn’t seem to work. The alpha channel of the render target doesn’t seem to be changing when I try this.
Some background is necessary: The default render brushes use AlphaComposite and Additive blend modes. These allow us to use just a single texture (per layer) for creating effects instead of storing one extra texture (for reading) and a main texture (for writing) as seen in some other techniques.
Erasure requires the second setup and that hasn’t been built in the plugin yet as the the early focus was on gameplay usecases (which usually only accumulate effects).
Implementing this requires both code changes and a new render brush. The code will need to accommodate dual textures (read texture, write texture) per layer and the new render brush needs to read from the read texture, mask out the erased areas and render it out to the write texture with an opaque blend mode.
This is just theory though as I haven’t tried this out, there could be complex edge-cases involved. I can try targeting this for the next engine version update whenever that is…
Ah that makes sense. Thank you for the explanation. I hope you do add this feature and the infinite undo/redo in the next version. Epic seems to release new engine versions every 3 months or so and I expect 4.18 to be released in October or November at the latest.
I have another feature request. I’d like something similar to Photoshop’s Clone Stamp tool. So I give the BP paint node a texture and it paints directly from the texture, translating the HitResult’s UV coordinates to the provided texture and then paints on to the RenderTarget. I’d like it to paint to the RenderTarget (like decal stamping) so that I can swap out multiple textures to clone from on one model. An example would be if I wanted to clone stamp a camouflage texture onto my model and then stamp a different pattern elsewhere on the same model. This would be affected by UV seams, but that can be worked around. Unlike the sample project painting a lava material, that happens after the Render Targets so we couldn’t have unlimited dynamic textures. If I’m wrong and this is already possible, please correct me!!
Yes, for blending in an unlimited number of texture as you describe we’d need a setup where the BP paint node takes in a texture parameter per-stroke for the render brush to apply.
Right now there’s some features requested by other users off the forums to consider too, so will have to prioritize these and see how many features can actually make it into the next build. Will post a summary of that down the line!
That’s fair! I have my fingers crossed at least for having the second opaque RenderTarget added in with the capability to paint directly to it for things like an eraser brush to paint erase decals and text per layer.
Just a quick suggestion, have you considered releasing an executable for the sample projects? I’m currently debating if I should get your plugin or continue working on my own approach for performance reasons. I imagine others would also enjoy the convenience of just fiddling themselves and see what the plugin is capable of doing.
How would I go about adding one of the paint layers into a brush material used by the paint functions? I’m trying to do some creative masking based on something created on another paint layer.
I duplicated the existing paint brush material and added the Don_Mesh_Paint_UV0_Layer7 node to it, but that node doesn’t seem to update based on what’s actually added to that render target (I can confirm that the render target is changing by painting it out onto a mesh). I get the feeling that instead I need to pass the render target in as a Texture Object Parameter in DonMeshPainterComponent with the rest of the “common parameters”, but I don’t know how to get a reference to that render target in that bit of DonMeshPainterComponent.
Visual example - Same paint brush material except I inserted a render target into it at the end:
^ @**DanimalsOnParade **- This is somewhat related to your recent feature request. Layer nodes can’t be used inside the render brush because of context: layers reside inside a dynamic material on a per-mesh basis, while a render brush is a standalone utility material (used to render a stroke).
It is possible to magically wire a layer node in a render brush to the actual layers of the mesh being painted, but can you clarify the need for this?
With this setup there are complications (eg: you might be painting on Layer 2 but unwittingly use a render brush which also tries reading from Layer 2, etc) making it a slightly messy thing to support as as formal feature!
I’m trying to implement a paint mask like you would find in ZBrush. By that I mean, I paint a normal paint brush on a special render target. Anything on this render target blocks any of the normal painting or decal stamping from putting color in that location. For example, if I cover my entire model with the masking paint except for a small star, then when I stamp a huge decal in that area, only the area of the star that isn’t masked receives the color from the stamped decal. I hope that makes sense. Since the masking paint only needs to be one color, I can alternate between black and white colors (ignoring the render target’s alpha channel) and be able to erase the masking paint. This doesn’t replace erasing decals or text though, that’s a still needed feature. This just allows for a non-destructive way of creating shapes and lines that can be painted or stamped on. It’s not easy to make a straight line on a curved convex model with a mouse, so being able to paint and erase with a mask can help create that perfect shape :).
I have everything working except for the part where the normal paint brush that basic paint and decals use read from the masked render target to not apply paint in masked areas (shown in the screenshot in my previous post).
In my example, masking paint is hardcoded to Layer 7 with its own special brush separate from basic painting, decal stamping, and text stamping. This masking brush will never need to read from Layer 7, only render to it. All the other layers use a different render brush that will read from Layer 7 but never render to it. So assuming I can magically wire Layer 7 only into the brush that reads from it but not the other brush that writes to it, I should be okay.
Is it possible to clear placed decals away from selected character and other objects? Having character pool so would need reset ther possible wound decals.
@**DanimalsOnParade **-Would it be preferable to have a pre-canned set of texture masks in the render brush that you can switch between using your own logic? If you need fully paintable masks defined at runtime I’d need to expose a new parameter “Mask Layer To Read” or something to that effect so that the plugin knows which layer to route to the render brush, maybe a new node for that too. I’ll add this to the stack of feature requests, it’s piling up a bit now though
@**SaOk **- Hey, to confirm your requirement - you have a character with wound decals painted and need to reset the wounds at some point? (maybe when they’re healed?) If you need this urgently you can mail me (link) for follow up - it’s only a couple of lines of code to reset all paint for an actor so it’s easy to add this.
Btw if you have a large pool of characters, each one will acquire separate would textures with no additional effort on your part. Unlimited number of actors (even dynamically spawned ones) are supported.
Yes, sir. I’d need fully paintable masks. If you expose this new parameter, could you make it a TArray< layers > to make it future proof for any number of layers? I can’t think of an application for more than one layer off the top of my head, but if the amount of extra work is minimal (presumably just a for loop through the TArray), it might not be a bad idea to save more work down the road if someone else needs more than one.