Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

[SOLVED] mesh extrusion error - procedural tube / tunnel

Discussion in 'Scripting' started by HelenOnGames, Nov 10, 2015.

  1. HelenOnGames

    HelenOnGames

    Joined:
    Nov 10, 2015
    Posts:
    6
    Hello everybody!
    I pretty much started playing around with the Unity engine and tried to make some progress.
    For now I'm trying to create a simple flight through a tunnel or a tube if you want to call it so.
    I tried to create a procedural tunnel out of a simple mesh that I made in unity Blender. It's just a short tube with no caps.

    So I tried to understand the basic codes and wrote some on my own. But when I hit the play Button It struggles over an "array index out of range" error. The exact message is
    I hope that someone can help me to fix this so I can get better in what I'm doing here.

    Thank you in forward and best regards!

    Helen

    Here is the Code (the error is almost at the end)

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class createTunnel : MonoBehaviour {
    5.  
    6.     public float Time_ = 2.0f;
    7.     public float minDistance = 0.5f;
    8.  
    9.     private Mesh tube;
    10.     private MeshExtrusion.Edge[] precomputedEdges;
    11.  
    12.     private ArrayList sections;
    13.     private Vector3 position;
    14.     private float now;
    15.     private Vector3 point;
    16.     private Matrix4x4 matrix;
    17.     private Matrix4x4 worldToLocal;
    18.     private Matrix4x4[] finalSections;
    19.     private Quaternion prevRotation;
    20.     private createTunnel currSection;
    21.  
    22.     // Use this for initialization
    23.     void Start () {
    24.         tube = GetComponent<MeshFilter>().sharedMesh;
    25.         precomputedEdges = MeshExtrusion.BuildManifoldEdges(tube);
    26.         sections = new ArrayList();
    27.     }
    28.  
    29.     // Update is called once per frame
    30.     void Update () {
    31.  
    32.     }
    33.  
    34.     void LateUpdate()
    35.     {
    36.         position = transform.position;
    37.         now = Time.time;
    38.         // first item in list
    39.         createTunnel firstSection = (sections.Count > 0) ? (createTunnel) sections[0] : null;
    40.         // last item in list
    41.         if(sections.Count > 0)
    42.         {
    43.             currSection = (createTunnel)sections[sections.Count - 1];
    44.         }
    45.  
    46.         while (sections.Count > 0 && now > currSection.Time_ + Time_)
    47.         {
    48.             sections.RemoveAt(sections.Count - 1);
    49.         }
    50.  
    51.         if(sections.Count == 0 || (firstSection.point - position).sqrMagnitude > minDistance * minDistance)
    52.         {
    53.             createTunnel section = gameObject.AddComponent<createTunnel>();
    54.             section.point = position;
    55.             section.matrix = transform.localToWorldMatrix;
    56.             section.Time_ = now;
    57.             sections.Insert(0, section);
    58.         }
    59.    
    60.         if(sections.Count > 2)
    61.         {
    62.             return;
    63.         }
    64.  
    65.         worldToLocal = transform.worldToLocalMatrix;
    66.         finalSections = new Matrix4x4[sections.Count];
    67.    
    68.         for(int i = 0; i < sections.Count; i++)
    69.         {
    70.             if(i == 0)
    71.             {
    72.                 finalSections[i] = Matrix4x4.identity;
    73.             }
    74.             else
    75.             {
    76.                 currSection = (createTunnel)sections[i];
    77.                 finalSections[i] = worldToLocal * currSection.matrix;
    78.             }
    79.         }
    80.  
    81.         MeshExtrusion.ExtrudeMesh(tube, (gameObject.GetComponent<MeshFilter>() as MeshFilter).mesh, finalSections, precomputedEdges, false);
    82.     }
    83. }
    84.  
     
    Last edited: Nov 10, 2015
  2. Wowo51

    Wowo51

    Joined:
    Oct 12, 2015
    Posts:
    25
    I solved this in a much different way in my 3D Tube Racer game. I've got the code debugged but I'm not sure I want to give it away. I might trade it for code or something. It'll output segments of tube along spline curves, you can just enter the splines in Hermite form with a radius.

    You can see the end result here: TechnologicalUtopia.com/tuberacer.htm

    or here's a screenshot:
     
  3. HelenOnGames

    HelenOnGames

    Joined:
    Nov 10, 2015
    Posts:
    6
    Hey Wowo51!

    Thank you for the offer but since I'm pretty new to this I would like to figure it out by myself but I don't know how I messed it up. It wouldn't help me if I would just get your Code. On the other hand I don't know what I could offer for a trade.
    Plus my first goal is a straight tube and not a curvy one.

    But still thank you very much! I appreciate any help :)

    Helen
     
  4. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
  5. HelenOnGames

    HelenOnGames

    Joined:
    Nov 10, 2015
    Posts:
    6
    Hey ThemalFusion!

    Yeah that's the class I'm using. Should I use another class or what would you suggest?
    I spend a lot of time searching Google and Youtube for some simple tutorials but couldn't even find a single one :(
    So I had to try it completely by myself. Also I had some errors in the code like it never left the while-loop.
    I noticed that MeshExtrusion.BuildManifoldEdges gave me 256 edges from my cylinder made out of a circle with 64 sides. So the maximum of vertices should be 128 and not twice that much. I guess I have to try the suggested single cap in the next step.

    Also I tried to clean up and fix the code so below is what I've done so far. The code runs but nothing happens on screen plus I wonder why "if(sections.Count > 2)" never happens so I just commented it out but I only get 1 Index in List<Section>. Still don't know why.

    Thank you for your help!

    Helen

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class createTunnel : MonoBehaviour {
    6.  
    7.     public float Time_ = 0.5f;
    8.     public float minDistance = 0.5f;
    9.  
    10.     private Mesh tube;
    11.     private MeshExtrusion.Edge[] precomputedEdges;
    12.  
    13.     private Vector3 position;
    14.     private Matrix4x4 worldToLocal;
    15.     private Matrix4x4[] finalSections;
    16.     private Quaternion prevRotation;
    17.  
    18.     private float now;
    19.     private List<Section> sections;
    20.  
    21.     private class Section
    22.     {
    23.         public float time;
    24.         public Matrix4x4 matrix;
    25.         public Vector3 point;
    26.     }
    27.  
    28.     // Use this for initialization
    29.     void Start () {
    30.         tube = GetComponent<MeshFilter>().sharedMesh;
    31.         precomputedEdges = MeshExtrusion.BuildManifoldEdges(tube);
    32.         sections = new List<Section>();
    33.     }
    34.  
    35.     // Update is called once per frame
    36.     void Update () {
    37.  
    38.     }
    39.  
    40.     void LateUpdate()
    41.     {
    42.         position = transform.position;
    43.         now = Time.time;
    44.  
    45.         while (sections.Count > 0 && now > sections[sections.Count - 1].time + Time_)
    46.         {
    47.             sections.RemoveAt(sections.Count - 1);
    48.         }
    49.  
    50.         if(sections.Count == 0 || (sections[0].point - position).sqrMagnitude > minDistance * minDistance)
    51.         {
    52.             Section section = new Section();
    53.             section.point = position;
    54.             section.matrix = transform.localToWorldMatrix;
    55.             section.time = now;
    56.             sections.Add(section);
    57.         }
    58.      
    59.         /*if(sections.Count > 2)
    60.         {
    61.             return;
    62.         } */
    63.         worldToLocal = transform.worldToLocalMatrix;
    64.         finalSections = new Matrix4x4[sections.Count];
    65.  
    66.         for(int i = 0; i < sections.Count; i++)
    67.         {
    68.             if(i == 0)
    69.             {
    70.                 finalSections[i] = Matrix4x4.identity;
    71.             }
    72.             else
    73.             {
    74.                 finalSections[i] = worldToLocal * sections[i].matrix;
    75.             }
    76.         }
    77.      
    78.         MeshExtrusion.ExtrudeMesh(tube, (gameObject.GetComponent<MeshFilter>() as MeshFilter).mesh, finalSections, precomputedEdges, false);
    79.      
    80.     }
    81. }
     
    Last edited: Nov 10, 2015
  6. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    So, I gave the thing a whirl. I used the default quad mesh on the mesh filter.
    I suppose you move the tunnel object somehow, I just dragged the object around in the editor during playmode.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class Meshex : MonoBehaviour
    7. {
    8.  
    9.     public float Time_ = 0.5f;
    10.     public float minDistance = 0.5f;
    11.  
    12.     private Mesh tube;
    13.     private MeshExtrusion.Edge[] precomputedEdges;
    14.  
    15.     private Vector3 position;
    16.     private Matrix4x4 worldToLocal;
    17.     private Matrix4x4[] finalSections;
    18.     private Quaternion prevRotation;
    19.  
    20.     private float now;
    21.     private List<Section> sections;
    22.  
    23.     private Mesh currentMesh;
    24.  
    25.     private class Section
    26.     {
    27.         public float time;
    28.         public Matrix4x4 matrix;
    29.         public Vector3 point;
    30.     }
    31.  
    32.     // Use this for initialization
    33.     void Start () {
    34.         tube = Instantiate(GetComponent<MeshFilter>().sharedMesh) as Mesh; // Instantiate so we can modify
    35.         currentMesh = GetComponent<MeshFilter>().mesh;    // Cache mesh instance.
    36.         precomputedEdges = MeshExtrusion.BuildManifoldEdges(tube);
    37.  
    38.         tube.triangles = new int[0]; // Clear triangles to get rid of profile.
    39.         sections = new List<Section>();
    40.     }
    41.  
    42.     // Update is called once per frame
    43.     void Update () {
    44.  
    45.     }
    46.  
    47.     void LateUpdate()
    48.     {
    49.         position = transform.position;
    50.         now = Time.time;
    51.  
    52.         while (sections.Count > 2 && now > sections[0].time + Time_)
    53.         {
    54.             sections.RemoveAt(0);
    55.         }
    56.  
    57.         if(sections.Count == 0 || (sections[sections.Count - 1].point - position).sqrMagnitude > minDistance * minDistance)
    58.         {
    59.             Section section = new Section();
    60.             section.point = position;
    61.             section.matrix = transform.localToWorldMatrix;
    62.             section.time = now;
    63.             sections.Add(section);
    64.         }
    65.      
    66.         if(sections.Count < 1)
    67.         {
    68.             return;
    69.         }
    70.         worldToLocal = transform.worldToLocalMatrix;
    71.         finalSections = new Matrix4x4[sections.Count];
    72.  
    73.         int end = sections.Count - 1;
    74.         for(int i = end; i >= 0; i--)
    75.         {
    76.             if(i == end)
    77.             {
    78.                 finalSections[i] = Matrix4x4.identity;
    79.             }
    80.             else
    81.             {
    82.                 finalSections[i] = worldToLocal * sections[i].matrix;
    83.             }
    84.         }
    85.      
    86.         MeshExtrusion.ExtrudeMesh(tube, currentMesh, finalSections, precomputedEdges, false);
    87.      
    88.     }
    89. }
    90.  
    meshex.jpg
     
  7. HelenOnGames

    HelenOnGames

    Joined:
    Nov 10, 2015
    Posts:
    6
    Hey ThermalFusion!

    Thank you for your help! So do I have to create that single cap you mentioned before? Does the size matter?
    And would you please be so nice to tell me what failures I made/why you changed what you changed?
    Looks like I wasn't that far away from the correct code. :)

    And yes, I want it to move endless towards the camera so it looks like an endless Tunnel.
    Later I want to add Textures to achieve something that gives me at least a feeling of the speed.
    I also want to play around with shaders to see what I can get out of them.

    Again thank you very much for your help!

    Edit: I tried using your script with a Circle and a Nurbs Circle instead of the tube but nothing happens on screen. I also tried only a single cap of a cylinder and still nothing happens. What am I doing wrong? :(

    Helen
     
    Last edited: Nov 10, 2015
  8. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    It doesnt really matter what the source of the model is, Meshes in unity are almost exclusively points and triangles. If your nurbs imports into this and you get something you can view, its fine. If not, you need to get back to exporting the proper geometry unity can interpret.
    The way the script is setup with the range test the object has to move during runtime, are you doing this? Else nothing happens.
    I didnt really do much other than reversing the order some segment calculations were performed because there was a long extrusion from what I thought of the last segment to the shape.Removing the profile triangles just got rid of the cap otherwise produced.
     
  9. HelenOnGames

    HelenOnGames

    Joined:
    Nov 10, 2015
    Posts:
    6
    Hey ThermalFusion!

    Ok, I didn't moved the object :D
    So if I want to make it like an endless flight now I guess I have to move it towards the cameras direction and flip it back to a position before the end reaches the camera. Not sure if that's the best or even the right way to do so but I'll give it a shot.

    So thank you once again you helped me alot and keep on doing amazing 3D works! Best regards!

    Helen
     
    ThermalFusion likes this.
  10. DougMcFarlane

    DougMcFarlane

    Joined:
    Apr 25, 2009
    Posts:
    197
    Catlike Coding has an excellent tutorial on an endless pipe design:
    http://catlikecoding.com/unity/tutorials/swirly-pipe/

    He also has tons of other great tutorials, guiding you step by step through various topics.

    For an endless flight, you could keep your camera positioned at (0,0,0), and only rotate it based on the 'movement' inputs, and move all pipes towards the camera to simulate the movement. Just keep adding pipes to the end whenever pipes moves completely past you. The math may be a bit tricky / reversed (not sure).

    Or do as you are, and once your player has only has a certain distance of tunnel ahead, generate a new section, and remove the oldest section. (Instead of inserting and deleting, of course you should move the one from behind to now be the one further ahead, to pool the sections and avoid performance issues.)

    I tackled a 3d tunnel 6 months or so ago, and gave up. I want to continue when I get more time as I love the concept.
     
  11. ThermalFusion

    ThermalFusion

    Joined:
    May 1, 2011
    Posts:
    906
    That is indeed an excellent tutorial.
     
  12. HelenOnGames

    HelenOnGames

    Joined:
    Nov 10, 2015
    Posts:
    6
    Hey Doug.McFarlane!

    Thank you for the post but this tutorial is not what I was looking for.
    # I need a straight tube but one can't achieve that with this tutorial
    # Even though he added a fix for "unstable pipes" you'll get small gaps after a very short time
    # I couldn't find a way to add a single seamless texture instead of a texture on each quad (didn't tried much)
    # I want to "fly" through the pipe but the game rotates the tube around when the player is moving (almost solved that but didn't worked as good as I want it to)

    But I still thank you for your effort!
    Best regards!

    Helen