Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  How do I set a particle position at runtime ?
#1
Hi !

I am looking for a way to set a particle position at runtime. I've been stuck on this for quite a long time.
All my ropes are instantiated based on the same prefab and use the same blueprint.
I found a way to set a particle position to a specific position by doing : 
Code:
solver.positions[this.rope.solverIndices[solverIndex]] = position;
This works, but only if I put it in the Update method of my script.
However, only this specific particle is stuck in place, all the other particles stay in the exact same position they were, leaving a huge distance between my first particle and the second one...

But if I set the particle positions in the Start method, once the simulation starts, all the particles just go back to the position same they were supposed to have, including the one I set to another position.  Enfadado

Here is a screenshot of the issue I am trying to describe, I hope that helps. 
particle0 is right where I want it but the other particle won't follow : 

[Image: screenshot042.jpg]

Is there a way to set the first particle of my rope to a specific position, but still have all the other particles on the rope follow the simulation ? If so, where should the code go ? I am not sure the Update method is the way to go.
Also, I really would like to avoid having to instantiate 40+ blueprints. Is this something I really have to do ?

Thank you in advance to whoever might be able to help me.  Sonrojado
Reply
#2
Quote:Is there a way to set the first particle of my rope to a specific position, but still have all the other particles on the rope follow the simulation ?
Particles are completely independent from each other. Setting the position of one particle won't automatically move other particles, nor make the rest of the rope "follow" it. If you want the entire rope to move to a different place, you need to iterate over all of its particles, and set their position.

Quote:If so, where should the code go ? I am not sure the Update method is the way to go.
Never use Update for physics (at least, not unless you know exactly what you are doing and why). All physics-related code should go in FixedUpdate.

Quote:Also, I really would like to avoid having to instantiate 40+ blueprints. Is this something I really have to do ?
Do you mean 40 different blueprints? If you need 40 completely different ropes, then yes, you need a blueprint for each one. The advantage of blueprints is that they can be shared by multiple ropes, saving a lot of memory.
Reply
#3
(29-03-2020, 06:13 PM)josemendez Wrote: Particles are completely independent from each other. Setting the position of one particle won't automatically move other particles, nor make the rest of the rope "follow" it. If you want the entire rope to move to a different place, you need to iterate over all of its particles, and set their position.

Never use Update for physics (at least, not unless you know exactly what you are doing and why). All physics-related code should go in FixedUpdate.

Do you mean 40 different blueprints? If you need 40 completely different ropes, then yes, you need a blueprint for each one. The advantage of blueprints is that they can be shared by multiple ropes, saving a lot of memory.

Hey Jose ! 
Thanks a lot for answering so quickly. 


I tried setting the position in FixedUpdate instead, but sadly, the particle just goes straight back to its initial position with each new frame.
I have drawn a gizmo on the position it was supposed to move to (the bottom one), and one at the position it's actually at: 

Code:
Gizmos.DrawSphere(this.rope.solver.positions[solverIndex], 0.1f);

Gizmos.DrawSphere(position, 0.1f);
[Image: screenshot043.jpg]
As you can see they don't quite match.

I feel like there is something I am missing. Am I supposed to push the modified data to the solver or anything similar?
Thank you in advance !  Sonrojado
Reply
#4
(29-03-2020, 07:04 PM)slugGoddess Wrote: Hey Jose ! 
Thanks a lot for answering so quickly. 


I tried setting the position in FixedUpdate instead, but sadly, the particle just goes straight back to its initial position with each new frame.
I have drawn a gizmo on the position it was supposed to move to (the bottom one), and one at the position it's actually at: 

Code:
Gizmos.DrawSphere(this.rope.solver.positions[solverIndex], 0.1f);

Gizmos.DrawSphere(position, 0.1f);
[Image: screenshot043.jpg]
As you can see they don't quite match.

I feel like there is something I am missing. Am I supposed to push the modified data to the solver or anything similar?
Thank you in advance !  Sonrojado

Particle positions (and velocities, radii, ... all particle properties) are expressed in solver space. So if you pass a solver space position to Gizmos.Draw() (which by default expects data to be expressed in world space), the drawn position and the actual particle position won't match at all. You must express the position in world space before drawing it, or set Gizmos.matrix accordingly. See: https://docs.unity3d.com/ScriptReference...atrix.html

Setting particle positions in FixedUpdate() works just fine for me. Can you share your code?
Reply
#5
(29-03-2020, 07:12 PM)josemendez Wrote: Particle positions (and velocities, radii, ... all particle properties) are expressed in solver space. So if you pass a solver space position to Gizmos.Draw() (which by default expects data to be expressed in world space), the drawn position and the actual particle position won't match at all. You must express the position in world space before drawing it, or set Gizmos.matrix accordingly. See: https://docs.unity3d.com/ScriptReference...atrix.html

Setting particle positions in FixedUpdate() works just fine for me. Can you share your code?

Yes of course Sonrisa .
However changing the matrix for the Gizmos displays them at the wrong position. For some unknown reasons, those gizmos are already at the right place.

Here is for the code. I instantiate the ropes in this object : 
Code:
 void Awake() {

   this.mesh = GetComponent<MeshFilter>().mesh;
   this.vertices = mesh.vertices;
   this.normals = mesh.normals;


   for (int i = 0; i < this.vertices.Length; i++) {
     this.ropeObj = Instantiate(this.prefab);
     this.rope = ropeObj.GetComponent<ObiRope>();
     this.rope.transform.parent = this.prefab.transform.parent;

     // that's where I add in my other script
     this.ropeObj.AddComponent<Strand>();
     this.currentVertice++;
     if (i == 0)
       break; // For now, let's keep this to one rope.
   }

Now this is the script component attached to the generated ropes : 
Code:
void Awake() {

   this.scalp = GameObject.FindWithTag("scalp").GetComponent<Scalp>();
   this.start = this.scalp.getNextVertice();
   this.normal = this.scalp.getNextNormal();

   this.makeStrand();
}

private /*IEnumerator*/ void makeStrand () {

   this.rope = gameObject.GetComponent<ObiRope>();
   ObiRopeCursor cursor = this.rope.GetComponent<ObiRopeCursor>();
   this.solver = this.rope.GetComponent<ObiSolver>();


/*  //## No longer making a new blueprint ##

[size=small][font=Monaco, Consolas, Courier, monospace]    this.localHit = this.rope.transform.InverseTransformPoint(this.start);[/font][/size]
   Vector3 end = this.rope.transform.InverseTransformPoint(this.normal);
   ObiRopeBlueprint blueprint = ScriptableObject.CreateInstance<ObiRopeBlueprint>();
   blueprint.resolution = 0.5f;
   blueprint.path.Clear();
   blueprint.path.AddControlPoint(
       localHit, -localHit.normalized, localHit.normalized, -end.normalized,
       0.1f, 0.1f, 1, 1, Color.white, "start");
   blueprint.path.AddControlPoint(
       localHit + (end.normalized * 2) , end.normalized, end.normalized, Vector3.up,
       0.1f, 0.1f, 1, 1, Color.white, "end");
   blueprint.path.FlushEvents();
   this.rope.ropeBlueprint = blueprint;
*/

    this.rope.solver.positions[this.rope.solverIndices[solverIndex]] = this.start;

   // I already have the particle attachment on the prefab
   //this.rope.GetComponent<ObiParticleAttachment>().particleGroup = this.rope.ropeBlueprint.groups[0];

   Strand.index++;

   /*yield return blueprint.Generate()*/
}

 void FixedUpdate() {

   this.last = this.rope.solverIndices.Length - 1;
   this.first = this.rope.solverIndices[0];
   this.rope.solver.positions[this.rope.solverIndices[solverIndex]] = this.start;
 }

I feel like there is something really really obvious I am missing. Is there anything that looks wrong to you, Jose ?
Thank you so much for your help so far and for those amazing assets.
Reply
#6
Hi there,

Will take a look at your scripts later today and give feedback Sonrisa
Reply
#7
(31-03-2020, 10:39 AM)josemendez Wrote: Hi there,

Will take a look at your scripts later today and give feedback Sonrisa

Thanks a lot Jose, you're the best !   Sonrisa  Loving ObiRope so much. Can't wait to try ObiCloth later.
Let me know if it makes it easier/quicker for you if I include the whole class definition instead of just snippets.
Reply
#8
(31-03-2020, 07:07 PM)slugGoddess Wrote: Thanks a lot Jose, you're the best !   Sonrisa  Loving ObiRope so much. Can't wait to try ObiCloth later.
Let me know if it makes it easier/quicker for you if I include the whole class definition instead of just snippets.

Took a look at the snippets, but it's confusing because there's bits missing that I'm not entirely sure how I'm supposed to fill in.

I think the overall intent of the code is to instantiate a rope at each vertex of a mesh, kinda like hair strands. Is this correct? In that case there's no need to fiddle with particles, simply setting the position/orientation of the rope should be enough.

1.- Create a rope prefab that has one end centered in the transform's origin, and is aligned with the Z axis, like this (the translation gizmo is the actual object's gizmo, not a control point). This is so that when we translate/rotate the prefab, we know that we are placing the root of the hair strand, and orienting it perpendicular to the mesh surface.
[Image: 8GXYXu9.png]
2.- Then, instantiate the prefab once per vertex using this code:

Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Obi;

public class Scalp : MonoBehaviour
{
   public Mesh scalp;
   public GameObject hair;

   private GameObject[] strandInstances;

   void Awake()
   {
       Vector3[] vertices = scalp.vertices;
       Vector3[] normals = scalp.normals;
       strandInstances = new GameObject[vertices.Length];

       for (int i = 0; i < vertices.Length; ++i)
       {
           // instantiate a strand at the vertex position/orientation:
           strandInstances[i] = Instantiate(hair, vertices[i], Quaternion.LookRotation(normals[i]));

           // parent it to the scalp (we assume the scalp has the solver component).
           strandInstances[i].transform.SetParent(transform, false);

           // get the first particle group (first control point):
           var group = strandInstances[i].GetComponent<ObiRope>().blueprint.groups[0];

           // attach it to the scalp:
           var attachment = strandInstances[i].AddComponent<ObiParticleAttachment>();
           attachment.particleGroup = group;
           attachment.target = this.transform;
       }
   }

   private void OnDestroy()
   {
       for (int i = 0; i < strandInstances.Length; ++i)
       {
           Destroy(strandInstances[i]);
       }
   }
}

Results when applied to a icosphere:
[Image: LMGcj5x.png]
[Image: ScFO5Ru.png]

It's quite slow though. Keep in mind that simulating and generating a mesh for each individual strand is way too costly. Even when done in the GPU instead of the CPU, guide hairs/interpolation is used. No hair system I've come across (realtime or not) simulates each individual hair.
Reply
#9
(31-03-2020, 09:04 PM)josemendez Wrote: Took a look at the snippets, but it's confusing because there's bits missing that I'm not entirely sure how I'm supposed to fill in.

I think the overall intent of the code is to instantiate a rope at each vertex of a mesh, kinda like hair strands. Is this correct? In that case there's no need to fiddle with particles, simply setting the position/orientation of the rope should be enough.

1.- Create a rope prefab that has one end centered in the transform's origin, and is aligned with the Z axis, like this (the translation gizmo is the actual object's gizmo, not a control point). This is so that when we translate/rotate the prefab, we know that we are placing the root of the hair strand, and orienting it perpendicular to the mesh surface.

2.- Then, instantiate the prefab once per vertex using this code:

It's quite slow though. Keep in mind that simulating and generating a mesh for each individual strand is way too costly. Even when done in the GPU instead of the CPU, guide hairs/interpolation is used.  No hair system I've come across  (realtime or not) simulates each individual hair.

Thank you so much !!

I tried fiddling with the particles themselves because moving the entire rope object didn't work at all, for some reason...
I think it has to do with what you just explained, actually.

About the CPU/GPU, yes I know, but I do need them generated in realtime. I already have an idea on how to reduce the load on the processor and it's working pretty nicely. (However I still need to try on a computer that doesn't have 32gb of ram...) I have tried with ropes that I added manually, and removed most of the contraints I didn't need. Also, the scalp mesh isn't visible and only has vertices in strategic positions. I'm trying to generate as little ropes as possible and not all of them have the same size.
I need people to be able to manually move the strands, brush them down, split them, cut them, make them longer, turn them into braids, tie them up, etc... The end result is then going to be turned into a simple mesh with culling on all non visible faces from all angles, but I'm serializing the particle positions for later editing by the user.  Sonrisa I really hope I won't have too much troubles resetting those particles...  Indeciso

Thank you sooooo much for helping, I'm going to try this out and let you know! I'll add the [solved] prefix to my thread later if it works... Well, if that kind of forum allows me to edit the thread name  Lengua

Thank you so much again, Jose !!  Sonrisa Sonrisa
Reply
#10
Hi Jose, 

So your advice worked, especially the one with the solver being a parent of the scalp and moving the translation gizmo to the beginning of my rope. This helped a lot.
Everything is working and I can set the ropes in place programmatically  Sonrisa

However, at some point, I'm going to need to restore the particle positions I am serializing and storing for later use.
Updating them in FixedUpdate doesn't work for me. They just go back to their normal position when Update runs.
I tried setting them in world position with transform.TransformPoint (correct me if that's not the right way to do this), I tried with the solver positions, using Vector3, Vector4...

What I am doing is : 

Code:
void FixedUpdate() {

 this.rope.solver.positions[this.rope.solverIndices[solverIndex]] = new Vector3(0f, 0f, 0f);

 Debug.Log(this.rope.solver.positions[this.rope.solverIndices[solverIndex]]);

}

void Update() {
   Debug.Log(this.rope.solver.positions[this.rope.solverIndices[solverIndex]]);
}

But the Debug.Log in FixedUpdate() prints : 0, 0, 0, 0
and the one in Update() prints :  0.1, 7.1, -0.3, 0.0

Basically, it just always goes back to its normal position after I set it in FixedUpdate().
Am I doing something wrong ? 
Is there a way to force those particle into place, and then, allow the simulation to run as normal ?

I've read quite a bit about direct interaction with Oni, but all the examples I found no longer seem to work with my current version of ObiRope, for exemple : 


Code:
pos[0] = l2sTransform.MultiplyPoint3x4(this.start);
Oni.SetParticlePositions(this.solver.OniSolver, pos,1, this.rope.solverIndices[solverIndex]);
   

So sorry this issue is taking so much of your time  Triste
I am very grateful for your help.
Reply