Search Unity

TerrainStitcher v1.0 - free for non-commercial use

Discussion in 'Assets and Asset Store' started by reijerh, Aug 17, 2011.

  1. reijerh

    reijerh

    Joined:
    Aug 15, 2011
    Posts:
    5
    One of the most asked questions concerning terrain in Unity is how to stitch together multiple terrains in order to create a large and seamless world. I was surprised (and a bit annoyed) that such an elementary feature isn't present in the Unity editor or API, so I decided to implement it myself and share it with the community. I intend to use it myself to stitch together lots of procedurally generated terrains to create one massive world.



    I present to you TerrainStitcher, it is a Unity editor and API tool that stitches terrains together by adjusting their heightmaps along the borders that "connect" the terrains. This creates a seamless transition from one terrain to the other and makes manually adjusting terrain borders unnecessary. The most important feature is automatic stitching, which means you simply layout the terrains in the editor in the way you would like them to be stitched together (exact positioning isn't necessary), select the terrains, click 'Stitch Selected' and the tool will figure out the rest!

    Automatic stitching


    Terrains must have the same dimensions (x, y, z) and resolution for stitching to work! Undo is supported.

    It is free for private, non-commercial use (donations are appreciated, if you found it particularly useful/timesaving)! For business or commercial use, a license must be obtained by paying a license fee of €10 per user (you are welcome to try it out for free as long as you buy a license when you're actually using it for a commercial product).

    Payments can be made through paypal (reijerh AT gmail.com) or bank transfer (please contact me at reijerh AT gmail.com), or by simply following this link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=SJJW6K9UPF4ZU&lc=NL&item_name=Terrain%20Stitcher&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted. Licenses remain valid for all future updates and bugfixes. Please mention your (company) name when making the payment.

    IMO it'd be much less of a hassle for Unity users if this feature was included in the engine (you are welcome to use my code in exchange for a pro license :cool:).
     

    Attached Files:

    Last edited: Aug 21, 2011
  2. pixelsteam

    pixelsteam

    Joined:
    May 1, 2009
    Posts:
    924
    Is there a way to cut out areas of unused terrain?
     
  3. reijerh

    reijerh

    Joined:
    Aug 15, 2011
    Posts:
    5
    I'm not sure what you mean, could you explain what you mean by 'unused terrain'?
     
  4. pixelsteam

    pixelsteam

    Joined:
    May 1, 2009
    Posts:
    924
    I have a very big terrain, but I am only using like 1/5th of it. There is lots of unused space. It would be great to have a tool that allows you to cut out the area's that are not relavent. Possibly there is a way. But it is unknown to me.

    Also have you found a clever and easy way to bring in GIS or real topo data easily, without some crazy workflow.

    Thanks.
     
  5. reijerh

    reijerh

    Joined:
    Aug 15, 2011
    Posts:
    5
    I'm sorry, this tool is purely for stitching terrains :). I haven't worked with real topo data myself so I can't really comment on that, you should probably convert it to heightmap format and import that heightmap into Unity. I'd be surprised if there are no people with experience in this field on these forums though (or answers.unity3d.com), so maybe try creating a topic/question :).
     
  6. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    Sounds like you're looking for a terrain paging or culling system. There have been a couple of scripts posted in the forums and there's also this which is still being developed: http://forum.unity3d.com/threads/96190-Terrain-Manager
     
  7. pixelsteam

    pixelsteam

    Joined:
    May 1, 2009
    Posts:
    924
    Thank you very much for your help.
     
  8. sjm-tech

    sjm-tech

    Joined:
    Sep 23, 2010
    Posts:
    734
    It seem interesting! I would try it with real data when i come back from vacation.
    Now I installed the package and I think that the Auto stitching feature could be a great feature.
    For my terrain navigation project I used the nice Eric Stitchscape but i think that is a bit hard to set (each one manually) with many terrains (in my case 45 topographic tiles).
    Regards
    Max
     
  9. reijerh

    reijerh

    Joined:
    Aug 15, 2011
    Posts:
    5
    I've drastically improved the speed when stitching multiple terrains together, this will probably be very useful for you Sjm Tech! Stitching multiple terrains now takes about the same time as stitching one terrain (in the same order anyway).

    Also changed the menu item text from "Terrain Stitcher" to "Terrain Stitcher...".

    (Added version 1.1 to the first post in this thread)
     
  10. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    @reijerh - The link no longer works.
     
  11. reijerh

    reijerh

    Joined:
    Aug 15, 2011
    Posts:
    5
    If you mean the download link: I was probably still busy uploading the attachment or something, the file is hosted here as an attachment and it seems to work for me...

    edit: would it be possible for a mod to remove the v1.0 from the thread title? Thought I could edit the title when I created the thread.
     
  12. bigkahuna

    bigkahuna

    Joined:
    Apr 30, 2006
    Posts:
    5,434
    Ok, the link now works here as well...
     
  13. sjm-tech

    sjm-tech

    Joined:
    Sep 23, 2010
    Posts:
    734
    Great improvement!
    I think that you could add (for create more interest):
    - more detail regards the feature of your tool in the main description (like autostitching feature).
    - a screenshoot of the interface.
    - a video that shows a pratical example of utilization.

    As i said ...i reserve my working impressions when i'll make a real test on it.
    For now compliments
    Max
     
  14. ElectricCrow

    ElectricCrow

    Joined:
    Sep 9, 2012
    Posts:
    91
    Just sent you $26 donation for commercial use of your script.
    Thank you!.
     
    twobob likes this.
  15. Reijer

    Reijer

    Joined:
    Mar 22, 2013
    Posts:
    2
    Thanks! "Crafting focused sandbox mmo", sounds good :).
     
  16. nastasache

    nastasache

    Joined:
    Jan 2, 2012
    Posts:
    74
    Hi Reijer Eric,

    It's there a way to stitch terrains with different heights? I tried both Stitchscape and TerrainStitcher with no success, both scripts looks having as requirement the Y (elevation) value to be the same. It not work for heightmaps of real-world terrains.

    Thanks for any idea.
     
  17. Nition

    Nition

    Joined:
    Jul 4, 2012
    Posts:
    781
    Nastasache, do you mean how it sets the Y position of the terrain's transforms to be the same? I noticed that too, although it's not an issue for me. I'm sure the script could be modified to take the terrain's existing Y position into account rather than adjusting it.

    Reijerh, I just sent you €10 for a commercial license. Thanks for writing this so I didn't have to.
     
  18. Reijer

    Reijer

    Joined:
    Mar 22, 2013
    Posts:
    2
    Thanks Nition :)

    nastasache: I honestly can't remember how it works anymore, it's been a long time since I wrote the script and haven't really used Unity since, sorry.
     
  19. Jacksendary

    Jacksendary

    Joined:
    Jan 31, 2012
    Posts:
    408
    Very old post revoking, but is there any chance any one have ported this to C#? I've tried my self but it seems to make very small gaps there it is not stitched, my personal guess is that this is due to rounding errors. There is no real particular reason for the port other than I prefer C# over JS and find C# easier to read and work with. You may take the code and put into your package if you can make it work.

    Image over the problem (Happens to all terrain from what I've tried so far): https://dl.dropboxusercontent.com/u/13581369/UnityTerrain.png

    I included the source I've made so far here:

    Code (csharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public static class TerrainTools
    7. {
    8.     static double[] smoothModTable = null;
    9.     static double[] smoothDYTable = null;
    10.     static bool memoizationTablesFilled = false;
    11.  
    12.     static Vector3 prevSize = new Vector3(0, 0, 0);
    13.     static int prevHeight = 0;
    14.     static int prevWidth = 0;
    15.     static int prevNumSamples = 0;
    16.  
    17.     delegate float GetYMod(int domTerrain, int terrainToMod, double dY, int curCellX, int maxCellsX, int curCellY, int maxCellsY);
    18.  
    19.     public static void StitchTerrains(Terrain terrain1, Terrain terrain2, int numSamples, int domTerrain)
    20.     {
    21.         var dX = terrain2.transform.position.x - terrain1.transform.position.x;
    22.         var dZ = terrain2.transform.position.z - terrain1.transform.position.z;
    23.  
    24.         var height = terrain1.terrainData.heightmapHeight;
    25.         var width = terrain1.terrainData.heightmapWidth;
    26.  
    27.         if (height != terrain2.terrainData.heightmapHeight || width != terrain2.terrainData.heightmapWidth || terrain1.terrainData.size != terrain2.terrainData.size)
    28.         {
    29.             return;
    30.         }
    31.  
    32.         if (height != prevHeight || width != prevWidth || terrain1.terrainData.size != prevSize || numSamples != prevNumSamples)
    33.         {
    34.             prevHeight = height;
    35.             prevWidth = width;
    36.             prevSize = terrain1.terrainData.size;
    37.             prevNumSamples = numSamples;
    38.  
    39.             memoizationTablesFilled = false;
    40.             smoothModTable = new double[height];
    41.             smoothDYTable = new double[numSamples];
    42.         }
    43.      
    44.         GetYMod getYMod;
    45.         if (memoizationTablesFilled)
    46.         {
    47.             getYMod = getYModMemoized;
    48.         }
    49.         else
    50.         {
    51.             getYMod = getYModDynamic;
    52.         }
    53.  
    54.         var heights1 = terrain1.terrainData.GetHeights(0, 0, width, height);
    55.         var heights2 = terrain2.terrainData.GetHeights(0, 0, width, height);
    56.  
    57.         if (Mathf.Abs(dX) > Mathf.Abs(dZ))
    58.         {
    59.             var xDir = terrain2.transform.position.x > terrain1.transform.position.x ? 1 : -1;
    60.  
    61.             terrain2.transform.position = terrain1.transform.position;
    62.             terrain2.transform.position = new Vector3(terrain1.transform.position.x + terrain1.terrainData.size.x * xDir, terrain2.transform.position.y, terrain2.transform.position.z);
    63.  
    64.             for (int z = 0; z < height; z++)
    65.             {
    66.                 var dY = 0.0;
    67.                 if (xDir == 1)
    68.                 {
    69.                     dY = heights2[z, 0] - heights1[z, width - 1];
    70.                 }
    71.                 else
    72.                 {
    73.                     dY = heights2[z, width - 1] - heights1[z, 0];
    74.                 }
    75.  
    76.                 for (int i = 0; i < numSamples; i++)
    77.                 {
    78.                     if (xDir == 1)
    79.                     {
    80.                         heights1[z, width - 1 - i] = heights1[z, width - 1 - i] + getYMod.Invoke(domTerrain, 1, dY, z, height, i, numSamples);
    81.                         heights2[z, i] = heights2[z, i] - getYMod.Invoke(domTerrain, 2, dY, z, height, i, numSamples);
    82.                     }
    83.                     else
    84.                     {
    85.                         heights1[z, i] = heights1[z, i] + getYMod.Invoke(domTerrain, 1, dY, z, height, i, numSamples);
    86.                         heights2[z, width - 1 - i] = heights2[z, width - 1 - i] - getYMod.Invoke(domTerrain, 2, dY, z, height, i, numSamples);
    87.                     }
    88.                 }
    89.             }
    90.         }
    91.         else
    92.         {
    93.             var zDir = terrain2.transform.position.z > terrain1.transform.position.z ? 1 : -1;
    94.  
    95.             terrain2.transform.position = terrain1.transform.position;
    96.             terrain2.transform.position = new Vector3(terrain2.transform.position.x, terrain2.transform.position.y, terrain1.transform.position.z + terrain1.terrainData.size.z * zDir);
    97.  
    98.             for (int x = 0; x < height; x++)
    99.             {
    100.                 var dY = 0.0;
    101.                 if (zDir == 1)
    102.                 {
    103.                     dY = heights2[0, x] - heights1[width - 1, x];
    104.                 }
    105.                 else
    106.                 {
    107.                     dY = heights2[width - 1, x] - heights1[0, x];
    108.                 }
    109.                 for (int i = 0; i < numSamples; i++)
    110.                 {
    111.                     if (zDir == 1)
    112.                     {
    113.                         heights1[width - 1 - i, x] = heights1[width - 1 - i, x] + getYMod.Invoke(domTerrain, 1, dY, x, height, i, numSamples);
    114.                         heights2[i, x] = heights2[i, x] - getYMod.Invoke(domTerrain, 2, dY, x, height, i, numSamples);
    115.                     }
    116.                     else
    117.                     {
    118.                         heights1[i, x] = heights1[i, x] + getYMod.Invoke(domTerrain, 1, dY, x, height, i, numSamples);
    119.                         heights2[width - 1 - i, x] = heights2[width - 1 - i, x] - getYMod.Invoke(domTerrain, 2, dY, x, height, i, numSamples);
    120.                     }
    121.                 }
    122.             }
    123.         }
    124.  
    125.         memoizationTablesFilled = true;
    126.  
    127.         terrain1.terrainData.SetHeights(0, 0, heights1);
    128.         terrain1.Flush();
    129.         terrain2.terrainData.SetHeights(0, 0, heights2);
    130.         terrain2.Flush();
    131.     }
    132.  
    133.     static float getYModDynamic(int domTerrain, int terrainToMod, double dY, int curCellX, int maxCellsX, int curCellY, int maxCellsY)
    134.     {
    135.         double yMod = dY / 2.0f;
    136.  
    137.         if (terrainToMod == 1)
    138.         {
    139.             if (domTerrain == 1)
    140.             {
    141.                 yMod = dY * smoothMod(curCellX, maxCellsX);
    142.             }
    143.             else if (domTerrain == 2)
    144.             {
    145.                 yMod = dY * (1.0f - smoothMod(curCellX, maxCellsX));
    146.             }
    147.         }
    148.         else if (terrainToMod == 2)
    149.         {
    150.             if (domTerrain == 1)
    151.             {
    152.                 yMod = dY * (1.0f - smoothMod(curCellX, maxCellsX));
    153.             }
    154.             else if (domTerrain == 2)
    155.             {
    156.                 yMod = dY * smoothMod(curCellX, maxCellsX);
    157.             }
    158.         }
    159.         else
    160.         {
    161.             Debug.LogError("terrainToMod must be either 1 or 2! (found: " + terrainToMod + ")");
    162.         }
    163.  
    164.         return (float)(yMod * smoothDY(curCellY, maxCellsY));
    165.     }
    166.  
    167.     static float getYModMemoized(int domTerrain, int terrainToMod, double dY, int curCellX, int maxCellsX, int curCellY, int maxCellsY)
    168.     {
    169.         double yMod = (double)dY / 2.0f;
    170.  
    171.         if (terrainToMod == 1)
    172.         {
    173.             if (domTerrain == 1)
    174.             {
    175.                 yMod = dY * smoothModTable[curCellX];
    176.             }
    177.             else if (domTerrain == 2)
    178.             {
    179.                 yMod = dY * (1.0f - smoothModTable[curCellX]);
    180.             }
    181.         }
    182.         else if (terrainToMod == 2)
    183.         {
    184.             if (domTerrain == 1)
    185.             {
    186.                 yMod = dY * (1.0f - smoothModTable[curCellX]);
    187.             }
    188.             else if (domTerrain == 2)
    189.             {
    190.                 yMod = dY * smoothModTable[curCellX];
    191.             }
    192.         }
    193.         else
    194.         {
    195.             Debug.LogError("terrainToMod must be either 1 or 2! (found: " + terrainToMod + ")");
    196.         }
    197.  
    198.         return (float)(yMod * smoothDYTable[curCellY]);
    199.     }
    200.  
    201.     static double smoothDY(int current, double max)
    202.     {
    203.         double x = 1.0f - (double)current / max - 1;
    204.  
    205.         var result = (236706659320000.0 * Math.Pow(x, 6) + 99115929736349168000.0 * Math.Pow(x, 5) - 247790818523389713100.0 * Math.Pow(x, 4)
    206.             + 80036273392876468420.0 * Math.Pow(x, 3) + 127736693875550215551.0 * Math.Pow(x, 2) - 713647159857677493.0 * x) / 58384668816317760000.0;
    207.         smoothDYTable[current] = result;
    208.         return result;
    209.     }
    210.  
    211.     static double smoothMod(int current, double max)
    212.     {
    213.         double x = (double)current / max - 1;
    214.  
    215.         var result = 1.0E-4 * ((1 * (x - 0.0) * (x - 0.05) * (x - 0.1) * (x - 0.5) * (x - 0.9) * (x - 0.95) * (x - 0.99) * (x - 1.0)) / -1.4317846804800003E-5) +
    216.             0.0050 * ((1 * (x - 0.0) * (x - 0.01) * (x - 0.1) * (x - 0.5) * (x - 0.9) * (x - 0.95) * (x - 0.99) * (x - 1.0)) / 3.074152499999999E-5) +
    217.             0.04 * ((1 * (x - 0.0) * (x - 0.01) * (x - 0.05) * (x - 0.5) * (x - 0.9) * (x - 0.95) * (x - 0.99) * (x - 1.0)) / -9.804240000000002E-5) +
    218.             0.5 * ((1 * (x - 0.0) * (x - 0.01) * (x - 0.05) * (x - 0.1) * (x - 0.9) * (x - 0.95) * (x - 0.99) * (x - 1.0)) / 0.0019448099999999997) +
    219.             0.04 * ((1 * (x - 0.0) * (x - 0.01) * (x - 0.05) * (x - 0.1) * (x - 0.5) * (x - 0.95) * (x - 0.99) * (x - 1.0)) / -9.804239999999985E-5) +
    220.             0.0050 * ((1 * (x - 0.0) * (x - 0.01) * (x - 0.05) * (x - 0.1) * (x - 0.5) * (x - 0.9) * (x - 0.99) * (x - 1.0)) / 3.0741525000000005E-5) +
    221.             1.0E-4 * ((1 * (x - 0.0) * (x - 0.01) * (x - 0.05) * (x - 0.1) * (x - 0.5) * (x - 0.9) * (x - 0.95) * (x - 1.0)) / -1.4317846804800018E-5);
    222.         smoothModTable[current] = result;
    223.         return result;
    224.     }
    225. }
    226.  
     
    Last edited: Aug 31, 2014
  20. GemInd_Games

    GemInd_Games

    Joined:
    Oct 28, 2015
    Posts:
    1
    Ok this is probably a dead thread and i know its a stupid question, but how do i get it to work? what do I do to install and how do I get it to actually open up
     
  21. nasos_333

    nasos_333

    Joined:
    Feb 13, 2013
    Posts:
    13,360
    Just to clarify what is meant by free for non commercial use ?

    Can we use the stitched terrain for example in a game and not use the software in a development environment as a tool ?

    Or the stitched terrains cant be used in a commercial game ?

    Thanks
     
  22. Yourking77

    Yourking77

    Joined:
    Jan 14, 2016
    Posts:
    303
    I realize this thread is probably dead but I need help, I have it working properly but it moves my terrain to a different height even though the seams are very close to each other, It makes it near impossible to get the results I need to make my game. Am I missing something? or is there some workaround or something?
     
  23. ila18555477

    ila18555477

    Joined:
    Aug 31, 2016
    Posts:
    2
    Hi
    i am creating my campus in unity 3D. I am using multiple terrains. But could you please explain the process how to stitch them using API provided by you?

    Thanks in advance
     
  24. ila18555477

    ila18555477

    Joined:
    Aug 31, 2016
    Posts:
    2
    I tried working the way readme document states. But its not working
     
  25. Ahad2232

    Ahad2232

    Joined:
    Jun 20, 2017
    Posts:
    1
    How can I use this because i dont rly understand how to use it
     
  26. mightybob

    mightybob

    Joined:
    Mar 23, 2014
    Posts:
    75
    Is there anything really keeping us from using this commercially? It's just modifying heightmap data, delete it before release and no one cares, right?

    edit: and if you're releasing it for free why even try to impose that limitation?