Posts: 48
Threads: 13
Joined: Jan 2025
Reputation:
0
Hi,
We're using Obi Cloth alongside Lattice Modifier Asset on skinned cloth. Because ObiSkinnedClothRenderer.ValidateRenderer() sets skinnedMeshRenderer.sharedMesh = null at runtime and rendering goes through Graphics.RenderMesh on a batched mesh, any SkinnedMeshRenderer-based modifier silently no-ops — which is a perfectly reasonable design, it just isn't reachable from outside.
We solved it by dispatching the deformer's compute kernel over our renderer's slice of the batch mesh after your render system writes it. That works well, but it needs three protected fields on ObiClothRenderSystem (sortedRenderers, batchList, vertexOffsets), which we currently reach by reflection. Two small additions would remove that entirely:
1. A public renderer → batch-geometry accessor. DynamicRenderBatch and its mesh field are already public; this just exposes the mapping:
// ObiClothRenderSystem
public bool TryGetRenderGeometry(ObiClothRendererBase renderer,
out Mesh batchMesh, out int vertexOffset, out int vertexCount)
2. Set GraphicsBuffer.Target.Raw unconditionally in DynamicRenderBatch.Initialize, not only when gpu: true. On the Burst backend we currently set mesh.vertexBufferTarget after the buffers already exist. It does work — your Compute backend relies on the same ordering — but it's undocumented, so we'd rather not depend on it.
^Claude Feedback after finding a solution of integrating
Posts: 82
Threads: 19
Joined: Jul 2025
Reputation:
0
Actually I also wanted to give feedback about this.
I wanted to bake, reuse or modify rendered mesh, but it's rendered internally in completely different way and it's hard to access it out of the box.
Can we get improvement here so it's possible to access deformed cloth mesh? Or what is the best strategy to do so?
My use case is to bake distance field from deformed cloth mesh.
Posts: 6,808
Threads: 28
Joined: Jun 2017
Reputation:
443
Obi Owner:
10-09-2026, 06:33 AM
(This post was last modified: 10-09-2026, 06:37 AM by josemendez.)
(09-09-2026, 03:30 PM)Qriva0 Wrote: Actually I also wanted to give feedback about this.
I wanted to bake, reuse or modify rendered mesh, but it's rendered internally in completely different way and it's hard to access it out of the box.
Can we get improvement here so it's possible to access deformed cloth mesh? Or what is the best strategy to do so?
My use case is to bake distance field from deformed cloth mesh.
Hi!
ObiClothRenderSystem exposes a BakeMesh method that allows you to access the deformed mesh for further processing/rendering:
Code: BakeMesh(ObiClothRendererBase renderer, ref Mesh mesh, bool transformToActorLocalSpace = false)
You pass the cloth renderer, a Mesh that will hold the mesh data extracted from the internal renderer, and an optional flag to transform the mesh to the actor's local space (they're expressed in solver space by default).
This works for both the Burst and the Compute backends. For instance:
Code: ObiClothRenderer renderer = (ObiClothRenderer)command.context;
if (renderer.actor.isLoaded)
{
var system = renderer.actor.solver.GetRenderSystem<ObiClothRenderer>() as ObiClothRenderSystem;
if (system != null)
{
var mesh = new Mesh();
system.BakeMesh(renderer, ref mesh, true);
// do whatever you want with your mesh
}
}
Posts: 6,808
Threads: 28
Joined: Jun 2017
Reputation:
443
Obi Owner:
10-09-2026, 07:20 AM
(This post was last modified: 10-09-2026, 08:11 AM by josemendez.)
(09-09-2026, 02:23 PM)Jawsarn Wrote: Hi,
We're using Obi Cloth alongside Lattice Modifier Asset on skinned cloth. Because ObiSkinnedClothRenderer.ValidateRenderer() sets skinnedMeshRenderer.sharedMesh = null at runtime and rendering goes through Graphics.RenderMesh on a batched mesh, any SkinnedMeshRenderer-based modifier silently no-ops — which is a perfectly reasonable design, it just isn't reachable from outside.
We solved it by dispatching the deformer's compute kernel over our renderer's slice of the batch mesh after your render system writes it. That works well, but it needs three protected fields on ObiClothRenderSystem (sortedRenderers, batchList, vertexOffsets), which we currently reach by reflection. Two small additions would remove that entirely:
1. A public renderer → batch-geometry accessor. DynamicRenderBatch and its mesh field are already public; this just exposes the mapping:
// ObiClothRenderSystem
public bool TryGetRenderGeometry(ObiClothRendererBase renderer,
out Mesh batchMesh, out int vertexOffset, out int vertexCount)
^Claude Feedback after finding a solution of integrating 
Hi!
I think there's a much simpler and robust way: a callback in RenderSystem.Render() that takes the batch and renderer info (vertex range) as argument. This not only gives you access to all required data (no need to manually iterate over batchList or check sortedRenderers: you directly get access to the batch/mesh that's about to be rendered) but also helps with timing - you get the callback right after the mesh data is final for that frame, and right before it gets rendered, so it's not possible to have 1-frame late / data not yet ready issues.
Since you need to iterate over all renderers to get the vertex offset, it's best to filter out by renderer as you go (we'd implement this using IEnumerable). Using an accessor by renderer as you propose requires to go over all renderers in the batch for each individual renderer you're interested in, and if you're interested in several of them (multiple TryGetRenderGeometry calls) that's quadratic cost - extremely wasteful stuff.
So the whole thing would look like:
Code: // this is automatically called by the render system at the right moment in time,
// and gets batch, renderer and vertex range by parameter
OnWillRenderMesh(DynamicRenderBatch batch, RendererInfo rendererInfo)
{
if (rendererInfo.renderer == myRenderer) // or any other "I'm interested in this renderer!" check
{
// dispatch your stuff over batch.mesh.GetVertexBuffer [rendererInfo.vtxOffset, rendererInfo.vtxOffset + rendererInfo.vtxCount]
}
}
(09-09-2026, 02:23 PM)Jawsarn Wrote: 2. Set GraphicsBuffer.Target.Raw unconditionally in DynamicRenderBatch.Initialize, not only when gpu: true. On the Burst backend we currently set mesh.vertexBufferTarget after the buffers already exist. It does work — your Compute backend relies on the same ordering — but it's undocumented, so we'd rather not depend on it.
We'd rather not always allocate an extra GraphicsBuffer if it's not going to be needed - in the Burst backend, you'd usually modify the mesh using a job, not a kernel dispatch, so having a dangling GPU buffer there when no one is going to consume it is not that great.
If you need to do compute-based processing on the Burst backend, you can set the buffer target flags and request a vertex gpu buffer at any point in time however - we do it at initialization time but afaik there's no documented limitation: https://docs.unity3d.com/6000.0/Document...arget.html
Do remember to dispose the graphics buffer once you no longer need it, though.
Posts: 6,808
Threads: 28
Joined: Jun 2017
Reputation:
443
Obi Owner:
Jawsarn, I've implemented a proof of concept of the mechanism described in the post above. It's written over Obi 8 code but should work fine in previous versions as well. Drop me an email at support(at)virtualmethodstudio.com and I'll make the files available to you. This way I get feedback and we refine the design together, and you get stable code that won't change in the near future.
cheers,
Posts: 48
Threads: 13
Joined: Jan 2025
Reputation:
0
10-09-2026, 10:30 AM
(This post was last modified: 10-09-2026, 10:58 AM by Jawsarn.)
Hi again,
That's a better design than what "I" proposed.
Agreed on the GPU buffer.
One gotcha worth a doc line: on the Burst backend the buffer needs re-acquiring each frame rather than caching, since Render() rewrites the mesh via SetVertexBufferData every frame. We cached it initially and the cloth rendered completely undeformed — and reading the buffer back after dispatching still showed the deformation present, so the obvious way to verify gives a false positive. Probably worth calling out explicitly because DynamicRenderBatch caches gpuVertexBuffer once, which is right on Compute where nothing rewrites the mesh, but is the pattern people will carry onto Burst.
Thanks
(10-09-2026, 09:01 AM)josemendez Wrote: Jawsarn, I've implemented a proof of concept of the mechanism described in the post above. It's written over Obi 8 code but should work fine in previous versions as well. Drop me an email at support(at)virtualmethodstudio.com and I'll make the files available to you. This way I get feedback and we refine the design together, and you get stable code that won't change in the near future.
cheers,
Hadn't refreshed before my last message. I've sent you a message on the email.
Best regards,
|