Search Unity

Leveling Terrain below GameObjects

Discussion in 'World Building' started by Rowlan, Oct 21, 2019.

  1. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
    @wyatttt I was just prototyping and wondering if we'll see a Leveling Terrain Tool in the not too distant future. Can you shed light on this? If you guys will provide one, I'll save myself the work before I create my own.

    Example usage: I put a Wind Turbine on the terrain and would have the terrain leveled from top and bottom, see red arrows:

    float.jpg
    Same would apply for e. g. Houses and would be a very general use case, hence the question.

    If Unity doesn't provide one, would you guys (or anyone else, please feel free to join in) have any pointers about how to approach this? I'd simply use the Terrain Tools that Unity provides and if the brush is on a GameObject, I'd get the lowest point and use the Set Height Tool. Or is there a much better approach?
     
    transat likes this.
  2. Magnitude9

    Magnitude9

    Joined:
    Jul 11, 2017
    Posts:
    11
  3. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
    Thanks, I don't mind buying an asset, but my current motivation regarding terrain - which is very much in flux - is to either stick with the Unity Standard or create something on my own and give away for free :)
     
  4. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    Hey! We will at some point but I don't know if that will also be included in the Terrain Tools package.

    As for pointers, when I was thinking about doing this for Terrain Tools, I was going to take the projection code for the Mesh Stamp tool to get the area that should be flattened for the whatever mesh I had intersecting the Terrain. You could probably just have a MonoBehavior attached to those GameObjects that modifies the heightmap whenever it's transform changes.

    To make it a little more non-destructive, you will want to store state for the mesh stamp or Terrain so that you can "undo" the effects of the stamp when the stamp is moved elsewhere. You could do this by caching a texture for that area of the Terrain ( this might lead to a lot of memory usage though ).

    If the transform for the mesh changes, either apply the inverse of the mesh stamp or apply a cached portion of the heightmap that existed before the stamp was placed there then apply the stamp in the new location for the transform. This breaks when the mesh is being stamped at an elevation that causes the Terrain height to be 0 or the Terrain's max height; that's when you start to lose your height information.

    Actually, caching a subset of the heightmap is probably the better way to go. You might lose height information depending on how you flatten the Terrain under the stamp.

    You might also want to apply a blur on the mask

    Once you have the mask, then you'd want to modify the Terrain height by lerping the existing height towards the intersection height ( which could just be the bottom of the mesh bounds in Terrain space ) based on the texel value in the mask texture. You can use the Terrain Tools API to do all this. It's the same calls: BeginPaintHeightmap, EndPaintHeightmap, etc.
     
    Last edited: Oct 21, 2019
    Rowlan likes this.
  5. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    this is the function call in the Mesh Stamp Tool


    Code (CSharp):
    1. // generate a mask for the mesh to be used in the compositing shader
    2. TerrainTools.MeshUtility.RenderTopdownProjection(
    3.     activeMesh, //  the mesh to be rendered
    4.     model, // model transformation matrix to be used
    5.     renderTextureDestination, // render texture to blit to
    6.     TerrainTools.MeshUtility.defaultProjectionMaterial, // you can replace this with your own if you want. just needs to render the height or depth
    7.     TerrainTools.MeshUtility.ShaderPass.Height ); // this could just be zero if using the builtin material. the second pass always returns 1. can't remember why i did that
    8.  
    here is the code for the projection / height shader:

    Code (CSharp):
    1. Shader "Hidden/TerrainTools/MeshUtility"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ( "Texture", any ) = "" {}
    6.     }
    7.  
    8.     SubShader
    9.     {
    10.         ZTest LEQUAL Cull OFF ZWrite ON
    11.  
    12.         HLSLINCLUDE
    13.  
    14.         #include "UnityCG.cginc"
    15.  
    16.         sampler2D _MainTex;
    17.  
    18.         struct appdata_t
    19.         {
    20.             float4 vertex : POSITION;
    21.             float2 texcoord : TEXCOORD0;
    22.         };
    23.  
    24.         float4x4 _Matrix_M;
    25.         float4x4 _Matrix_MV;
    26.         float4x4 _Matrix_MVP;
    27.  
    28.         ENDHLSL
    29.  
    30.         Pass // render mesh depth to rendertexture
    31.         {
    32.             HLSLPROGRAM
    33.  
    34.             #pragma vertex vert
    35.             #pragma fragment frag
    36.  
    37.             struct v2f
    38.             {
    39.                 float4 vertex : SV_POSITION;
    40.                 float4 worldPos : TEXCOORD1;
    41.                 // float4 viewPos : TEXCOORD2;
    42.             };
    43.  
    44.             v2f vert( appdata_t v )
    45.             {
    46.                 v2f o;
    47.  
    48.                 float2 b = float2( 0, 1 );
    49.            
    50.                 o.worldPos = mul( _Matrix_M, float4( v.vertex.xyz, 1 ) );        // world space position
    51.                 o.vertex = mul( _Matrix_MVP, float4( v.vertex.xyz, 1 ) );   // clip space position
    52.  
    53.                 return o;
    54.             }
    55.  
    56.             float4 frag( v2f i ) : SV_Target
    57.             {
    58.                 return i.worldPos.y;
    59.             }
    60.  
    61.             ENDHLSL
    62.         }
    63.  
    64.         Pass // render mask
    65.         {
    66.             HLSLPROGRAM
    67.  
    68.             #pragma vertex vert
    69.             #pragma fragment frag
    70.  
    71.             struct v2f
    72.             {
    73.                 float4 vertex : SV_POSITION;
    74.             };
    75.  
    76.             v2f vert( appdata_t v )
    77.             {
    78.                 v2f o;
    79.  
    80.                 o.vertex = mul( _Matrix_MVP, float4( v.vertex.xyz, 1 ) );   // clip space position
    81.  
    82.                 return o;
    83.             }
    84.  
    85.             float4 frag( v2f i ) : SV_Target
    86.             {
    87.                 return 1;
    88.             }
    89.  
    90.             ENDHLSL
    91.         }
    92.     }
    93. }
     
    Last edited: Oct 21, 2019
    Rowlan likes this.
  6. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
    Thank you very much for the quick reply! I'll check it out.
     
  7. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    and this is how i build the model matrix in the Mesh Stamp tool:

    Code (CSharp):
    1.  
    2.             Matrix4x4 toolMatrix = Matrix4x4.TRS( Vector3.zero, toolSettings.rotation, toolSettings.scale );
    3.  
    4.             Bounds modelBounds = activeMesh.bounds;
    5.             float maxModelScale = Mathf.Max( Mathf.Max( modelBounds.size.x, modelBounds.size.y ), modelBounds.size.z );
    6.             float x = .5f;
    7.             float y = .5f;
    8.             float xy = Mathf.Sqrt( x * x + y * y );
    9.             float z = .5f;
    10.             float xyz = Mathf.Sqrt( xy * xy + z * z );
    11.             maxModelScale *= xyz; // this gets the max length of any bounds axis for the mesh given any rotation and scales
    12.                                                   // it so it always fits within the texture bounds (until you apply more scaling to it ofc.
    13.                                                   // but this will start as fitting in the rendertexture )
    14.  
    15.             // build the model matrix to transform the mesh with. we want to scale it to fit in the brush bounds and also center it in the brush bounds
    16.             Matrix4x4 model = toolMatrix * Matrix4x4.Scale( Vector3.one / maxModelScale ) * Matrix4x4.Translate( -modelBounds.center );
    17.  
    18.             Vector3 translate = Vector3.up * ( toolSettings.stampHeight ) / commonUI.terrainUnderCursor.terrainData.size.y;
    19.             model = Matrix4x4.Translate( translate ) * model;
    20.  
     
    Rowlan likes this.
  8. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    I believe lines 18 and 19 should involve the mesh.transform.y position instead of the toolSettings.stampHeight
     
  9. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    Anytime! Let me know if you have further questions
     
    Rowlan likes this.
  10. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    One thing to note is that this is not an intersection test and will give you odd results for shapes like bridge meshes and chairs. You'd think that the only part where the blending would occur is where the chair legs are but if you just use a plain topdown projection then it will also include the chair seat in the mask
     
  11. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
    That's fine. It's really only just for buildings in order to not have them float in the air.
     
    wyattt_ likes this.
  12. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    Cool. If you end up making something for this, let me know!
     
    Rowlan likes this.
  13. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,278
    Ooh that's a cool way of doing it! I've played around with terrain flattening. My solution was to use the meshes bounding box and flatten it, the use of the new API's is way way better. Would it be possible to make this not a terrain tool, but automated?

    Aka me selecting an object, and running a "flatten terrain" command in a menu? Is it possible to do mesh stamping without having an active tool?
     
    transat likes this.
  14. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    Mesh bounding box would also work. You could make it a MonoBehaviour instead of a Terrain Tool which is probably what you would want anyway. Or have a system running in-editor that you can add these Meshes and transforms to that will modify the Terrain. If there's an overarching system that is running, that might be better because you could then batch overlapping or closely clustered Mesh stamps together in a single render for better blending results instead of doing one Stamp after the other with will favor the blended state of the next Stamp instead of giving all the local Stamps equal blending weight
     
  15. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
    I actually gave scanning against the gameobjects of the scene and then using set height consistently a quick prototypic try. When I used it I noticed an advantage: You could paint e. g. alongside a house and the terrain height doesn't necessarily have to be the gameobject's bottom. It can be anywhere. Quick example usage with painting alongside a cube and a sphere:

    tp.gif

    I guess for fine-tuning this might be interesting.
     
    joshuacwilde, neoshaman and wyattt_ like this.
  16. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    I like that a lot!
     
    neoshaman likes this.
  17. marcrem

    marcrem

    Joined:
    Oct 13, 2016
    Posts:
    340
    Wow! I made my own spline tool that generates roads, because we wanted a tool that was tied to our specific needs instead of using EasyRoads or others, mainly to use with world streaming.

    I've been dreaming of a tool like that, especially non-destructive. If anyone comes up with a little package that does that, I'd gladly donate!
     
  18. konsic

    konsic

    Joined:
    Oct 19, 2015
    Posts:
    995
    What will be differences in Landscape tools in 2020x compared to this Terrain tools?
     
  19. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    Better. Much better
     
    transat and Rowlan like this.
  20. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    You might see some things from Terrain Tools in our work for 2020 but the end goal is a whole lot more than what we did with Terrain Tools.

    The main thing that we've announced is non-destructive layers and the workflows around that
     
    transat, joshcamas and konsic like this.
  21. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
    I've been toying around with terrain alignment recently, especially top-down project of gameobjects on the terrain. One thing led to the other and I thought I'd pick up this old post and finally implement bottom-up projection with heightmap blending:



    I guess I should add a smooth filter around the projected area :)
     
    Last edited: Aug 3, 2021
    marcrem, TerraUnity and wyattt_ like this.
  22. TerraUnity

    TerraUnity

    Joined:
    Aug 3, 2012
    Posts:
    1,255
    My noob question is if new Landscape Tools applies on top of current terrain system and its API or comes as a different solution?
     
    Rowlan likes this.
  23. marcrem

    marcrem

    Joined:
    Oct 13, 2016
    Posts:
    340
    Any chance you make this public?
     
  24. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
    I gotta toy around with it some more. I figured above doesn't make much sense and it would be better to automatically create an adjustable shape (polygon, closed spline) around the gameobject (or collection of gameobjects, eg houses) and apply the terrain level to that shape. The bottom-up projection doesn't work if eg a house doesn't have a floor pointing towards the camera. Besides one usually wants wider bounds around the objects and not just the exact object. But I'll upload the code anyway by end of next week, just in case it's of help for someone.
     
    wyattt_ likes this.
  25. wyattt_

    wyattt_

    Unity Technologies

    Joined:
    May 9, 2018
    Posts:
    424
    Environment is a different solution. You might see some similarities but it's not based on Terrain.
     
    Ruchir and TerraUnity like this.
  26. TerraUnity

    TerraUnity

    Joined:
    Aug 3, 2012
    Posts:
    1,255
    Oh I really thought the opposite, so if we want to port our terrain generators to this, do we need to start over? Is the API as mature as current terrain system?
     
  27. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
    I uploaded it. But as I said, it's not much use if the gameobject doesn't have a floor pointing towards the camera. One could get around it, but it makes much more sense to have an arbitrary shape, preferrably with smoothing around one or multiple gameobjects. Besides I had to introduce a bottom up cut off value which has to be set manually. You don't want eg the roof of a house considered in the projection.
     
    MuradD and TerraUnity like this.
  28. MuradD

    MuradD

    Joined:
    Jul 25, 2013
    Posts:
    1
    and where can I get this script from?
     
  29. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,299
  30. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    MrPapayaMan, imDanOush, 600 and 2 others like this.