Search Unity

B-Spline Surface

Discussion in 'Scripting' started by ethermammoth, Nov 28, 2010.

  1. ethermammoth

    ethermammoth

    Joined:
    Nov 28, 2010
    Posts:
    12
    I've portet a C implementation for (uniform) B-spline surfaces into unity(c#), since i didnt find a ready to go C# version.
    (it is not optimized for runtime manipulation yet (deBor etc.))




    Here it is:

    BSplineSurface.cs
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class BSplineSurface {
    7.    
    8.     /*
    9.      * THESE VALUES MUST BE SET BEFORE CALCULATING
    10.      */
    11.     //size of the control net (e.g. 3x4 net)
    12.     public int NI = 4; //setting these to high can crash unity
    13.     public int NJ = 4;
    14.     //Grid of control points
    15.     public Vector3[,] controlGrid;
    16.     //The degree in each direction
    17.     public int TI = 3;
    18.     public int TJ = 3;
    19.     //output GRID resolution (e.g. 30x40)
    20.     public int RESOLUTIONI = 150; //setting these to high can crash unity
    21.     public int RESOLUTIONJ = 150;
    22.     //the output Grid
    23.     public Vector3[,] outputGrid;
    24.    
    25.     /*
    26.      * INTERNAL VALUES
    27.      */
    28.     //internal knots in each direction
    29.     private int[] knotsI;
    30.     private int[] knotsJ;
    31.     //internal variables
    32.     private int i, j, ki, kj;
    33.     private float intervalI, incrementI, intervalJ, incrementJ, bi, bj;
    34.    
    35.     //FUNCTIONS
    36.     private BSplineMath bmath = new BSplineMath();
    37.    
    38.     //constructor
    39.     public BSplineSurface()
    40.     {
    41.         Init();
    42.     }
    43.    
    44.    
    45.     //MUST BE CALLED FIRST
    46.     public void Init () {
    47.        
    48.         controlGrid = new Vector3[NI+1,NJ+1];
    49.         outputGrid = new Vector3[RESOLUTIONI, RESOLUTIONJ];
    50.        
    51.         //init step size (the increment steps)
    52.         incrementI = (NI-TI+2) / ((float)RESOLUTIONI - 1);
    53.         incrementJ = (NJ-TJ+2) / ((float)RESOLUTIONJ - 1);
    54.        
    55.         //Calculate KNOTS
    56.         knotsI = bmath.SplineKnots(NI, TI);
    57.         knotsJ = bmath.SplineKnots(NJ, TJ);
    58.        
    59.     }
    60.    
    61.     // Use this for random initialization
    62.     public void InitRandomGrid () {
    63.        
    64.         //init control points (random z)
    65.         for(i=0; i<=NI; i++)
    66.         {
    67.             for(j=0; j<=NJ; j++)
    68.             {
    69.                 controlGrid[i, j].x = i;
    70.                 controlGrid[i, j].y = j;
    71.                 controlGrid[i, j].z = Random.value;
    72.             }
    73.         }  
    74.     }
    75.    
    76.     // Use this for grid input (remember to set NI and NJ before(the grid size) and call Init)
    77.     public void InitGrid (Vector3[,] inputGrid) {
    78.        
    79.         //init control points (random z)
    80.         for(i=0; i<=NI; i++)
    81.         {
    82.             for(j=0; j<=NJ; j++)
    83.             {
    84.                 controlGrid[i, j] = inputGrid[i, j];
    85.             }
    86.         }  
    87.     }
    88.    
    89.    
    90.     public void Calculate()
    91.     {
    92.         //MAIN CALCULATIONS
    93.         intervalI = 0;
    94.         for(i = 0; i < RESOLUTIONI-1; i++)
    95.         {
    96.             intervalJ = 0;
    97.             for(j = 0; j < RESOLUTIONJ-1; j++)
    98.             {
    99.                 outputGrid[i, j] = Vector3.zero;
    100.                 for(ki = 0; ki <= NI; ki++)
    101.                 {
    102.                     for(kj = 0; kj <= NJ; kj++)
    103.                     {
    104.                         bi = bmath.SplineBlend(ki, TI, knotsI, intervalI);
    105.                         bj = bmath.SplineBlend(kj, TJ, knotsJ, intervalJ);
    106.                         outputGrid[i, j].x += (controlGrid[ki, kj].x * bi * bj);
    107.                         outputGrid[i, j].y += (controlGrid[ki, kj].y * bi * bj);
    108.                         outputGrid[i, j].z += (controlGrid[ki, kj].z * bi * bj);
    109.                     }
    110.                 }
    111.                 intervalJ += incrementJ;
    112.             }          
    113.             intervalI += incrementI;
    114.         }
    115.        
    116.         intervalI = 0;
    117.         for(i = 0; i < RESOLUTIONI-1; i++)
    118.         {
    119.             outputGrid[i, RESOLUTIONJ-1] = Vector3.zero;
    120.             for(ki = 0; ki <= NI; ki++)
    121.             {
    122.                 bi = bmath.SplineBlend(ki, TI, knotsI, intervalI);
    123.                 outputGrid[i, RESOLUTIONJ-1].x += (controlGrid[ki, NJ].x * bi);
    124.                 outputGrid[i, RESOLUTIONJ-1].y += (controlGrid[ki, NJ].y * bi);
    125.                 outputGrid[i, RESOLUTIONJ-1].z += (controlGrid[ki, NJ].z * bi);
    126.             }
    127.             intervalI += incrementI;
    128.         }
    129.         outputGrid[i, RESOLUTIONJ-1] = controlGrid[NI, NJ];
    130.        
    131.         intervalJ = 0;
    132.         for(j = 0; j < RESOLUTIONJ-1; j++)
    133.         {
    134.             outputGrid[RESOLUTIONI-1, j] = Vector3.zero;
    135.             for(kj = 0; kj <= NJ; kj++)
    136.             {
    137.                 bj = bmath.SplineBlend(kj, TJ, knotsJ, intervalJ);
    138.                 outputGrid[RESOLUTIONI-1, j].x += (controlGrid[NI, kj].x * bj);
    139.                 outputGrid[RESOLUTIONI-1, j].y += (controlGrid[NI, kj].y * bj);
    140.                 outputGrid[RESOLUTIONI-1, j].z += (controlGrid[NI, kj].z * bj);
    141.             }
    142.             intervalJ += incrementJ;
    143.         }
    144.         outputGrid[RESOLUTIONI-1, j] = controlGrid[NI, NJ];
    145.        
    146.     }
    147. }
    148.  
    BSplineMath.cs
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class BSplineMath {
    6.  
    7.     //constructor
    8.     public BSplineMath()
    9.     {
    10.        
    11.     }
    12.    
    13.    
    14.     public int[] SplineKnots(int n, int t)
    15.     {
    16.         int[] u = new int[n+t+1];
    17.        
    18.         for(int j=0; j<n+t; j++)
    19.         {
    20.             if(j < t)
    21.                 u[j] = 0;
    22.             else if(j <= n)
    23.                 u[j] = j - t + 1;
    24.             else if(j > n)
    25.                 u[j] = n - t + 2;
    26.         }
    27.        
    28.         return u;
    29.     }  
    30.    
    31.     public float SplineBlend(int k, int t, int[] u, float v)
    32.     {
    33.         float rvalue = 0;
    34.        
    35.         if(t == 1)
    36.         {
    37.             if((u[k] <= v)  (v < u[k+1]))
    38.                 rvalue = 1;
    39.             else
    40.                 rvalue = 0;
    41.         }else{
    42.            
    43.             if ((u[k+t-1] == u[k])  (u[k+t] == u[k+1]))
    44.               rvalue = 0;
    45.             else if (u[k+t-1] == u[k])
    46.               rvalue = (u[k+t] - v) / (u[k+t] - u[k+1]) * SplineBlend(k+1,t-1,u,v);
    47.             else if (u[k+t] == u[k+1])
    48.               rvalue = (v - u[k]) / (u[k+t-1] - u[k]) * SplineBlend(k,t-1,u,v);
    49.             else
    50.               rvalue = (v - u[k]) / (u[k+t-1] - u[k]) * SplineBlend(k,t-1,u,v) + (u[k+t] - v) / (u[k+t] - u[k+1]) * SplineBlend(k+1,t-1,u,v);
    51.        
    52.         }
    53.         return rvalue;
    54.     }
    55. }
    56.  

    USAGE EXAMPLE

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public class testSplines : MonoBehaviour {
    8.  
    9.     BSplineSurface mySurface;
    10.     public Vector3[,] resultGrid;
    11.     public Vector3[,] controlGrid;
    12.    
    13.     // Use this for initialization
    14.     void Start () {
    15.         mySurface = new BSplineSurface();
    16.         mySurface.InitRandomGrid();
    17.         mySurface.Calculate();
    18.         resultGrid = mySurface.outputGrid;
    19.         controlGrid = mySurface.controlGrid;
    20.         generateMesh();
    21.         updateMesh();
    22.     }
    23.    
    24.     // Update is called once per frame
    25.     void Update () {
    26.         int j = 0;
    27.         //CONTROL DRAWS
    28.         for(int i=0; i<mySurface.NI; i++)
    29.         {
    30.             for(j=0; j<mySurface.NJ; j++)
    31.             {
    32.                 Debug.DrawLine(controlGrid[i, j], controlGrid[i+1, j], Color.red);
    33.                 Debug.DrawLine(controlGrid[i, j], controlGrid[i, j+1], Color.red); 
    34.             }
    35.             Debug.DrawLine(controlGrid[i, j], controlGrid[i+1, j], Color.red);
    36.         }
    37.         for(int i=0; i<mySurface.NJ; i++)
    38.         {
    39.             Debug.DrawLine(controlGrid[mySurface.NI, i], controlGrid[mySurface.NI, i+1], Color.red);   
    40.         }
    41.     }
    42.    
    43.     //generate a Mesh
    44.     private void generateMesh()
    45.     {
    46.        
    47.         Mesh mesh = GetComponent<MeshFilter>().mesh;
    48.        
    49.         int width = mySurface.RESOLUTIONI;
    50.         int height = mySurface.RESOLUTIONJ;
    51.         int y = 0;
    52.         int x = 0;
    53.    
    54.         // Build vertices and UVs
    55.         Vector3[] vertices = new Vector3[height * width];
    56.         Vector2[] uv = new Vector2[height * width];
    57.        
    58.         for (y=0;y<height;y++)
    59.         {
    60.             for (x=0;x<width;x++)
    61.             {
    62.                 vertices[y*width + x] = new Vector3 (x, 0, y);
    63.                 uv[y*width + x] = new Vector2(x, y);
    64.             }
    65.         }
    66.        
    67.         // Assign them to the mesh
    68.         mesh.vertices = vertices;
    69.         mesh.uv = uv;
    70.    
    71.         // Build triangle indices: 3 indices into vertex array for each triangle
    72.         int[] triangles = new int[(height - 1) * (width - 1) * 6];
    73.         int index = 0;
    74.         for (y=0;y<height-1;y++)
    75.         {
    76.             for (x=0;x<width-1;x++)
    77.             {
    78.                 // For each grid cell output two triangles
    79.                 triangles[index++] = (y     * width) + x;
    80.                 triangles[index++] = ((y+1) * width) + x;
    81.                 triangles[index++] = (y     * width) + x + 1;
    82.    
    83.                 triangles[index++] = ((y+1) * width) + x;
    84.                 triangles[index++] = ((y+1) * width) + x + 1;
    85.                 triangles[index++] = (y     * width) + x + 1;
    86.             }
    87.         }
    88.         // And assign them to the mesh
    89.         mesh.triangles = triangles;
    90.            
    91.         // Auto-calculate vertex normals from the mesh
    92.         mesh.RecalculateNormals(); 
    93.     }
    94.    
    95.     private void updateMesh()
    96.     {
    97.         Mesh mesh = GetComponent<MeshFilter>().mesh;
    98.        
    99.         int width = mySurface.RESOLUTIONI;
    100.         int height = mySurface.RESOLUTIONJ;
    101.         int y = 0;
    102.         int x = 0;
    103.        
    104.         // Build vertices and UVs
    105.         Vector3[] vertices = new Vector3[height * width];
    106.        
    107.        
    108.         for (y=0;y<height;y++)
    109.         {
    110.             for (x=0;x<width;x++)
    111.             {
    112.                 vertices[y*width + x] = resultGrid[x, y];
    113.             }
    114.         }
    115.         mesh.vertices = vertices;
    116.         mesh.RecalculateNormals();
    117.         MeshCollider mcol = GetComponent<MeshCollider>();
    118.         mcol.sharedMesh = null;
    119.         mcol.sharedMesh = mesh;
    120.        
    121.     }
    122. }
    123.  
     
    Argos and Eric2241 like this.
  2. pakfront

    pakfront

    Joined:
    Oct 6, 2010
    Posts:
    551
    Wow, that's quite cool. Thanks!
     
  3. monark

    monark

    Joined:
    May 2, 2008
    Posts:
    1,598
    nice one
     
  4. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    thanks!
     
  5. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    Thank you very much for sharing this.
     
  6. Mike4

    Mike4

    Joined:
    Oct 24, 2013
    Posts:
    92
    I get:
    The class named 'BSplineSurface' is not derived from MonoBehaviour or ScriptableObject!

    Thanks for help
     
  7. Cat_cop

    Cat_cop

    Joined:
    Aug 21, 2015
    Posts:
    5
    Can you please add download?
     
  8. ahancock1

    ahancock1

    Joined:
    Dec 16, 2014
    Posts:
    1
    chadiik likes this.
  9. imissuwhy

    imissuwhy

    Joined:
    Aug 31, 2015
    Posts:
    3
    Thanks you a lot.
     
  10. jackdos

    jackdos

    Joined:
    Sep 8, 2019
    Posts:
    1
    The problem is the script "BSplineMath.cs",
    I've fix it like this :
    line 37: if((u[k] <= v) && (v < u[k+1]))
    line 43: if ((u[k+t-1] == u[k]) && (u[k+t] == u[k+1]))