19-01-2026, 11:10 AM
(This post was last modified: 19-01-2026, 11:51 AM by josemendez.)
(19-01-2026, 02:26 AM)CptnFabulous Wrote: I’m trying to get cloth garments on a character model to behave looser without entirely slipping off, and from my research I think modifying the skin constraints will be the best solution.
Hi!
You need to increase the skinRadius property of the constraints. This property controls how far from the animated position the cloth can get, so it's literally the "looseness" of the cloth.
(19-01-2026, 02:26 AM)CptnFabulous Wrote:
From what I can tell, constraint batches are created only when a particle is created with different settings, and they’re grouped into batches at runtime to limit the number of different settings in memory.
Batches are *always* created, their purpose is not to limit the number of different settings (particle or constraint setting deduplication is not related to this at all, you might as well have the exact same properties for all particles and constraints and you'd still get batches). The reason batches are created is to prevent race conditions, which are a basic problem in multithreaded code:
When you have multiple threads working on something, it's fine if the data they're working on is completely independent: they can go on reading and writing data without stepping on each other's toes. However, as soon as two or more threads are reading from/writing to the same memory location you must ensure they do it in a specific, predictable order or otherwise you'd get incorrect results. For instance if you have two threads trying to naively increment the same variable (let's say, A = 5), you may get this order of operations:
- thread #1 reads value of variable A, 5.
- thread #2 reads value of variable A, 5.
- thread #1 does A = A+1, so A = 6.
- thread #2 does A = A+1, so A = 6.
Since thread #2 didn't wait for thread #1 to write its result into A before reading A and incrementing it again, you end up with A = 6 even though the expected result would be A = 7, since 2 threads have incremented the variable.
There's 3 ways to solve race conditions like these (from worse to better performant):
- Use thread synchronization primitives like mutexes or semaphores to make sure only one thread can be executing a specific section of code at any given time.
- Use atomic/interlocked instructions (in many cases, basic operations like integer addition/subtraction or compare and swap (CAS) are the only atomics available, so while faster than mutexes they're a bit limiting). An atomic operation performed by a thread is guaranteed to complete before any other thread steps in.
- Avoid having race conditions in the first place.
Batches are a way of implementing option #3, that is, ensuring race conditions aren't possible. They're based on a concept called graph coloring, which strives to segregate a bunch of items into as few groups as possible, in such a way that items in the same group don't share any specific property. This makes it safe to process all items in a group in parallel.
In Obi's case, the items are constraints, and they're batched so that constraints in the same batch they don't affect the same particles. All constraints in a batch can be processed by multiple threads, and we know no two threads will simultaneously attempt to operate on the same data (particles). For physics, batching also has additional benefits (minimum jittering since the order in which constraint corrections are applied to particles is always the same every frame).
Skin constraints are a bit of a special case regarding batches, since each skin constraint only affects one particle. So typically you'd only need one batch of skin constraints as they don't share particles with other skin constraints. Other constraints (distance constraints, which operate on 2 particles each, or bend constraints which operate on 3 particles) do need multiple batches in most cases.
Batches are created during blueprint generation and stored in the blueprint, since graph coloring is a lengthy process (can take a few seconds). At runtime, batch data is simply copied over to the solver when the actor is inserted into the simulation.
(19-01-2026, 02:26 AM)CptnFabulous Wrote:
But I don’t really know how to add new ones. For now I only need one set of constraints for the entire cloth actor, and they don’t really need to change after being generated (although if they can that might help with debugging). The cloth shapes are being generated procedurally from procedural meshes, so it needs to all work through code.
You don't have to add new batches to loosen the cloth. Assuming you already have a skinned mesh and a cloth blueprint generated from it, you just need to change the skin radius of already existing constraints in already existing batches. In your case it might be possible to modify the blueprint itself after generating it (write to blueprint.skinConstraints.skinRadiiBackstop) before the cloth gets loaded into the solver.
(19-01-2026, 02:26 AM)CptnFabulous Wrote: I looked at the example in that documentation (which is conveniently already for setting the skin constraints), and tried copying it and turning it into a standalone function with parameters instead of hardcoded values. But I got null errors when trying to retrieve the batches, so there didn’t seem to be values there to edit. I realised that the example code uses an ObiSkinnedCloth, whereas our current system just uses regular ObiCloth components, so I switched the cloth, renderer and mesh renderer components to the right ones. But now I'm getting an out of range error when trying to get the index offset and access the correct batches.
I’m sure I’m going about this completely the wrong way. Maybe I’m running this code at the wrong point during the generation, or I’m meant to modify values in the blueprint rather than the cloth itself. Any help would be greatly appreciated.
Thanks!
The main issue with skinned cloth is that it needs to be, well, skinned. In cases where you have lots of different clothing that must be worn by a single character/mannequin (or even worse, multiple characters of varying body shapes/sizes) the amount of artist work required skyrockets, as they must now skin every clothing piece to every possible body that may wear it. This typically makes it an impractical approach unless you can procedurally skin the cloth mesh to the character's skeleton upon deciding which cloth/body combination you want, which presents its own set of hurdles. If you've already figured out a solution to this or if it's not a problem in your case, forget what I just said.
It would help taking a look at your code. If you're not comfortable sharing it here, a PM or an email is fine, I'm sure it's a relatively quick fix.
cheers!

