Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

uFlex soft body animation

Discussion in 'Works In Progress' started by CodeKiwi, Nov 1, 2016.

  1. CodeKiwi

    CodeKiwi

    Joined:
    Oct 27, 2016
    Posts:
    73


    I’ve recently been trying a method to animate FleX soft bodies for uFlex. I just thought I’d post my scripts and a rough description on how to use it. I'm just posting it here to avoid cluttering the uFlex thread. For this example I’ll be using the Allosaurus model.

    In blender: Import the dino and edit the mesh to only include the main body shape e.g edit mode, L key select, invert and delete. Apply armature, apply location / rotation / scale. Export fbx at x10 scale. Import into unity. Untick optimize mesh.

    Create a uFlex rigid body with the default settings. Tick "Draw Debug" in Flex Particles, the model should be standing upright and have an even distribution of particles. Try to creating a soft body, with the default settings the model will collapse. Try again with cluster spacing increased to 5. Run the simulation and it should seem like rubber.

    Right click / create / vertMapAsset. Add a BlenderExporter script to the soft body and assign vertMapAsset. Run the game, stop and save. Turn off or remove the exporter script. This should have updated the vertMapAsset and copied a blender script to the clipboard. Open blender and run the script. You should see a spiky mesh (particle positions).

    In blender import this mesh into the same file as the skinned dino model. Align it (rotate -90 deg on the x axis and scale down by 10% / s key 0.1). Apply rotation and scale. It should align with the mesh. Select the spiky mesh add add a "Armature" modifier (select armature object) and then add a "Data transfer modifier". In the data transfer modifier: set the source object to the original mesh. Tick "Vertex Data" and enable "Vertex Group". Click the "Generate Data Layers" button and apply. The skinned weights should now be transferred. You can test it by changing the skeleton to pose mode and change the spiked mesh to weight paint mode. Then select each bone to see the weights.

    Export this new mesh at x10 scale again. Import it into Unity (IMPORTANT: disable optimize). Add it to the stage and set the scale to 0.1. Make sure the animator "Culling mode" is set to "Always Animate". Add a "Apply Particle Animation" component to the soft body. Assign the "Vert Map Asset", set the "skinned Mesh Renderer" to the spiky mesh (probably called Solid), untick "Apply Animation" and tick "Draw Skel" and "Draw Rest". Set "Draw Index" to -1. Add a large point locker prefab that covers the entire mesh. Run the game and enter "Scene" mode to view the debug data. You should see red dots on the skinned mesh and blue squares on the spiky mesh. Disable the renderers for the soft body and spiky mesh so you can only see the red and blue dots. Increase the "Draw Index" value, the blue and red dot positions should match. If it doesn't then the spiky mesh might have the optimize setting turned on.

    If this works then turn on the soft bodies "particle renderer" and disable the point locker. Enable "Apply Animation" and disable "Draw Skel" and "Draw Rest". It should fall like normal (no animation / directly copying the ref pose).

    Export the animations from blender (x10 scale) and apply them to an animation controller on the character. The skinned mesh should follow the animation.

    In this example the mesh doesn't follow the particles directly. This is because the soft body values need to the adjusted e.g cluster spacing, verts on the left leg are affected by the right leg.

    As a side note: it looks like the soft body is created from clusters of shapes and each shape can have it's own settings e.g maybe the limbs are more rigid and the tail is flexible. It should be possible to edit these values (maybe view in blender?).
     

    Attached Files:

    theANMATOR2b likes this.
  2. dudester

    dudester

    Joined:
    Oct 25, 2014
    Posts:
    323
    this is awesome well done , keep at it , why unity couldn't do this is a mystery .
     
  3. CodeKiwi

    CodeKiwi

    Joined:
    Oct 27, 2016
    Posts:
    73
    I’ve found that the process of going between Unity and blender was taking too long so I’ve removed it. For the new script just add the animated mesh and set “Culling Mode” to “Always Animate”. Also add an animation controller with an animation. Create the soft body and add a FlexAnimation script. Then assign the “Skinned Mesh Renderer” to the new animated mesh. The first time you run it it will generate a scriptable object. Stop the game and assign this created asset to the “Vert Map Asset” property. I’ll probably upload a short video tutorial and maybe some examples at some point.

    One interesting thing I found is that the soft body worked with extra parts e.g in the original video I removed every thing except for the main mesh e.g eyes, teeth. I was surprised to see it working with multiple meshes.

    VertMapAsset.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. [System.Serializable]
    5. public class VertexWeight
    6. {
    7.     public int index;
    8.     public Vector3 localPosition;
    9.     public float weight;
    10.  
    11.     public VertexWeight()
    12.     {
    13.  
    14.     }
    15.  
    16.     public VertexWeight(int i, Vector3 p, float w)
    17.     {
    18.         index = i;
    19.         localPosition = p;
    20.         weight = w;
    21.     }
    22. }
    23.  
    24. [System.Serializable]
    25. public class WeightList
    26. {
    27.     private Transform _temp; // cached on use, not serialized
    28.     public Transform transform
    29.     {
    30.         get
    31.         {
    32.             if (_temp == null)
    33.             {
    34.                 _temp = new GameObject().transform;
    35.                 _temp.position = pos;
    36.                 _temp.rotation = new Quaternion(rot.x, rot.y, rot.z, rot.w);
    37.                 _temp.localScale = scale;
    38.             }
    39.             return _temp;
    40.         }
    41.         set
    42.         {
    43.             pos = value.position;
    44.             rot = new Vector4(value.rotation.x, value.rotation.y, value.rotation.z, value.rotation.w);
    45.             scale = value.localScale;
    46.         }
    47.     }
    48.     public int boneIndex; // for transform
    49.     public Vector3 pos;
    50.     public Vector4 rot;
    51.     public Vector3 scale;
    52.  
    53.     public List<VertexWeight> weights = new List<VertexWeight>();
    54. }
    55.  
    56. public class VertMapAsset : ScriptableObject {
    57.     // index = soft body particle index. Value = vertex index.
    58.     public List<int> vertexParticleMap;
    59.     public List<Vector3> particleRestPositions;
    60.     public List<int> nearestVertIndex;
    61.     public List<int> uniqueIndex;
    62.     public WeightList[] particleNodeWeights; // one per node (vert). Weights of standard mesh
    63. }
    64.  
    FlexAnimation.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using uFlex;
    4.  
    5. #if UNITY_EDITOR
    6. using UnityEditor; // required to build assets
    7. #endif
    8.  
    9. public class VertMapAssetBuilder
    10. {
    11.     public FlexShapeMatching flexShapeMatching;
    12.     public VertMapAsset vertMapAsset;
    13.     public SkinnedMeshRenderer skin;
    14.  
    15.     public VertMapAssetBuilder(FlexShapeMatching flexShapeMatching, VertMapAsset vertMapAsset, SkinnedMeshRenderer skin)
    16.     {
    17.         this.flexShapeMatching = flexShapeMatching;
    18.         this.vertMapAsset = vertMapAsset;
    19.         this.skin = skin;
    20.     }
    21.  
    22.     private List<Vector3> GetFlexShapeMatchingPositions()
    23.     {
    24.         List<Vector3> vertPos = new List<Vector3>();
    25.  
    26.         FlexShapeMatching shapes = this.flexShapeMatching;
    27.         int shapeIndex = 0;
    28.         int shapeIndexOffset = shapes.m_shapesIndex;
    29.         int shapeStart = 0;
    30.  
    31.         for (int s = 0; s < shapes.m_shapesCount; s++)
    32.         {
    33.             shapeIndex++;
    34.             int shapeEnd = shapes.m_shapeOffsets[s];
    35.             for (int i = shapeStart; i < shapeEnd; ++i)
    36.             {
    37.                 Vector3 pos = flexShapeMatching.m_shapeRestPositions[i] + shapes.m_shapeCenters[s];
    38.                 vertPos.Add(pos);
    39.                 shapeIndexOffset++;
    40.             }
    41.  
    42.             shapeStart = shapeEnd;
    43.         }
    44.  
    45.         return vertPos;
    46.     }
    47.  
    48.     Vector3 RoundVec(Vector3 value)
    49.     {
    50.         float dp = 100000.0f;
    51.         value.x = Mathf.RoundToInt(value.x * dp) / dp;
    52.         value.y = Mathf.RoundToInt(value.y * dp) / dp;
    53.         value.z = Mathf.RoundToInt(value.z * dp) / dp;
    54.         return value;
    55.     }
    56.  
    57.     private int GetNearestVertIndex(Vector3 particlePos, ref Vector3[] cachedVertices)
    58.     {
    59.         float nearestDist = float.MaxValue;
    60.         int nearestIndex = -1;
    61.         for (int i = 0; i < cachedVertices.Length; i++)
    62.         {
    63.             float dist = Vector3.Distance(particlePos, cachedVertices[i]);
    64.             if (dist < nearestDist)
    65.             {
    66.                 nearestDist = dist;
    67.                 nearestIndex = i;
    68.             }
    69.         }
    70.         return nearestIndex;
    71.     }
    72.  
    73.     private void SetBoneWeights(ref List<Vector3> uniqueParticlePositions, ref List<int> uniqueParticleIndices)
    74.     {
    75.         SkinnedMeshRenderer skinnedMeshRenderer = skin;
    76.  
    77.         // Cache used values rather than accessing straight from the mesh on the loop below
    78.         Vector3[] cachedVertices = skinnedMeshRenderer.sharedMesh.vertices;
    79.         Matrix4x4[] cachedBindposes = skinnedMeshRenderer.sharedMesh.bindposes;
    80.         BoneWeight[] cachedBoneWeights = skinnedMeshRenderer.sharedMesh.boneWeights;
    81.  
    82.         // Make a CWeightList for each bone in the skinned mesh
    83.         WeightList[] nodeWeights = new WeightList[skinnedMeshRenderer.bones.Length];
    84.         for (int i = 0; i < skinnedMeshRenderer.bones.Length; i++)
    85.         {
    86.             nodeWeights[i] = new WeightList();
    87.             nodeWeights[i].boneIndex = i;
    88.             nodeWeights[i].transform = skinnedMeshRenderer.bones[i];
    89.         }
    90.  
    91.         for (int uniqueIndex = 0; uniqueIndex < uniqueParticleIndices.Count; uniqueIndex++)
    92.         {
    93.             Vector3 particlePos = uniqueParticlePositions[uniqueIndex];
    94.             int i = GetNearestVertIndex(particlePos, ref cachedVertices);
    95.  
    96.             vertMapAsset.nearestVertIndex.Add(i);
    97.             vertMapAsset.uniqueIndex.Add(uniqueIndex);
    98.             BoneWeight bw = cachedBoneWeights[i];
    99.  
    100.             if (bw.weight0 != 0.0f)
    101.             {
    102.                 Vector3 localPt = cachedBindposes[bw.boneIndex0].MultiplyPoint3x4(particlePos);// cachedVertices[i]);
    103.                 nodeWeights[bw.boneIndex0].weights.Add(new VertexWeight(uniqueIndex, localPt, bw.weight0));
    104.             }
    105.             if (bw.weight1 != 0.0f)
    106.             {
    107.                 Vector3 localPt = cachedBindposes[bw.boneIndex1].MultiplyPoint3x4(particlePos);//cachedVertices[i]);
    108.                 nodeWeights[bw.boneIndex1].weights.Add(new VertexWeight(uniqueIndex, localPt, bw.weight1));
    109.             }
    110.             if (bw.weight2 != 0.0f)
    111.             {
    112.                 Vector3 localPt = cachedBindposes[bw.boneIndex2].MultiplyPoint3x4(particlePos);//cachedVertices[i]);
    113.                 nodeWeights[bw.boneIndex2].weights.Add(new VertexWeight(uniqueIndex, localPt, bw.weight2));
    114.             }
    115.             if (bw.weight3 != 0.0f)
    116.             {
    117.                 Vector3 localPt = cachedBindposes[bw.boneIndex3].MultiplyPoint3x4(particlePos);//cachedVertices[i]);
    118.                 nodeWeights[bw.boneIndex3].weights.Add(new VertexWeight(uniqueIndex, localPt, bw.weight3));
    119.             }
    120.         }
    121.  
    122.         vertMapAsset.particleNodeWeights = nodeWeights;
    123.  
    124.     }
    125.  
    126.     public void CreateAsset()
    127.     {
    128.         vertMapAsset.vertexParticleMap = new List<int>();
    129.  
    130.         List<Vector3> particlePositions = GetFlexShapeMatchingPositions();
    131.         List<Vector3> uniqueParticlePositions = new List<Vector3>();
    132.         List<int> uniqueParticleIndices = new List<int>();
    133.         for (int i = 0; i < particlePositions.Count; i++)
    134.         {
    135.             Vector3 vert = particlePositions[i];
    136.             vert = RoundVec(vert);
    137.  
    138.             if (uniqueParticlePositions.Contains(vert) == false)
    139.             {
    140.                 uniqueParticleIndices.Add(i);
    141.                 uniqueParticlePositions.Add(vert);
    142.             }
    143.  
    144.  
    145.             vertMapAsset.vertexParticleMap.Add(uniqueParticlePositions.IndexOf(vert));
    146.         }
    147.  
    148.         vertMapAsset.particleRestPositions = uniqueParticlePositions;
    149.         vertMapAsset.nearestVertIndex = new List<int>();
    150.         vertMapAsset.uniqueIndex = new List<int>();
    151.         SetBoneWeights(ref uniqueParticlePositions, ref uniqueParticleIndices);
    152.  
    153.         // trigger save
    154. #if UNITY_EDITOR
    155.         UnityEditor.EditorUtility.SetDirty(vertMapAsset);
    156. #endif
    157.     }
    158. }
    159.  
    160. public class FlexAnimation : FlexProcessor
    161. {
    162.     public bool rebuildVertMapAsset = false;
    163.     public VertMapAsset vertMapAsset;
    164.     public SkinnedMeshRenderer skinnedMeshRenderer;
    165.     private FlexShapeMatching flexShapeMatching;
    166.     private bool firstRun = true;
    167.     private Vector3[] particlePositions; // world particle positions
    168.     public bool drawVertMapAsset = false;
    169.  
    170.     Vector3[] _cachedVertices;
    171.     Matrix4x4[] _cachedBindposes;
    172.     BoneWeight[] _cachedBoneWeights;
    173.     float refreshRate = 1.0f / 30.0f;
    174.     float timeDelta;
    175.  
    176.     void OnEnable()
    177.     {
    178.         if (flexShapeMatching == null)
    179.             flexShapeMatching = GetComponent<FlexShapeMatching>();
    180.     }
    181.  
    182.     public override void PreContainerUpdate(FlexSolver solver, FlexContainer cntr, FlexParameters parameters)
    183.     {
    184.         if (enabled == false)
    185.             return;
    186.  
    187.         if (firstRun)
    188.         {
    189.             firstRun = false;
    190.          
    191.             _cachedVertices = skinnedMeshRenderer.sharedMesh.vertices;
    192.             _cachedBindposes = skinnedMeshRenderer.sharedMesh.bindposes;
    193.             _cachedBoneWeights = skinnedMeshRenderer.sharedMesh.boneWeights;
    194.  
    195.             bool createAsset = vertMapAsset == null || rebuildVertMapAsset;
    196.  
    197.             if (vertMapAsset == null)
    198.             {
    199. #if UNITY_EDITOR
    200.                 vertMapAsset = ScriptableObject.CreateInstance<VertMapAsset>();
    201.                 AssetDatabase.CreateAsset(vertMapAsset, "Assets/" + this.name + "VertMapAsset.asset");
    202.                 AssetDatabase.SaveAssets();
    203.                 EditorUtility.FocusProjectWindow();
    204.                 Selection.activeObject = vertMapAsset;
    205. #endif
    206.             }
    207.  
    208.             if (createAsset)
    209.             {
    210.                 vertMapAsset.vertexParticleMap = new List<int>();
    211.                 VertMapAssetBuilder vertMapAssetBuilder = new VertMapAssetBuilder(flexShapeMatching, vertMapAsset, skinnedMeshRenderer);
    212.                 vertMapAssetBuilder.CreateAsset();
    213.             }
    214.  
    215.             particlePositions = new Vector3[vertMapAsset.particleRestPositions.Count];
    216.             UpdateParticlePositions();
    217.  
    218.         }
    219.         else
    220.         {
    221.             if (timeDelta < refreshRate)
    222.                 return;
    223.  
    224.             // Only process once 30 times a second
    225.             UpdateParticlePositions();
    226.             MatchShapes(); // apply to soft body
    227.  
    228.             while (timeDelta >= refreshRate)
    229.                 timeDelta -= refreshRate;
    230.         }
    231.     }
    232.  
    233.     public void UpdateParticlePositions()
    234.     {
    235.         for (int i = 0; i < particlePositions.Length; i++)
    236.         {
    237.             particlePositions[i] = Vector3.zero;
    238.         }
    239.  
    240.         // Now get the local positions of all weighted indices...
    241.         foreach (WeightList wList in vertMapAsset.particleNodeWeights)
    242.         {
    243.             foreach (VertexWeight vw in wList.weights)
    244.             {
    245.                 Transform t = skinnedMeshRenderer.bones[wList.boneIndex];
    246.                 particlePositions[vw.index] += t.localToWorldMatrix.MultiplyPoint3x4(vw.localPosition) * vw.weight;
    247.             }
    248.         }
    249.  
    250.         // Now convert each point into local coordinates of this object.
    251.         for (int i = 0; i < particlePositions.Length; i++)
    252.         {
    253.             particlePositions[i] = transform.InverseTransformPoint(particlePositions[i]);
    254.         }
    255.     }
    256.  
    257.     private void MatchShapes()
    258.     {
    259.         FlexShapeMatching shapes = this.flexShapeMatching;
    260.  
    261.         int shapeIndex = 0;
    262.         int shapeIndexOffset = shapes.m_shapesIndex;
    263.         int shapeStart = 0;
    264.  
    265.         int vertIndex = 0;
    266.         for (int s = 0; s < shapes.m_shapesCount; s++)
    267.         {
    268.             Vector3 shapeCenter = new Vector3();
    269.             shapeIndex++;
    270.  
    271.             int shapeEnd = shapes.m_shapeOffsets[s];
    272.  
    273.             int shapeCount = shapeEnd - shapeStart;
    274.             int origShapeIndexOffset = shapeIndexOffset;
    275.             for (int i = shapeStart; i < shapeEnd; ++i)
    276.             {
    277.                 int mappedIndex = vertMapAsset.vertexParticleMap[vertIndex];
    278.                 Vector3 pos = particlePositions[mappedIndex];
    279.                 shapes.m_shapeRestPositions[shapeIndexOffset] = pos;
    280.                 shapeCenter += pos;
    281.                 shapeIndexOffset++;
    282.                 vertIndex++;
    283.             }
    284.  
    285.             shapeCenter /= shapeCount;
    286.  
    287.             for (int i = shapeStart; i < shapeEnd; ++i)
    288.             {
    289.                 Vector3 pos = shapes.m_shapeRestPositions[origShapeIndexOffset];
    290.                 pos -= shapeCenter;
    291.                 shapes.m_shapeRestPositions[origShapeIndexOffset] = pos;
    292.                 origShapeIndexOffset++;
    293.             }
    294.  
    295.             shapeStart = shapeEnd;
    296.         }
    297.     }
    298.  
    299.     public void Update()
    300.     {
    301.         timeDelta += Time.deltaTime;
    302.      
    303.     }
    304.  
    305.     public virtual void OnDrawGizmos()
    306.     {
    307.         if (drawVertMapAsset && vertMapAsset != null)
    308.         {
    309.             float boxSize = 0.2f;
    310.             /*
    311.             Gizmos.color = Color.red;
    312.             foreach (Vector3 vert in vertMapAssetBuilder._cachedVertices)
    313.             {
    314.                 Gizmos.DrawCube(vert, new Vector3(boxSize, boxSize, boxSize));
    315.             }
    316.  
    317.             Gizmos.color = Color.blue;
    318.             foreach (Vector3 vert in vertMapAssetBuilder._uniqueParticlePositions)
    319.             {
    320.                 Gizmos.DrawCube(vert, new Vector3(boxSize, boxSize, boxSize));
    321.             }
    322.             */
    323.  
    324.             if (particlePositions != null)
    325.             {
    326.                 Gizmos.color = Color.red;
    327.                 foreach (Vector3 vert in particlePositions)
    328.                 {
    329.                     Gizmos.DrawCube(vert, new Vector3(boxSize, boxSize, boxSize));
    330.                 }
    331.             }
    332.  
    333.         }
    334.     }
    335.  
    336. }
    337.  
     
  4. liang-xiao

    liang-xiao

    Joined:
    Nov 18, 2016
    Posts:
    1
    I use the latest two scripts, but still doesn't work, I don't know whether my operation has a problem, I am such operation, FlexAnimation binding on software model Then run directly Unity, then the end, the newly generated VertMapAsset drag and drop to FlexAnimation script, and then on to this software binding Animator, and binding AnimatorController and skeleton, let after Culling Mode is set to Always Animate, run Unity does not make the software after the flash, the software is still there. Don't know where is wrong, AnimatorController checked no problem, I don't know whether I generated out of the question of the software, for software, is there any fixed requirements? I tried for 3 days Still can not let this software animation move...
     

    Attached Files:

    • 16.png
      16.png
      File size:
      370.5 KB
      Views:
      768
  5. CodeKiwi

    CodeKiwi

    Joined:
    Oct 27, 2016
    Posts:
    73
    Sorry for the late reply, I forgot to check this post for a while. I created a new project from scratch and noted down all steps. I also included the modified version of the Allosaurus model I used. I did find one bug with this code. It looks like the soft body is created as a set of bones (one per cluster). I updated the particle positions / bone locations but not the bone rotations. It should probably record the average rotation offset when modifying the particles. Or you could select three particles per cluster and create a rotation from them. UE4 has some new features I want to try, so I won't be making any changes in Unity for a while.

    I can’t see anything wrong in your setup. It might be that Dynamic isn’t ticked for the solver. Try ticking “Draw Vert Map Asset” in the “Flex Animation” and check scene mode when the game is running. You should see a bunch of red dots that animate with the character. Also try enabling “Draw Debug” in the FlexParticles script (scene view). It might be related to the Flex units. It’s probably easier to just update your models to be in cm units (UE4) instead of meters (Unity). Also try the soft body on its own, it should bounce on the ground. Maybe an issue with cluster spacing?
    • Create a new project
    • Import standard effects package
    • Import uFlex
    • Test a uFlex demo scene
    • Create a new scene
    • Add uFlex/Prefabs/Flex to stage
    • Important: make sure that Dynamic is ticked in the solver
    • Add a plane for the ground. x and z scale = 10
    • Test with existing soft body e.g SoftBunny (might need to update drivers)
    • Run it and note that it falls slowly. This is because the bunny is 10m tall in unity. Flex particles are normally one unit large (UE4 1 unit = cm, unity 1 unit = m). Might need to update gravity.
    • Import the Allosaurus assets. This caused an upgrade issue. Had to remove SSF_ComposeFluid (or import Allosaurus differently)
    • Add the scripts VertMapAsset.cs and FlexAnimation.cs.
    • The original Allosaurus model is too small so I modified it in blender. I've included the modified fbx (should be ok as the Allosaurus assets are free and you still need to download it for the textures)
    • Add Dino_runMin to the stage. Set scale in Dino_runMin to 10%. Set culling mode to "Always animate".
    • Create soft body of Dino_runMin, cluster spacing = 5
    • Add an animation controller / loop run animation "Armature|Armature|Take 001|BaseLayer".
    • Add a FlexAnimation script to the soft body and assign the “Skinned Mesh Renderer” to the new animated mesh.
    • Run the game and it should follow the animation but the skinned mesh doesn't look correct (bone rotation bug). Enable "Debug Draw" on the "Flex Particles" component. Check the particle in scene view when it's running. The particle positions should be correct.
     

    Attached Files:

  6. AKQJ10

    AKQJ10

    Joined:
    Feb 9, 2012
    Posts:
    33
    Hi, High_Noon,

    Thanks a lot for providing the step by step guide, which worked for me. When Allosaurus model's skinnedMeshRender plays the walk anim, the FlexSoftbody also moves its body parts accordingly.

    Only issue is the rotation bug you mentioned, the softbody allosaurus just rotates by itself and rolls on the floor. Shall I turn on the OverrideMass toggle and set Mass value to 0 for the softbody? As for code, I need to change FlexAnimation::UpdateParticlePositions()
    Code (CSharp):
    1. private void UpdateParticlePositions()
    2.     {
    3.         for (int i = 0; i < particlePositions.Length; i++)
    4.         {
    5.             particlePositions[i] = Vector3.zero;
    6.         }
    7.  
    8.         // Now get the local positions of all weighted indices...
    9.         foreach (WeightList wList in vertMapAsset.particleNodeWeights)
    10.         {
    11.             foreach (VertexWeight vw in wList.weights)
    12.             {
    13.                 Transform t = skinnedMeshRenderer.bones[wList.boneIndex];
    14.                 particlePositions[vw.index] += t.localToWorldMatrix.MultiplyPoint3x4(vw.localPosition) * vw.weight;
    15.                 //compute and save the roation for this particle item
    16.                 //particleRotations[vw.index] += compute the weighted rotation here;
    17.             }
    18.         }
    19.  
    20.         // Now convert each point into local coordinates of this object.
    21.         for (int i = 0; i < particlePositions.Length; i++)
    22.         {
    23.             particlePositions[i] = transform.InverseTransformPoint(particlePositions[i]);
    24.             //do world to local convertion here?
    25.             //particleRotations[i] = ;
    26.         }
    27.     }
    Then in the FlexAnimation::MatchShapes(), after the line of shapes.m_shapeRestPositions[shapeIndexOffset] = pos; also set the new rotation values. Is my understanding correct? Thanks in advance.
     
    Last edited: Dec 15, 2016
  7. korzen303

    korzen303

    Joined:
    Oct 2, 2012
    Posts:
    177
    @High_Noon Thanks a lot for sharing this. I will have a deeper look after I finish performance improvements for uFlex v6.0.

    Cheers!
     
  8. CodeKiwi

    CodeKiwi

    Joined:
    Oct 27, 2016
    Posts:
    73
    I had some free time so I tried a fix for the bone rotation. The codes a bit messy so I’ll probably upload a cleaner version at some point. I tried updating the bone rotations in the skinning code as AKQJ10 suggested and also tried updating the bone rotations in a custom FlexSkinnedMesh. I couldn’t seem to get it to work so I’m just rotating the particles directly. I get the rotation by selecting three particles and creating a Quaternion using Quaternion.LookRotation(). The three particles need to be close to 90 degrees to avoid twisting.



    Controlling this type of character is difficult. I’m more interested in robotics so I’d probably use it for simulating things like cars, spiders, snakes or anything that is easy to control. Controlling a biped would probably require something like deep reinforcement learning.

    One method to control a character might be to create a puppet type system e.g link the softbody to a character skeleton using springs. The character skeleton could have 0 mass particles that don’t collide with anything. This way if you moved the skeleton leg though the floor the soft body won’t pass though the floor. I haven’t tried it, but it might work.

    I tried controlling the character with 0 mass in UE4. This had an interesting effect where it helped with skinning difficult areas like the shoulders (rubber joints). The main problem with 0 mass is that it doesn’t interact well with the environment e.g can move through walls, pass through cloth etc.



    korzen303: thanks for looking at integrating this idea as a part of uFlex. My code was mainly for testing so it probably needs a few optimizations.

    VertMapAsset.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. [System.Serializable]
    5. public class VertexWeight
    6. {
    7.     public int index;
    8.     public Vector3 localPosition;
    9.     public float weight;
    10.  
    11.     public VertexWeight()
    12.     {
    13.  
    14.     }
    15.  
    16.     public VertexWeight(int i, Vector3 p, float w)
    17.     {
    18.         index = i;
    19.         localPosition = p;
    20.         weight = w;
    21.     }
    22. }
    23.  
    24. [System.Serializable]
    25. public class WeightList
    26. {
    27.     private Transform _temp; // cached on use, not serialized
    28.     public Transform transform
    29.     {
    30.         get
    31.         {
    32.             if (_temp == null)
    33.             {
    34.                 _temp = new GameObject().transform;
    35.                 _temp.position = pos;
    36.                 _temp.rotation = new Quaternion(rot.x, rot.y, rot.z, rot.w);
    37.                 _temp.localScale = scale;
    38.             }
    39.             return _temp;
    40.         }
    41.         set
    42.         {
    43.             pos = value.position;
    44.             rot = new Vector4(value.rotation.x, value.rotation.y, value.rotation.z, value.rotation.w);
    45.             scale = value.localScale;
    46.         }
    47.     }
    48.     public int boneIndex; // for transform
    49.     public Vector3 pos;
    50.     public Vector4 rot;
    51.     public Vector3 scale;
    52.  
    53.     public List<VertexWeight> weights = new List<VertexWeight>();
    54. }
    55.  
    56. [System.Serializable]
    57. public class ShapeIndex
    58. {
    59.     public int shapeStart;
    60.     public int shapeMid;
    61.     public int shapeEnd;
    62.     public bool valid; // false if particle count < 3
    63. }
    64.  
    65. public class VertMapAsset : ScriptableObject {
    66.     // index = soft body particle index. Value = vertex index.
    67.     public List<int> vertexParticleMap;
    68.     public List<Vector3> particleRestPositions;
    69.     public List<int> nearestVertIndex;
    70.     public List<int> uniqueIndex;
    71.     public WeightList[] particleNodeWeights; // one per node (vert). Weights of standard mesh
    72.     public List<ShapeIndex> shapeIndex;
    73. }
    74.  
    FlexAnimation.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using uFlex;
    4.  
    5. #if UNITY_EDITOR
    6. using UnityEditor; // required to build assets
    7. #endif
    8.  
    9. public class VertMapAssetBuilder
    10. {
    11.     public FlexShapeMatching flexShapeMatching;
    12.     public VertMapAsset vertMapAsset;
    13.     public SkinnedMeshRenderer skin;
    14.  
    15.     public float scale = 1;
    16.  
    17.     public VertMapAssetBuilder(FlexShapeMatching flexShapeMatching, VertMapAsset vertMapAsset, SkinnedMeshRenderer skin)
    18.     {
    19.         this.flexShapeMatching = flexShapeMatching;
    20.         this.vertMapAsset = vertMapAsset;
    21.         this.skin = skin;
    22.     }
    23.  
    24.     private List<Vector3> GetFlexShapeMatchingPositions()
    25.     {
    26.         List<Vector3> vertPos = new List<Vector3>();
    27.  
    28.         FlexShapeMatching shapes = this.flexShapeMatching;
    29.         int shapeIndex = 0;
    30.         int shapeIndexOffset = shapes.m_shapesIndex;
    31.         int shapeStart = 0;
    32.  
    33.         for (int s = 0; s < shapes.m_shapesCount; s++)
    34.         {
    35.             shapeIndex++;
    36.             int shapeEnd = shapes.m_shapeOffsets[s];
    37.             for (int i = shapeStart; i < shapeEnd; ++i)
    38.             {
    39.                 Vector3 pos = flexShapeMatching.m_shapeRestPositions[i] + shapes.m_shapeCenters[s];
    40.                 vertPos.Add(pos);
    41.                 shapeIndexOffset++;
    42.             }
    43.  
    44.             shapeStart = shapeEnd;
    45.         }
    46.  
    47.         return vertPos;
    48.     }
    49.  
    50.     Vector3 RoundVec(Vector3 value)
    51.     {
    52.         float dp = 100000.0f;
    53.         value.x = Mathf.RoundToInt(value.x * dp) / dp;
    54.         value.y = Mathf.RoundToInt(value.y * dp) / dp;
    55.         value.z = Mathf.RoundToInt(value.z * dp) / dp;
    56.         return value;
    57.     }
    58.  
    59.     private int GetNearestVertIndex(Vector3 particlePos, ref Vector3[] cachedVertices)
    60.     {
    61.         float nearestDist = float.MaxValue;
    62.         int nearestIndex = -1;
    63.         for (int i = 0; i < cachedVertices.Length; i++)
    64.         {
    65.             float dist = Vector3.Distance(particlePos, cachedVertices[i]);
    66.             if (dist < nearestDist)
    67.             {
    68.                 nearestDist = dist;
    69.                 nearestIndex = i;
    70.             }
    71.         }
    72.         return nearestIndex;
    73.     }
    74.  
    75.     private void SetBoneWeights(ref List<Vector3> uniqueParticlePositions, ref List<int> uniqueParticleIndices)
    76.     {
    77.         SkinnedMeshRenderer skinnedMeshRenderer = skin;
    78.  
    79.         // Cache used values rather than accessing straight from the mesh on the loop below
    80.         Vector3[] cachedVertices = skinnedMeshRenderer.sharedMesh.vertices;
    81.         Matrix4x4[] cachedBindposes = skinnedMeshRenderer.sharedMesh.bindposes;
    82.         BoneWeight[] cachedBoneWeights = skinnedMeshRenderer.sharedMesh.boneWeights;
    83.  
    84.         // Make a CWeightList for each bone in the skinned mesh
    85.         WeightList[] nodeWeights = new WeightList[skinnedMeshRenderer.bones.Length];
    86.         for (int i = 0; i < skinnedMeshRenderer.bones.Length; i++)
    87.         {
    88.             nodeWeights[i] = new WeightList();
    89.             nodeWeights[i].boneIndex = i;
    90.             nodeWeights[i].transform = skinnedMeshRenderer.bones[i];
    91.         }
    92.  
    93.         for (int uniqueIndex = 0; uniqueIndex < uniqueParticleIndices.Count; uniqueIndex++)
    94.         {
    95.             Vector3 particlePos = uniqueParticlePositions[uniqueIndex];
    96.             Vector3 nearestPos = particlePos * scale; // required for animation scale. Don't modify particlePos
    97.             int i = GetNearestVertIndex(nearestPos, ref cachedVertices);
    98.  
    99.             vertMapAsset.nearestVertIndex.Add(i);
    100.             vertMapAsset.uniqueIndex.Add(uniqueIndex);
    101.             BoneWeight bw = cachedBoneWeights[i];
    102.  
    103.             if (bw.weight0 != 0.0f)
    104.             {
    105.                 Vector3 localPt = cachedBindposes[bw.boneIndex0].MultiplyPoint3x4(particlePos);// cachedVertices[i]);
    106.                 nodeWeights[bw.boneIndex0].weights.Add(new VertexWeight(uniqueIndex, localPt, bw.weight0));
    107.             }
    108.             if (bw.weight1 != 0.0f)
    109.             {
    110.                 Vector3 localPt = cachedBindposes[bw.boneIndex1].MultiplyPoint3x4(particlePos);//cachedVertices[i]);
    111.                 nodeWeights[bw.boneIndex1].weights.Add(new VertexWeight(uniqueIndex, localPt, bw.weight1));
    112.             }
    113.             if (bw.weight2 != 0.0f)
    114.             {
    115.                 Vector3 localPt = cachedBindposes[bw.boneIndex2].MultiplyPoint3x4(particlePos);//cachedVertices[i]);
    116.                 nodeWeights[bw.boneIndex2].weights.Add(new VertexWeight(uniqueIndex, localPt, bw.weight2));
    117.             }
    118.             if (bw.weight3 != 0.0f)
    119.             {
    120.                 Vector3 localPt = cachedBindposes[bw.boneIndex3].MultiplyPoint3x4(particlePos);//cachedVertices[i]);
    121.                 nodeWeights[bw.boneIndex3].weights.Add(new VertexWeight(uniqueIndex, localPt, bw.weight3));
    122.             }
    123.         }
    124.  
    125.         vertMapAsset.particleNodeWeights = nodeWeights;
    126.  
    127.     }
    128.  
    129.     public void CreateAsset()
    130.     {
    131.         vertMapAsset.vertexParticleMap = new List<int>();
    132.  
    133.         List<Vector3> particlePositions = GetFlexShapeMatchingPositions();
    134.         List<Vector3> uniqueParticlePositions = new List<Vector3>();
    135.         List<int> uniqueParticleIndices = new List<int>();
    136.         for (int i = 0; i < particlePositions.Count; i++)
    137.         {
    138.             Vector3 vert = particlePositions[i];
    139.             vert = RoundVec(vert);
    140.  
    141.             if (uniqueParticlePositions.Contains(vert) == false)
    142.             {
    143.                 uniqueParticleIndices.Add(i);
    144.                 uniqueParticlePositions.Add(vert);
    145.             }
    146.  
    147.  
    148.             vertMapAsset.vertexParticleMap.Add(uniqueParticlePositions.IndexOf(vert));
    149.         }
    150.  
    151.         vertMapAsset.particleRestPositions = uniqueParticlePositions;
    152.         vertMapAsset.nearestVertIndex = new List<int>();
    153.         vertMapAsset.uniqueIndex = new List<int>();
    154.         SetBoneWeights(ref uniqueParticlePositions, ref uniqueParticleIndices);
    155.  
    156.      
    157.  
    158.         // trigger save
    159. #if UNITY_EDITOR
    160.         UnityEditor.EditorUtility.SetDirty(vertMapAsset);
    161. #endif
    162.     }
    163. }
    164.  
    165. public class FlexAnimation : FlexProcessor
    166. {
    167.     public bool rebuildVertMapAsset = false;
    168.     public VertMapAsset vertMapAsset;
    169.     public SkinnedMeshRenderer skinnedMeshRenderer;
    170.     private FlexShapeMatching flexShapeMatching;
    171.     private bool firstRun = true;
    172.     private Vector3[] particlePositions; // world particle positions
    173.     public bool drawVertMapAsset = false;
    174.     public bool useBasicRotationMap = false;
    175.  
    176.     Vector3[] _cachedVertices;
    177.     Matrix4x4[] _cachedBindposes;
    178.     BoneWeight[] _cachedBoneWeights;
    179.     float refreshRate = 1.0f / 30.0f;
    180.     float timeDelta;
    181.  
    182.     /*
    183.     private FlexSkinnedMesh m_flexSkinnedMesh;
    184.  
    185.     void Start()
    186.     {
    187.  
    188.        // m_shapes = GetComponent<FlexShapeMatching>();
    189.         m_flexSkinnedMesh = GetComponent<FlexSkinnedMesh>();
    190.         m_flexSkinnedMesh.enabled = false;
    191.     }
    192.     */
    193.  
    194.     public float scale = 1;
    195.  
    196.     [HideInInspector]
    197.     public bool DrawBones = false;
    198.  
    199.     void OnEnable()
    200.     {
    201.         if (flexShapeMatching == null)
    202.             flexShapeMatching = GetComponent<FlexShapeMatching>();
    203.     }
    204.  
    205.     public override void PreContainerUpdate(FlexSolver solver, FlexContainer cntr, FlexParameters parameters)
    206.     {
    207.         if (enabled == false)
    208.             return;
    209.  
    210.         if (firstRun)
    211.         {
    212.             firstRun = false;
    213.  
    214.             _cachedVertices = skinnedMeshRenderer.sharedMesh.vertices;
    215.             _cachedBindposes = skinnedMeshRenderer.sharedMesh.bindposes;
    216.             _cachedBoneWeights = skinnedMeshRenderer.sharedMesh.boneWeights;
    217.  
    218.             bool createAsset = vertMapAsset == null || rebuildVertMapAsset;
    219.  
    220.             if (vertMapAsset == null)
    221.             {
    222. #if UNITY_EDITOR
    223.                 vertMapAsset = ScriptableObject.CreateInstance<VertMapAsset>();
    224.                 AssetDatabase.CreateAsset(vertMapAsset, "Assets/" + this.name + "VertMapAsset.asset");
    225.                 AssetDatabase.SaveAssets();
    226.                 EditorUtility.FocusProjectWindow();
    227.                 Selection.activeObject = vertMapAsset;
    228. #endif
    229.             }
    230.  
    231.             if (createAsset)
    232.             {
    233.                 vertMapAsset.vertexParticleMap = new List<int>();
    234.                 VertMapAssetBuilder vertMapAssetBuilder = new VertMapAssetBuilder(flexShapeMatching, vertMapAsset, skinnedMeshRenderer);
    235.                 vertMapAssetBuilder.scale = this.scale;
    236.                 vertMapAssetBuilder.CreateAsset();
    237.  
    238.                 if (useBasicRotationMap)
    239.                     SetupShapeIndicesBasic();
    240.                 else
    241.                     SetupShapeIndices();
    242.             }
    243.  
    244.             particlePositions = new Vector3[vertMapAsset.particleRestPositions.Count];
    245.             UpdateParticlePositions();
    246.  
    247.         }
    248.         else
    249.         {
    250.             if (timeDelta < refreshRate)
    251.                 return;
    252.  
    253.             // Only process once 30 times a second
    254.             UpdateParticlePositions();
    255.             MatchShapes(); // apply to soft body
    256.  
    257.             while (timeDelta >= refreshRate)
    258.                 timeDelta -= refreshRate;
    259.         }
    260.     }
    261.  
    262.     public void UpdateParticlePositions()
    263.     {
    264.         for (int i = 0; i < particlePositions.Length; i++)
    265.         {
    266.             particlePositions[i] = Vector3.zero;
    267.         }
    268.  
    269.         // Now get the local positions of all weighted indices...
    270.         foreach (WeightList wList in vertMapAsset.particleNodeWeights)
    271.         {
    272.             foreach (VertexWeight vw in wList.weights)
    273.             {
    274.                 Transform t = skinnedMeshRenderer.bones[wList.boneIndex];
    275.                 particlePositions[vw.index] += t.localToWorldMatrix.MultiplyPoint3x4(vw.localPosition) * vw.weight;
    276.             }
    277.         }
    278.  
    279.         // Now convert each point into local coordinates of this object.
    280.         for (int i = 0; i < particlePositions.Length; i++)
    281.         {
    282.             particlePositions[i] = transform.InverseTransformPoint(particlePositions[i]);
    283.         }
    284.     }
    285.  
    286.  
    287.     public bool fixRotation = true;
    288.     private bool shapeInitCalled = false;
    289.     public List<Quaternion> initShapeRotations = null;
    290.     public List<Quaternion> initShapeRotationDirections = null;
    291.     public List<Quaternion> currentShapeRotationDirections = null;
    292.  
    293.     public List<Quaternion> globalInitShapeRotationDirections = null;
    294.     public List<Quaternion> globalCurrentShapeRotationDirections = null;
    295.     public List<Vector3> globalInitShapeA = null;
    296.     public List<Vector3> globalInitShapeB = null;
    297.     public List<Vector3> globalInitShapeC = null;
    298.     public List<Vector3> globalCurrentShapeA = null;
    299.     public List<Vector3> globalCurrentShapeB = null;
    300.     public List<Vector3> globalCurrentShapeC = null;
    301.  
    302.  
    303.     // Get particle indexes to create quaterion.
    304.     // select the max y and z positions that have the most distance between them e.g max y and z could be the same position
    305.     public void SetupShapeIndices()
    306.     {
    307.         FlexShapeMatching shapes = this.flexShapeMatching;
    308.         vertMapAsset.shapeIndex = new List<ShapeIndex>();
    309.  
    310.      
    311.         int shapeIndexOffset = shapes.m_shapesIndex;
    312.         int shapeStart = 0;
    313.         for (int s = 0; s < shapes.m_shapesCount; s++)
    314.         {
    315.             int shapeEnd = shapes.m_shapeOffsets[s];
    316.  
    317.             // verts should be normalized. center pos closest to zero
    318.             int closestToZeroIndex = 0;
    319.             float closestDist = float.MaxValue;
    320.             Vector3 minDist = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); // ve direction
    321.             Vector3 maxDist = new Vector3(float.MinValue, float.MinValue, float.MinValue);
    322.             // Vector3 minDistIndex = new Vector3(-1, -1, -1);
    323.             //Vector3 maxDistIndex = new Vector3(1, 1, 1);
    324.  
    325.             int minDistIndexX = -1;
    326.             int maxDistIndexX = -1;
    327.             int minDistIndexY = -1;
    328.             int maxDistIndexY = -1;
    329.             int minDistIndexZ = -1;
    330.             int maxDistIndexZ = -1;
    331.  
    332.  
    333.             for (int i= shapeStart; i< shapeEnd; i++)
    334.             {
    335.                 Vector3 pos = shapes.m_shapeRestPositions[i];
    336.                 float dist = pos.magnitude;
    337.                 if (dist < closestDist)
    338.                 {
    339.                     closestToZeroIndex = i;
    340.                     closestDist = dist;
    341.                 }
    342.  
    343.                 if (pos.x < minDist.x)
    344.                 {
    345.                     minDist.x = pos.x;
    346.                     minDistIndexX = i;
    347.                 }
    348.                 if (pos.y < minDist.y)
    349.                 {
    350.                     minDist.y = pos.y;
    351.                     minDistIndexY = i;
    352.                 }
    353.                 if (pos.z < minDist.z)
    354.                 {
    355.                     minDist.z = pos.z;
    356.                     minDistIndexZ = i;
    357.                 }
    358.              
    359.                 if (pos.x > maxDist.x)
    360.                 {
    361.                     maxDist.x = pos.x;
    362.                     maxDistIndexX = i;
    363.                 }
    364.                 if (pos.y > maxDist.y)
    365.                 {
    366.                     maxDist.y = pos.y;
    367.                     maxDistIndexY = i;
    368.                 }
    369.                 if (pos.z > maxDist.z)
    370.                 {
    371.                     maxDist.z = pos.z;
    372.                     maxDistIndexZ = i;
    373.                 }
    374.             }
    375.  
    376.             // bool positiveXGreater = maxDist.x > Mathf.Abs(minDist.x);
    377.             //bool positiveYGreater = maxDist.y > Mathf.Abs(minDist.y);
    378.             //bool positiveZGreater = maxDist.z > Mathf.Abs(minDist.z);
    379.  
    380.  
    381.             // just use y and z for now (might need to test other axis for flat surfaces)
    382.             // Note: its posible that the max x and z values are the same point. find max distance between points
    383.             Vector3 posXMin = shapes.m_shapeRestPositions[minDistIndexX];
    384.             Vector3 posXMax = shapes.m_shapeRestPositions[maxDistIndexX];
    385.             Vector3 posYMin = shapes.m_shapeRestPositions[minDistIndexY];
    386.             Vector3 posYMax = shapes.m_shapeRestPositions[maxDistIndexY];
    387.             Vector3 posZMin = shapes.m_shapeRestPositions[minDistIndexZ];
    388.             Vector3 posZMax = shapes.m_shapeRestPositions[maxDistIndexZ];
    389.  
    390.             /*float yMinZMinDist = Vector3.Distance(posYMin, posZMin);
    391.             float yMinZMaxDist = Vector3.Distance(posYMin, posZMax);
    392.             float yMaxZMinDist = Vector3.Distance(posYMax, posZMin);
    393.             float yMaxZMaxDist = Vector3.Distance(posYMax, posZMax);*/
    394.             float a = minDistIndexY == minDistIndexZ ? float.MaxValue : Vector3.Angle(posYMin, posZMin); //Vector3.Distance(posYMin, posZMin);
    395.             float b = minDistIndexY == maxDistIndexZ ? float.MaxValue : Vector3.Angle(posYMin, posZMax);
    396.             float c = maxDistIndexY == minDistIndexZ ? float.MaxValue : Vector3.Angle(posYMax, posZMin);
    397.             float d = maxDistIndexY == maxDistIndexZ ? float.MaxValue : Vector3.Angle(posYMax, posZMax);
    398.  
    399.             float e = minDistIndexX == minDistIndexZ ? float.MaxValue : Vector3.Angle(posXMin, posZMin);
    400.             float f = minDistIndexX == maxDistIndexZ ? float.MaxValue : Vector3.Angle(posXMin, posZMax);
    401.             float g = maxDistIndexX == minDistIndexZ ? float.MaxValue : Vector3.Angle(posXMax, posZMin);
    402.             float h = maxDistIndexX == maxDistIndexZ ? float.MaxValue : Vector3.Angle(posXMax, posZMax);
    403.  
    404.  
    405.             List<float> findMinErr = new List<float>();
    406.             findMinErr.AddRange(new float[] { a,b,c,d, e, f, g});
    407.  
    408.             //List<int> findMinErrIndex = new List<int>();
    409.             // findMinErr.AddRange(new int[] { minDistIndexY, maxDistIndexY, c, d });
    410.             if (s == 0)
    411.             {
    412.                 s = s;
    413.             }
    414.             float minErr = float.MaxValue;// MinValue;
    415.             int maxIndex = -1;
    416.             for (int i=0; i<findMinErr.Count; i++)
    417.             {
    418.                 if (findMinErr[i] == float.MaxValue)
    419.                     continue;
    420.                 float err = Mathf.Abs(findMinErr[i] - 90); // closest to 90 degress
    421.                 if (err < minErr) //  findMax[i] > max)
    422.                 {
    423.                     minErr = err;// findMinErr[i];
    424.                     maxIndex = i;
    425.                 }
    426.             }
    427.             ShapeIndex shapeIndex = new ShapeIndex();
    428.             shapeIndex.shapeMid = closestToZeroIndex;
    429.          
    430.             switch (maxIndex)
    431.             {
    432.                 // y & z
    433.                 case 0:
    434.                     shapeIndex.shapeStart = minDistIndexY;
    435.                     shapeIndex.shapeEnd = minDistIndexZ;
    436.                     break;
    437.                 case 1:
    438.                     shapeIndex.shapeStart = minDistIndexY;
    439.                     shapeIndex.shapeEnd = maxDistIndexZ;
    440.                     break;
    441.                 case 2:
    442.                     shapeIndex.shapeStart = maxDistIndexY;
    443.                     shapeIndex.shapeEnd = minDistIndexZ;
    444.                     break;
    445.                 case 3:
    446.                     shapeIndex.shapeStart = maxDistIndexY;
    447.                     shapeIndex.shapeEnd = maxDistIndexZ;
    448.                     break;
    449.  
    450.                     // x & z
    451.                 case 4:
    452.                     shapeIndex.shapeStart = minDistIndexX;
    453.                     shapeIndex.shapeEnd = minDistIndexZ;
    454.                     break;
    455.                 case 5:
    456.                     shapeIndex.shapeStart = minDistIndexX;
    457.                     shapeIndex.shapeEnd = maxDistIndexZ;
    458.                     break;
    459.                 case 6:
    460.                     shapeIndex.shapeStart = maxDistIndexX;
    461.                     shapeIndex.shapeEnd = minDistIndexZ;
    462.                     break;
    463.                 case 7:
    464.                     shapeIndex.shapeStart = maxDistIndexX;
    465.                     shapeIndex.shapeEnd = maxDistIndexZ;
    466.                     break;
    467.             }
    468.  
    469.             int shapeCount = shapeEnd - shapeStart;
    470.             if (shapeIndex.shapeStart == shapeIndex.shapeEnd)
    471.             {
    472.              
    473.                 Debug.LogError("Start == End? Less than three particles? " + shapeCount);
    474.             }
    475.             shapeIndex.valid = shapeCount >= 3 && shapeIndex.shapeStart != shapeIndex.shapeEnd && shapeIndex.shapeStart != shapeIndex.shapeMid && shapeIndex.shapeEnd != shapeIndex.shapeMid;
    476.  
    477.  
    478.             vertMapAsset.shapeIndex.Add(shapeIndex);
    479.  
    480.             shapeStart = shapeEnd;
    481.         }
    482.     }
    483.  
    484.  
    485.     public void SetupShapeIndicesBasic()
    486.     {
    487.         FlexShapeMatching shapes = this.flexShapeMatching;
    488.         vertMapAsset.shapeIndex = new List<ShapeIndex>();
    489.      
    490.         int shapeIndexOffset = shapes.m_shapesIndex;
    491.         int shapeStart = 0;
    492.         for (int s = 0; s < shapes.m_shapesCount; s++)
    493.         {
    494.             int shapeEnd = shapes.m_shapeOffsets[s];
    495.             int shapeCount = shapeEnd - shapeStart;
    496.             ShapeIndex shapeIndex = new ShapeIndex();
    497.             shapeIndex.shapeMid = shapeStart + (shapeCount / 2);
    498.             shapeIndex.shapeStart = shapeStart;
    499.             shapeIndex.shapeEnd = shapeEnd - 1;
    500.             shapeIndex.valid = shapeIndex.shapeStart != shapeIndex.shapeEnd && shapeIndex.shapeStart != shapeIndex.shapeMid;
    501.  
    502.             vertMapAsset.shapeIndex.Add(shapeIndex);
    503.  
    504.             shapeStart = shapeEnd;
    505.         }
    506.     }
    507.  
    508.     private void SetupInitShapeRotations()
    509.     {
    510.         FlexShapeMatching shapes = this.flexShapeMatching;
    511.         initShapeRotations = new List<Quaternion>();
    512.         initShapeRotationDirections = new List<Quaternion>();
    513.         currentShapeRotationDirections = new List<Quaternion>();
    514.         globalInitShapeRotationDirections = new List<Quaternion>();
    515.         globalCurrentShapeRotationDirections = new List<Quaternion>();
    516.  
    517.  
    518.         globalInitShapeA = new List<Vector3>();
    519.         globalInitShapeB = new List<Vector3>();
    520.         globalInitShapeC = new List<Vector3>();
    521.  
    522.         globalCurrentShapeA = new List<Vector3>();
    523.         globalCurrentShapeB = new List<Vector3>();
    524.         globalCurrentShapeC = new List<Vector3>();
    525.  
    526.  
    527.         int shapeIndex = 0;
    528.         int shapeIndexOffset = shapes.m_shapesIndex;
    529.         int shapeStart = 0;
    530.         for (int s = 0; s < shapes.m_shapesCount; s++)
    531.         {
    532.             shapeIndex++;
    533.             initShapeRotations.Add(shapes.m_shapeRotations[s]);
    534.  
    535.  
    536.             int shapeEnd = shapes.m_shapeOffsets[s];
    537.  
    538.             int shapeCount = shapeEnd - shapeStart;
    539.  
    540.             if (shapeCount <= 3)
    541.             {
    542.                 //Debug.LogWarning("Missing Particles. Each shape should have at least three particles");
    543.             }
    544.  
    545.             int shapeMid = shapeStart + (shapeCount / 2);
    546.  
    547.             // override positions
    548.             int shapeStart2 = vertMapAsset.shapeIndex[s].shapeStart;
    549.             int shapeMid2 = vertMapAsset.shapeIndex[s].shapeMid;
    550.             int shapeEnd2 = vertMapAsset.shapeIndex[s].shapeEnd;
    551.  
    552.             Vector3 a = shapes.m_shapeRestPositions[shapeStart2];
    553.             Vector3 b = shapes.m_shapeRestPositions[shapeMid2];
    554.             Vector3 c = shapes.m_shapeRestPositions[shapeEnd2];// - 1];
    555.  
    556.             Vector3 dirA = a - b;
    557.             Vector3 dirC = b - c;
    558.  
    559.             Quaternion rot = vertMapAsset.shapeIndex[s].valid ? Quaternion.LookRotation(dirA, dirC) : Quaternion.identity;
    560.             initShapeRotationDirections.Add(rot);
    561.             currentShapeRotationDirections.Add(rot);
    562.  
    563.  
    564.             //////////
    565.             int startIndex = vertMapAsset.vertexParticleMap[shapeStart2];
    566.             int midIndex = vertMapAsset.vertexParticleMap[shapeMid2];
    567.             int endIndex = vertMapAsset.vertexParticleMap[shapeEnd2];// - 1];
    568.             Vector3 posA = particlePositions[startIndex];
    569.             Vector3 posB = particlePositions[midIndex];
    570.             Vector3 posC = particlePositions[endIndex];
    571.             globalInitShapeA.Add(posA);
    572.             globalInitShapeB.Add(posB);
    573.             globalInitShapeC.Add(posC);
    574.             globalCurrentShapeA.Add(posA);
    575.             globalCurrentShapeB.Add(posB);
    576.             globalCurrentShapeC.Add(posC);
    577.  
    578.             Vector3 dir1 = posA - posB;
    579.             Vector3 dir2 = posB - posC;
    580.             Quaternion rotB = vertMapAsset.shapeIndex[s].valid ? Quaternion.LookRotation(dirA, dirC) : Quaternion.identity;
    581.             globalInitShapeRotationDirections.Add(rotB);
    582.             globalCurrentShapeRotationDirections.Add(rotB);
    583.  
    584.             shapeStart = shapeEnd;
    585.         }
    586.     }
    587.  
    588.  
    589.     private void MatchShapes()
    590.     {
    591.  
    592.         if (shapeInitCalled == false)
    593.         {
    594.             shapeInitCalled = true;
    595.             SetupInitShapeRotations();
    596.         }
    597.  
    598.  
    599.         FlexShapeMatching shapes = this.flexShapeMatching;
    600.  
    601.         int shapeIndex = 0;
    602.         int shapeIndexOffset = shapes.m_shapesIndex;
    603.         int shapeStart = 0;
    604.  
    605.         int vertIndex = 0;
    606.         for (int s = 0; s < shapes.m_shapesCount; s++)
    607.         {
    608.             Vector3 shapeCenter = new Vector3();
    609.             shapeIndex++;
    610.  
    611.             int shapeEnd = shapes.m_shapeOffsets[s];
    612.  
    613.  
    614.             // Quaternion curRot = shapes.m_shapeRotations[s];
    615.             // shapes.m_shapeRotations[s] = Quaternion.identity;
    616.  
    617.             // curRot = curRot * Quaternion.Euler(10, 0, 0);
    618.             //shapes.m_shapeRotations[s] = curRot * Quaternion.Euler(10, 0, 0);
    619.             //shapes.m_shapeRotations[s] = Quaternion.Euler(10, 0, 0) * curRot;
    620.  
    621.             int shapeCount = shapeEnd - shapeStart;
    622.  
    623.             // update rotation offset
    624.             int shapeMid = shapeStart + (shapeCount / 2);
    625.  
    626.  
    627.             // override positions
    628.             int shapeStart2 = vertMapAsset.shapeIndex[s].shapeStart;
    629.             int shapeMid2 = vertMapAsset.shapeIndex[s].shapeMid;
    630.             int shapeEnd2 = vertMapAsset.shapeIndex[s].shapeEnd;
    631.  
    632.             Vector3 a = shapes.m_shapeRestPositions[shapeStart2];
    633.             Vector3 b = shapes.m_shapeRestPositions[shapeMid2];
    634.             Vector3 c = shapes.m_shapeRestPositions[shapeEnd2];// - 1];
    635.             Vector3 dirA = a - b;
    636.             Vector3 dirC = b - c;
    637.             Quaternion rot = vertMapAsset.shapeIndex[s].valid ? Quaternion.LookRotation(dirA, dirC) : Quaternion.identity;
    638.             //initShapeRotationDirections.Add(rot);
    639.             currentShapeRotationDirections[s] = rot;
    640.  
    641.             //////////
    642.             int startIndex = vertMapAsset.vertexParticleMap[shapeStart2];
    643.             int midIndex = vertMapAsset.vertexParticleMap[shapeMid2];
    644.             int endIndex = vertMapAsset.vertexParticleMap[shapeEnd2];// - 1];
    645.             Vector3 posA = particlePositions[startIndex];
    646.             Vector3 posB = particlePositions[midIndex];
    647.             Vector3 posC = particlePositions[endIndex];
    648.             Vector3 dir1 = posA - posB;
    649.             Vector3 dir2 = posB - posC;
    650.  
    651.             globalCurrentShapeA.Add(posA);
    652.             globalCurrentShapeB.Add(posB);
    653.             globalCurrentShapeC.Add(posC);
    654.  
    655.             Quaternion rotB = vertMapAsset.shapeIndex[s].valid ? Quaternion.LookRotation(dir1, dir2) : Quaternion.identity;
    656.             //globalInitShapeRotationDirections.Add(rotB);
    657.             globalCurrentShapeRotationDirections[s] = rotB;
    658.  
    659.             //shapes.m_shapeRotations [s]= Quaternion.Euler(90, 0, 0);
    660.  
    661.             int origShapeIndexOffset = shapeIndexOffset;
    662.             for (int i = shapeStart; i < shapeEnd; ++i)
    663.             {
    664.                 int mappedIndex = vertMapAsset.vertexParticleMap[vertIndex];
    665.                 Vector3 pos = particlePositions[mappedIndex];
    666.                 pos *= scale;
    667.                 shapes.m_shapeRestPositions[shapeIndexOffset] = pos;
    668.  
    669.                 shapeCenter += pos;
    670.                 shapeIndexOffset++;
    671.                 vertIndex++;
    672.             }
    673.  
    674.             shapeCenter /= shapeCount;
    675.  
    676.  
    677.  
    678.             Quaternion offsetA = globalInitShapeRotationDirections[s] * Quaternion.Inverse(globalCurrentShapeRotationDirections[s]);
    679.             Quaternion rotQ = offsetA;// Quaternion.Inverse(offsetA);// Quaternion.Euler(45, 0, 0);
    680.             for (int i = shapeStart; i < shapeEnd; ++i)
    681.             {
    682.                 Vector3 pos = shapes.m_shapeRestPositions[origShapeIndexOffset];
    683.  
    684.                 pos -= shapeCenter;
    685.                 if (fixRotation)
    686.                 {
    687.                     pos = rotQ * pos;
    688.                 }
    689.  
    690.                 shapes.m_shapeRestPositions[origShapeIndexOffset] = pos;
    691.              
    692.                 origShapeIndexOffset++;
    693.             }
    694.  
    695.  
    696.  
    697.             shapeStart = shapeEnd;
    698.         }
    699.     }
    700.  
    701.     public void Update()
    702.     {
    703.         timeDelta += Time.deltaTime;
    704.         //UpdateShapes();
    705.     }
    706.  
    707.  
    708.     private List<Color> shapeColors;
    709.     public int drawVertMapIndex = -1;
    710.     public virtual void OnDrawGizmos()
    711.     {
    712.         if (drawVertMapAsset && vertMapAsset != null)
    713.         {
    714.             float boxSize = 0.2f;
    715.             /*
    716.             Gizmos.color = Color.red;
    717.             foreach (Vector3 vert in vertMapAssetBuilder._cachedVertices)
    718.             {
    719.                 Gizmos.DrawCube(vert, new Vector3(boxSize, boxSize, boxSize));
    720.             }
    721.  
    722.             Gizmos.color = Color.blue;
    723.             foreach (Vector3 vert in vertMapAssetBuilder._uniqueParticlePositions)
    724.             {
    725.                 Gizmos.DrawCube(vert, new Vector3(boxSize, boxSize, boxSize));
    726.             }
    727.             */
    728.  
    729.  
    730.             if (particlePositions != null)
    731.             {
    732.  
    733.                 FlexShapeMatching shapes = this.flexShapeMatching;
    734.                 int shapeIndexOffset = shapes.m_shapesIndex;
    735.                 int shapeStart = 0;
    736.  
    737.                 if (shapeColors == null || shapeColors.Count == 0)
    738.                 {
    739.                     shapeColors = new List<Color>();
    740.                     for (int s = 0; s < shapes.m_shapesCount; s++)
    741.                     {
    742.                         shapeColors.Add(new Color(Random.Range(0.0f, 1.0f), Random.Range(0.0f, 1.0f), Random.Range(0.0f, 1.0f), 1));
    743.                     }
    744.                 }
    745.  
    746.                 for (int s = 0; s < shapes.m_shapesCount; s++)
    747.                 {
    748.                     int shapeEnd = shapes.m_shapeOffsets[s];
    749.                     if (drawVertMapIndex >= 0 && drawVertMapIndex != s)
    750.                     {
    751.                         shapeStart = shapeEnd;
    752.                         continue;
    753.                     }
    754.  
    755.  
    756.                     Gizmos.color = shapeColors[s];
    757.                     for (int i = shapeStart; i < shapeEnd; ++i)
    758.                     {
    759.                         int mappedIndex = vertMapAsset.vertexParticleMap[i];
    760.                         Vector3 pos = particlePositions[mappedIndex];
    761.                         pos *= scale;
    762.  
    763.                         Gizmos.DrawCube(pos, new Vector3(boxSize, boxSize, boxSize));
    764.                     }
    765.  
    766.  
    767.  
    768.                     // Bones;
    769.                     /*int shapeCount = shapeEnd - shapeStart;
    770.                     int shapeMid = shapeStart + (shapeCount / 2);
    771.                     Vector3 a = shapes.m_shapeRestPositions[shapeStart];
    772.                     Vector3 b = shapes.m_shapeRestPositions[shapeMid];
    773.                     Vector3 c = shapes.m_shapeRestPositions[shapeEnd - 1];
    774.                     Vector3 dirA = a - b;
    775.                     Vector3 dirC = b - c;
    776.                     Quaternion rot = Quaternion.LookRotation(dirA, dirC);
    777.                     Gizmos.color = Color.red;
    778.                    Gizmos.DrawLine(a,b);
    779.                     Gizmos.color = Color.blue;
    780.                     Gizmos.DrawLine(b, c);*/
    781.                     int shapeCount = shapeEnd - shapeStart;
    782.                     int shapeMid = shapeStart + (shapeCount / 2);
    783.  
    784.  
    785.                     // override positions
    786.                     int shapeStart2 = vertMapAsset.shapeIndex[s].shapeStart;
    787.                     int shapeMid2 = vertMapAsset.shapeIndex[s].shapeMid;
    788.                     int shapeEnd2 = vertMapAsset.shapeIndex[s].shapeEnd;
    789.  
    790.                     int startIndex = vertMapAsset.vertexParticleMap[shapeStart2];
    791.                     int midIndex = vertMapAsset.vertexParticleMap[shapeMid2];
    792.                     int endIndex = vertMapAsset.vertexParticleMap[shapeEnd2];// - 1];
    793.                     Vector3 a = particlePositions[startIndex] * scale;
    794.                     Vector3 b = particlePositions[midIndex] * scale;
    795.                     Vector3 c = particlePositions[endIndex] * scale;
    796.                     Vector3 dir1 = a - b;
    797.                     Vector3 dir2 = b - c;
    798.                     Quaternion rotB = vertMapAsset.shapeIndex[s].valid ? Quaternion.LookRotation(dir1, dir2) : Quaternion.identity;
    799.                     Gizmos.color = Color.red;
    800.                     Gizmos.DrawLine(a, b);
    801.                     Gizmos.color = Color.blue;
    802.                     Gizmos.DrawLine(b, c);
    803.  
    804.                     shapeStart = shapeEnd;
    805.                 }
    806.  
    807.  
    808.  
    809.                 /*
    810.                 Gizmos.color = Color.red;
    811.                 foreach (Vector3 vert in particlePositions)
    812.                 {
    813.                     Gizmos.DrawCube(vert, new Vector3(boxSize, boxSize, boxSize));
    814.                 }*/
    815.             }
    816.  
    817.             /*
    818.             if (currentShapeRotationDirections != null)
    819.             {
    820.                 Gizmos.color = Color.red;
    821.                 Gizmos.DrawLine
    822.                 Quaternion initDir = initShapeRotationDirections[i];
    823.                 Quaternion dir = currentShapeRotationDirections[i];
    824.                 Quaternion offset = dir * Quaternion.Inverse(initDir);
    825.             }
    826.             */
    827.  
    828.         }
    829.     }
    830.  
    831.     /*
    832.     public Vector3 eulerTest;
    833.  
    834.     // Update is called once per frame
    835.     void UpdateShapes()
    836.     {
    837.         if (initShapeRotations == null)
    838.             return;
    839.  
    840.         for (int i = 0; i < flexShapeMatching.m_shapesCount; i++)
    841.         {
    842.             m_flexSkinnedMesh.m_bones[i].localPosition = transform.InverseTransformPoint(flexShapeMatching.m_shapeTranslations[i]);
    843.             //m_flexSkinnedMesh.m_bones[i].localRotation = Quaternion.Inverse(transform.rotation) * flexShapeMatching.m_shapeRotations[i];
    844.  
    845.             Quaternion curRot = flexShapeMatching.m_shapeRotations[i];
    846.             curRot = curRot * Quaternion.Euler(eulerTest);// Quaternion.Euler(90, 0, 0);
    847.  
    848.  
    849.             Quaternion init = initShapeRotations[i];
    850.             Quaternion initDir = initShapeRotationDirections[i];
    851.             Quaternion dir = currentShapeRotationDirections[i];
    852.             Quaternion offset = dir * Quaternion.Inverse(initDir); // dir - init dir
    853.  
    854.             //Quaternion offset =  Quaternion.Inverse(initDir) * dir;
    855.             //Quaternion offset = Quaternion.Inverse(dir) * initDir; // invalid
    856.  
    857.             //Quaternion offset = Quaternion.Inverse(dir) * initDir;
    858.             //curRot = init * Quaternion.Inverse(offset);
    859.             //curRot = Quaternion.identity; // same as init?
    860.             curRot = init * offset;
    861.             //curRot = Quaternion.Inverse(curRot);
    862.             m_flexSkinnedMesh.m_bones[i].localRotation = Quaternion.Inverse(transform.rotation) * curRot;
    863.         }
    864.  
    865.  
    866.     }*/
    867. }
    868.  
     
    AKQJ10 likes this.
  9. AKQJ10

    AKQJ10

    Joined:
    Feb 9, 2012
    Posts:
    33
  10. sashahush

    sashahush

    Joined:
    Sep 5, 2012
    Posts:
    75
    Hi there, i have been kindly directed to this thread in order to try and have flex run on my animated skeletal mesh.
    I'm using Maya skinned characters.
    What should be my steps in order to have a character animated with flex running on the skin?

    Cheers!
     
  11. CodeKiwi

    CodeKiwi

    Joined:
    Oct 27, 2016
    Posts:
    73
    Sorry, I haven’t tried FleX in Maya before. Animating the particles like in the “uFlex soft body skeleton animation test 3” video would require some custom code. I’m guessing Maya can probably link soft bodies to rigid bodies similar to the “UE4 FleX Bone Bone Tutorial” video above. Unfortunately, I’ve found that linking soft bodies to rigid bodies creates odd effects e.g hair and clothing don’t collide correctly unless they move really slow.
     
    korzen303 likes this.
  12. sashahush

    sashahush

    Joined:
    Sep 5, 2012
    Posts:
    75
    Hi there thanks for the reply High Noon, so i'm following the steps above and there this step
    • Add the scripts VertMapAsset.cs and FlexAnimation.cs.
    What do i apply VertMapAsset.cs to?

    Cheers!
     
  13. sashahush

    sashahush

    Joined:
    Sep 5, 2012
    Posts:
    75
    Ah nevermind i got it to work! Fantastic work!
     
    korzen303 likes this.
  14. KAMI521

    KAMI521

    Joined:
    Jun 22, 2017
    Posts:
    5
    @korzen303 In the unite2016,The Uflex demo that woman has be dance with cloth,but I can not find it has been in Uflex0.55,How to it Play? The Function has been shared in github? or the theory could be share?
     
  15. CodeKiwi

    CodeKiwi

    Joined:
    Oct 27, 2016
    Posts:
    73
    I’m guessing you mean this
    .

    I think the part at the start with the animated character is not using FleX (custom physics engine made by the presenter). The simulation with the static character and cloth at the end is using FleX and can be made with standard UFleX.

    If you want to create an animated character with cloth you could try animating a rigid body instead of using a soft body like in this example (custom code). I used a soft body so the mesh could follow the shape (creates new deform bones) and interact with the environment. If you use a rigid body then you just need to display the animated mesh at the same location. The end result should be similar to the “uFlex soft body skeleton animation test 3” clip except the character won’t bounce around or be affected by the cloth. Also avoid locking the particle positions or the cloth will tunnel through the character e.g. could pin just a few parts to make sure the character stays in position.

    The main issue I found with this method is that flex only lets you use one radius for all particles. So if you want detailed / thin cloth you will need a large number of particles to create a character. If you want to use clothing I’d suggest looking at something like https://developer.nvidia.com/clothing instead.
     
    theANMATOR2b likes this.
  16. CodeKiwi

    CodeKiwi

    Joined:
    Oct 27, 2016
    Posts:
    73
    I made an animated rigid body before in UE4 (custom code) but never tried combining it with cloth.



    In this example there is a particle for each vertex and the character is hollow (uses less particles). This allows for cloth / liquid that wont penetrate the mesh. You could then add extra fixed particles at the bone positions to keep the world space (looks like a stick figure).



    The shape matching also has a strength setting so you can make the character slightly bounce or turn into a liquid. I'd only try it with low resolution meshes.
     
  17. KAMI521

    KAMI521

    Joined:
    Jun 22, 2017
    Posts:
    5
    Thank you reply,This demo is Great,I keep trying to come true it now.But not work well.
     
unityunity