Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only. On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live. Read our full announcement for more information and let us know if you have any questions.

SplineMesh, the plugin to create curved content

Discussion in 'Assets and Asset Store' started by methusalah999, Dec 14, 2017.

  1. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    Well finding solutions and solving errors are part of the process ^^ The next step is to read the first error in the list and solve it. If you have specific questions, you can paste your code and the error you get here.
     
  2. fahruz

    fahruz

    Joined:
    Mar 5, 2020
    Posts:
    57
    Thanks for the explanations and especially the illustrations. I think everyone here really appreciates the detailed help you're giving and so quickly, all for free (I certainly do).

    Ok, so I'll try to find a point on my shape to consistently orient the up vector when the shape is extruded (like the closest or farthest vertex to the shape's centroid). Would be nice if the SplineExtrusion script had a feature that would automatically do that. :)
     
  3. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    My pleasure!

    Finding the up vector automatically would be great, but as far as I know, there is no way to efficiently anticipate the orientation intended by the users. The few other spline tools that I have tested myself are struggling with this gimbal lock issue as well. But if you have an idea, don't hesitate to propose it ^^
     
  4. ViktoriusAne

    ViktoriusAne

    Joined:
    Sep 11, 2017
    Posts:
    1
    Is it possible to animate spline dots via Timeline animator?
     
  5. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    spline nodes does not have a transform and can't be added in animation as-is. But it's quite trivial to have a game object for each node and animate these game objects instead. A simple script on these objects will make the associated node follow each frame.
     
  6. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    i'm trying now to use the ExampleTentacle.cs. i added an update function because i want to be able to press a key on the keyboard and see a new node popping up. how do you create a new node via script? what is the command? and how can you edit its scale?
     
  7. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    creating a new node is
    spline.AddNode(New SplineNode(myPosition, myDirection));
    . There is an example in the RopeBuilde file.

    to edit a node scale in
    spline.Nodes[i].Scale = myScale;
    . there are Example in ExampleTentacle and ExampleGrowingRoot.
     
  8. fahruz

    fahruz

    Joined:
    Mar 5, 2020
    Posts:
    57
    I've actually set the up vector to zero and get better results, but of course it breaks when the spline has a lot of curves. Using a consistent point on the base shape also improved the results, but I still got twists in extreme cases.

    Another issue I have with extrusion is that there is no top to the created volume. Is there an easy way to remedy that? I tried to create a gameobject that would work as a cap, but I need to put and rotate it into place. Any suggestion on how to calculate that transform?

    SplineMesh - Cap.jpg
     
  9. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    As I said, I don't know of anyway of finding automatically the up vector in a liable way. There is one thing you can try, though.
    - compute several sample on each curve, like 30 samples per curve.
    - set a up vector manually for the first sample of the first curve of your spline.
    - iterate through the samples of the first curve.
    - for each sample, find the up vector with sample.Rotation.Up
    - if the angle between this up and the previous samples up is more than n degrees, you negate the sample. a n of 90° should be ok.
    - when you reach the end of the curve, apply the last sample up vector to the node ending the curve.
    - iterate through the curves with the same method.

    This simple procedure should find a continuity in the up vectors along the curve and prevent the flipping during gimball locks. You can also try and force the next up vector to be close enough to the previous up vector, to ensure that the up won't completely flip from a sample to the next, and the flipping occurs smoothly in the worst scenario.

    You can imagine having a script named AutoSplineOrienter that computes the up vectors this way at start and each time the spline changes, both in the editor and during play time.

    If you implement such a solution, I'm interested in your feedback or even a pull request on github.
     
  10. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    upload_2021-2-24_11-30-52.png

    this is where i am right now. i'm trying to come up with a code that, when the user press x, a new node is created after the last one. i want the position of the new node to depend on that of the last one, so i need to get the coordinates of the last one, right?
    this last non-commented line is supposed to get its value, but i'm getting the error "SplineNode.position is inaccesible due to its protection level". i tried going to the beginning of the code and changing the private Spline spline=null; to public. didn't help

    i also tried to use spline.nodes.Last(); instead of [i-1], but didn't work either
     

    Attached Files:

  11. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    SplineNode.position
    is a private field indeed. Use
    SplineNode.Position
    (with a upper "P") instead, which is the public property giving access to this private field. For your information, the role of the property is to send an event each time the position is changed. Same goes for all the other properties of the SplineNode class.

    Your code can effectively be replaced by
    var lastNodePosition = spline.nodes.Last().Position
    . Please note that the "Last" method is part of a package named Linq. When you try and use it, it will give a compile error unless you add
    using System.Linq;
    at the top of your file.
     
  12. SmartHouseGS

    SmartHouseGS

    Joined:
    Jul 28, 2016
    Posts:
    1
    Great assert !!! Solved quite a few problems in my application. I didn't see anywhere information about testing for iOS mobile devices that's why I write my observation. I had a problem with displaying animations when using the ExampleFollowSpline and ExampleGrowingRoot classes.
    The solution was to copy the EditorUpdate() function into a regular void Update(). Maybe this information is not rocket science but may save someone some time :)
     
  13. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    Thanks for the feedback. Indeed the ExampleXXX are only Example and should not be used as-is.

    Also SplineMesh is compatible with all devices, all render pipelines and all Unity version since 5.6.

    Have fun bending things !
     
  14. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    upload_2021-2-24_22-5-3.png

    when i select the first parts of the mesh on the inspector, i see this. the selcted part shows green. when i select one of the newly created (via script) parts, the same doesn't happen. i think that's preventing me to edit the scale of the new parts. by the way, can i add the scale i want for a new node when i add him?

    you told me to use spline.AddNode(new SplineNode(position,direction), but can i add Scale somehow?
     
  15. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    The green mesh is the mesh collider component. Mesh colliders are generated at startup is you have cjecked the box, but they are not generated automatically when spline change, for performance purpose, because mesh colliders takes time to build. If you don't need colliders , just uncheck generate mesh collider in SplineMeshTiling.

    Changing the Scale is as simple as changing the position
    splineNode.Scale = myScale
    . So in your case, you can do this
    Code (CSharp):
    1. var newNode = new SplineNode(myPosition, myDirection);
    2. newNode.Scale = myScale;
    3. spline.AddNode(newNode);
    You can also on-line it:
    spline.AddNode(new SplineNode(myPosition, myDirection){Scale = myScale});
     
  16. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    i need the new ones to have their own colliders =(
     
  17. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    gameObject.AddComponent<MeshCollider>();


    I think you should take some time and find tutorials to get a stronger base for coding and Unity stuff. You will feel much more autonomous and comfortable this way.
     
  18. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    none of the suggestions you gave me to change the scale worked

    newNode.AddComponent<MeshCollider>();
    this isn't working either =(

    i'm almost getting to where i want, so there aren't many questions left lol
     
    Last edited: Feb 24, 2021
  19. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    when i tried this:
    upload_2021-2-25_13-6-35.png

    i got this:
    .cs(66,19): error CS1061: 'SplineNode' does not contain a definition for 'AddComponent' and no accessible extension method 'AddComponent' accepting a first argument of type 'SplineNode' could be found (are you missing a using directive or an assembly reference?)

    the Scale part i managed to get right
     
  20. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    please help me with this, methusalah999. i'm almost where i need to be
     
  21. JoyTugce

    JoyTugce

    Joined:
    Feb 4, 2021
    Posts:
    1
    Hey, @methusalah999 thanks so much for this amazing asset!

    I am using your ExampleFollowSpline script to move an arrow along a spline. I need to make a ring shape and a coil spring around it as a path for the arrow objects (please see the drawing I attached).

    I could create a circle by manipulating the position and direction values of 4 nodes manually in the editor. However, it gets pretty complicated when I try to spiral a spline around the ring. Do you have any recommendations? Is spline mesh the right tool to achieve what I am trying to do?

    Thanks!
     

    Attached Files:

  22. montanhabio

    montanhabio

    Joined:
    Aug 8, 2020
    Posts:
    6
    I work with animations of marine animals and I need to know how I use SplineMesh in the same way that was done with the animation Whale. Your answer will help a lot in my project. Thank you for your attention
     
  23. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    I would advise that you place your nodes via a script. For example :
    - create PivotA, a game object that has no parent and is located in the center of the circle,
    - create PivotB, a child of PivotA located somewhere on the ring,
    - create HoldYourGuts, a child of PivotB located on the future spiral,
    - orient your circle so that the axis going through the center is Z

    Now, in your onValidate :
    - for myAngle, a float going from 0 to 360, with a step of your choice, do the following,
    - rotate PivotA around its Z with
    PivotA.transform.rotate(0, 0, myAngle, Space.Self);

    - rotate PivotB around its Y (which is the tangent of the circle) with
    PivotB.transform.rotate(0, 0, spiralStep, Space.Self);

    - add a node to the spline using
    HoldYourGuts.transform.position
    .

    Notes:
    - myAngle step reprent the "compression" of your spiral.
    - spiralStep will most likely be 90 or 180 ° so you can have nodes placed around the ring in a regular fashion.
    - You can add the AutoSmoother component to compute the node directions automatically, but you can also find the directions you need by using
    HoldYourGuts.transform.forward
    , or right or up depending on how you oriented HoldYourGuts initially.
    - You will need to set the nodes up vector to avoid gimbal lock. A very simple up vector is
    HoldYourGuts.transform.position - PivotB.transform.position


    Hope it helps !

    Have fun bending things
     
  24. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    You can take a look at ExampleContortAlong in the showcase scene. The idea is to decide an interval along the spline, with a start and end, then just set this interval into the MeshBender to have the mesh bent at this place.

    Tell me if you have more specific questions
     
  25. montanhabio

    montanhabio

    Joined:
    Aug 8, 2020
    Posts:
    6
    Ok I got it. But, I need to put a shark in place of the cylinder. So, that would be my main question. Do I have to change anything in the MeshBender script? Where can I put the shark's mesh in the script? I apologize for the questions, but your Asset is very important to my project. Thank you very much.
     
  26. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    ExampleContortAlong script has fields for mesh and material. There are also fields to rotate or scale the mesh before it is bent. If you need more than one material, you can use a list of materials instead.

    If you want to know more, read the ExampleContortAlong at line 76:
    Code (CSharp):
    1. meshBender.Source = SourceMesh.Build(mesh)
    2.     .Rotate(Quaternion.Euler(rotation))
    3.     .Scale(scale);
    The generated MeshBender is given the mesh by creating an object of type SourceMesh. SourceMesh is used to ready and optimise the mesh before it is bent by the MeshBender component. Note that you should not create a SourceMesh each frame, but only when the mesh must change.
     
  27. montanhabio

    montanhabio

    Joined:
    Aug 8, 2020
    Posts:
    6
    I think I understand. So, in my event, the shark must be a mesh so that it can replace the example cylinder. I already managed to replace it with other meshes, torus, sphere, but the shark hadn’t managed yet. I will try to put the shark as a mesh and see the result. Again, thank you very much and notice about the results.
     
    methusalah999 likes this.
  28. montanhabio

    montanhabio

    Joined:
    Aug 8, 2020
    Posts:
    6
    It worked !
    Thank you
    upload_2021-3-8_12-48-21.png

    upload_2021-3-8_12-48-50.png
     
    methusalah999 likes this.
  29. fakkoweb

    fakkoweb

    Joined:
    Jan 13, 2014
    Posts:
    3
    Hello! I am playing with the RopeBuilder example: when I Play the sample scene, I only see the colliders being animated, while the rope mesh stays in place where it originally was in Edit mode.
    Is this a bug? I am on 2019.4.10f1
     
  30. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    Yes, it's a bug. You can find a fix on the github repository here:
    https://github.com/benoit-dumas/Spl...ripts/MeshProcessing/SplineMeshTiling.cs#L118

    This simple line will set the generated object as static only if you set it not to be updated during play mode.
     
  31. fakkoweb

    fakkoweb

    Joined:
    Jan 13, 2014
    Posts:
    3
    Thank you! I am trying to tweak the Rope Generator: I wish to edit the generated spline in edit mode and both mesh and colliders will follow. Also, I find it a shame that when creating new segments, it recreates the curve straight, while it would be great if it could just add the new segments in the line (by sampling it), following the existing curve.
     
  32. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,473
    Love it. But not a fan of Bezier curves because I need to make 100 of these, would rather have the curve defined only by control points. Is there a fork or an easy way to add Hermite? B-spline is ok too.
     
  33. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    SplineMesh is 100% Bezier curves. You can obtain automatic tangents by adding the AutoSmoothing component to your spline object. This script will find directions for you, so you can focus on node positions.
     
  34. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,473
    nice modularity.
    upload_2021-3-26_11-18-31.png
    when I do that I orbit instead. Any way to change the shortcut used by spline to something that doesn't collide?
    Also it appears that the objects instantiated by ExampleSower during edit mode get destroyed around Start() then re instantiated so I have to wait one frame before doing anything to them. The code example however sets everything up in OnEnable that happens before Start(), where does the destroying actually happen?
     
    Last edited: Mar 26, 2021
  35. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,473
    One thing I ran into in the following scenario where a prefab exists in the scene and I try to edit it:
    upload_2021-3-26_14-41-34.png
    I know sow is just an example so perhaps you don't support such scenario, how would I fix that without unpacking prefab?
     
  36. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    The alt shortcut is indeed outdated since 2018.4 or so, and does not work anymore. You now have to select a node and use the "add node" button. This mention will disappear in a future version.

    In ExampleSower, the object are regenerated from the Sow method, which is called in any update if the flag "toUpdate" is true. This flag is indeed set to true from the OnValidate method, which will cause the regeneration to occur when entering play mode from the editor, but will not cause regeneration in a standalone application launch. This is only a proposition of the life cycle of the generated object and you can definitely write your own script to get a different life cycle. All script named with "Example..." in SplineMesh, are only examples ^^

    About the prefab, here is the explanation. SplineMesh examples usually generate and regenerate objects to update according to change in the spline, so the generated content follows the spline at anytime and provide a real time deformation. The generation of object is either brute force (destroy all, rebuild all) or pooled (reuse object as much as possible), depending on examples, but in both cases, the regeneration may need to get rid of useless objects, for example when you shrink the spline of a node.

    Issue is, since Unity 2018 and prefab rework, it is not possible to change the hierarchy of a prefab outside of the prefab editor. If you generate objects in the prefab editor, you won't be able to destroy these objects during the regeneration of the content in the scene. The solution is simple: don't save generated content in the prefab. You can either manually remove the generated content before you save the prefab, or use a condition in the generation of the content, that will avoid generating it from the prefab editor like this:

    Code (CSharp):
    1. public void Generate() {
    2. #if UNITY_EDITOR
    3.     // we don't generate if we are in prefab mode
    4.     if (PrefabStageUtility.GetCurrentPrefabStage() != null) return;
    5. #endif
    6.     // generate content...
    7. }
    8.  
    Have fun bending things!
     
    laurentlavigne likes this.
  37. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,473
    Thanks for your explanation,
    like that?
    in RopeBuilder
    upload_2021-3-31_18-29-39.png
     
    methusalah999 likes this.
  38. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    the segments of the solid generated initially in the scene have this component "Combined Mesh (root scene)", and they are capable of colliding with a particle system that i'm using that simulates liquid falling down. The segments generated via my script have this component: "Generated by Mesh Bender". the liquid by pass these segments

    that's the only difference between them, components-wise. and i need all of them to behave like the first type
     
  39. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    Some meshes must be static and have their mesh collider correct, while the other don't.

    The latest version include a fix about that, you should start by updating. Then, if you use mesh colliders, note that you should not change the spline because mesh collider won't follow. You should uncheck update in play mode to avoid that.
     
  40. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    what i want is this: i have this small disc (two nodes that have the same direction (0,0,1) and positions like (0,0,0) and (0,0,1). i want new nodes to be created via script. i already achieved this, but the new segments don't have colliders of their own. i tried doing what you just said but it didn't help =(

    i'm on version 1.3.3
     
  41. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    I'm not sure because I don't have the code right now but, as I said, mesh colliders are not updated in play mode. If you add nodes by script in play mode, you will have to add the mesh collider component yourself on the created object, which is as simple as a call to AddComponent<MeshCollider>();

    I will check the source code later to confirm that, in the meanwhile, you can go check it yourself in MeshBender class.
     
  42. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    i'm using this, at this point:

    upload_2021-4-5_17-27-17.png

    i tried adding spline.AddComponent<MeshCollider> but unity says this object doesn't have this method
     
  43. ITerenzi

    ITerenzi

    Joined:
    Dec 20, 2013
    Posts:
    6
    Hey methusalah999, thank you for the tool! I was wondering if it's possible to have a closed-loop that generates mesh to fill the inside, as a way to create terrains or bodies of water (similar to the new water system of unreal). Apologies if it's a dumb question, I'm not on the technical side
     
  44. yodavid

    yodavid

    Joined:
    Dec 12, 2016
    Posts:
    28
    methusalah999, i really need your help with some stuff. is there any other way i can contact you? i can pay for your help if you think that's necessary, but i really need this
     
  45. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    SplineMesh does not have this feature. You could easily find points along the spline to get the surrounding of the shape, but you would still need a triangulation algorithm to create the mesh.
     
  46. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    Hello yodavid.

    If you are stuck with "i tried adding spline.AddComponent<MeshCollider> but unity says this object doesn't have this method" then I think the issue is not related to SplineMesh at all and that you might need to run into some tutorials first, to get a better understanding of how Unity works.

    Unfortunatly, I don't have the time to provide support outside of SplineMesh's scope, but there are plenty of ressources you can use to get help, like this forum or the official Unity Discord server.

    Please do come back if you have any SplineMesh related issue!
     
  47. fahruz

    fahruz

    Joined:
    Mar 5, 2020
    Posts:
    57
    Hi again

    I'm curious to know when extruding per script, when that extrusion takes place. Evidently not directly after adding and parametrizing Spline and SplineExtrusion objects.

    I want to change the mesh colliders to be convex so after defining Spline and SplineExtrusion with its segments I'm trying to do this:

    Code (CSharp):
    1. foreach (var collider in newVolume.GetComponentsInChildren<MeshCollider>())
    2. {
    3.    collider.convex = true;
    4. }
    But the "generated by SplineExtrusion" child object seems to have zero segments at this point in the script so the above function doesn't find anything to change. Just after that I also want to add a component, which relies on the geometry of the extruded object, but since it's not built yet that component doesn't work.

    When I run the code the spline is generated at some time and I can see the segments, so I wonder when that happens.

    -edit-

    Ok, so looking at the code of SplineExtrusion, I see it's calling a private GenerateMesh() method from its update(). I'm already in my update() method when defining the extrusion so it'd be nice if I could execute it on demand. Is there any way I can force the mesh generation to happen? I verified that making GenerateMesh() public and calling it from my script worked, but of course, it'd be preferable to achieve that without editing the plugin's code.
     
    Last edited: Apr 15, 2021
  48. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    The generated content is created or updated in the update method of the SplineExtrusion so it may be only generated in the next frame if your script's update is executed before this one. You can try and use LateUpdate, or wait for the next frame, or start a one frame delay coroutine, for example.
     
  49. fahruz

    fahruz

    Joined:
    Mar 5, 2020
    Posts:
    57
    Thanks for the reply. Yes, I know I can use delays, I was hoping for a more direct way to keep the code clear and straightforward.
     
  50. methusalah999

    methusalah999

    Joined:
    May 22, 2017
    Posts:
    648
    You can also change the order of execution of scripts in project settings, but I personally feel like this way is even less elegant.

    I've seen your edit about calling the method manually. It's seems ok to me but you should ensure two things:
    - the update should not occur more than once in a frame for performance purposes (you could use a test by keeping the last Time.frame to be sure),
    - if something else would trigger an update, you should still allow it and manage it yourself.