15-08-2023, 09:59 PM
(This post was last modified: 16-08-2023, 02:07 PM by sebjf.
Edit Reason: Added more complete code sample
)
Hi,
I am trying to use ObiCloth to make an adhesive pad in VR, which the user can place on a surface, and pull away.
The way I am approaching this, is to use the collision callback to detect when particles are within a given distance of the target surface, and at the end of the frame create new Pin Constraints for all those that are not already adhered. At the end of each step, the breaking threshold is used to remove the constraints based on ObiParticleAttachment::BreakDynamicAttachment().
To avoid adding particles multiple times, I keep a list of particles that have been added to the ObiPinConstraintsBatch, and remove them from this list when they are removed in my version of BreakDynamicAttachment. However, this step often attempts to remove particles that don't exist in the list, and it appears others remain in the list even though there don't appear to be any active attachments affecting the cloth.
I have tried approaches using RemoveConstraint and DeactivateConstraint. I have successfully used a procedural ObiPinConstraintsBatch to facilitate grasping of the cloth, but in this case I can clear the ObiPinConstraintsBatch each time I need to update it.
I suspect therefore my approach to adding and removing particles from the ObiPinConstraintsBatch is wrong.
What is the best way to handle creating and destroying a small number of Pin Constraints for this purpose?
Would it be better, for example, to create an ObiPinConstraintsBatch with a single constraint for each particle at startup, then update/Activate/Deactivate these as they come into and out of contact?
Or perhaps I am getting the index for a broken constraint the wrong way - is this code correct?
I am trying to use ObiCloth to make an adhesive pad in VR, which the user can place on a surface, and pull away.
The way I am approaching this, is to use the collision callback to detect when particles are within a given distance of the target surface, and at the end of the frame create new Pin Constraints for all those that are not already adhered. At the end of each step, the breaking threshold is used to remove the constraints based on ObiParticleAttachment::BreakDynamicAttachment().
To avoid adding particles multiple times, I keep a list of particles that have been added to the ObiPinConstraintsBatch, and remove them from this list when they are removed in my version of BreakDynamicAttachment. However, this step often attempts to remove particles that don't exist in the list, and it appears others remain in the list even though there don't appear to be any active attachments affecting the cloth.
I have tried approaches using RemoveConstraint and DeactivateConstraint. I have successfully used a procedural ObiPinConstraintsBatch to facilitate grasping of the cloth, but in this case I can clear the ObiPinConstraintsBatch each time I need to update it.
I suspect therefore my approach to adding and removing particles from the ObiPinConstraintsBatch is wrong.
What is the best way to handle creating and destroying a small number of Pin Constraints for this purpose?
Would it be better, for example, to create an ObiPinConstraintsBatch with a single constraint for each particle at startup, then update/Activate/Deactivate these as they come into and out of contact?
Or perhaps I am getting the index for a broken constraint the wrong way - is this code correct?
Code:
// The particlesToStick is populated in the collision callback, with solver indices
private void UpdateAdhesionConstraintsBatch()
{
if (particlesToStick.Count > 0)
{
var bindMatrix = Dummy.transform.worldToLocalMatrix * actor.solver.transform.localToWorldMatrix;
if(adhesionBatch == null)
{
adhesionBatch = new ObiPinConstraintsBatch();
}
foreach (var solverIndex in particlesToStick)
{
var positionOffset = bindMatrix.MultiplyPoint3x4(actor.solver.positions[solverIndex]);
var orientationOffset = bindMatrix.rotation * actor.solver.orientations[solverIndex];
adhesionBatch.AddConstraint(
solverIndex,
Dummy,
positionOffset,
orientationOffset,
0,
0,
0.1f
);
//q: why is this needed?
adhesionBatch.activeConstraintCount++;
stuck.Add(solverIndex);
actor.SetConstraintsDirty(Oni.ConstraintType.Pin);
}
particlesToStick.Clear();
UpdateAdhesion(); [color=#333333][size=small][font=Monaco, Consolas, Courier, monospace]// This adds or removes the batch depending on whether there are any active constraints, and tells the solver to rebuild the pins[/font][/size][/color]
}
}
// And to break the constraints, at the end of each step
private void BreakDynamicAttachment(float stepTime)
{
if (actor.isLoaded)
{
var actorConstraints = actor.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
var solverConstraints = actor.solver.GetConstraintsByType(Oni.ConstraintType.Pin) as ObiConstraints<ObiPinConstraintsBatch>;
if (actorConstraints != null && adhesionBatch != null)
{
int batchIndex = actorConstraints.batches.IndexOf(adhesionBatch);
if (batchIndex >= 0 && batchIndex < actor.solverBatchOffsets[(int)Oni.ConstraintType.Pin].Count)
{
int offset = actor.solverBatchOffsets[(int)Oni.ConstraintType.Pin][batchIndex];
var solverBatch = solverConstraints.batches[batchIndex];
float sqrTime = stepTime * stepTime;
for (int i = 0; i < adhesionBatch.constraintCount; i++)
{
// in case the constraint has been broken:
var b = -solverBatch.lambdas[(offset + i) * 4 + 3] / sqrTime;
if (b > adhesionBatch.breakThresholds[i])
{
var solverIndex = adhesionBatch.particleIndices[i];
if (!stuck.Remove(solverIndex))
{
Debug.Log("Error");
}
adhesionBatch.RemoveConstraint(i);
}
}
}
UpdateAdhesion(); // This adds or removes the batch depending on whether there are any active constraints, and tells the solver to rebuild the pins
}
}
}