Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice

how to detect terrain type

Discussion in 'Editor & General Support' started by patricia, Aug 12, 2007.

  1. patricia

    patricia

    Joined:
    Aug 10, 2007
    Posts:
    73
    I'm currently testing cinema 4d ( i figured i'd better buy a good 3d software for use with unity ), and i have a little question.
    If you have a racing games, how do you detect that the car is on a muddy road, or on asphalt ?
    Is it possible to flag the polygons ?
    I was thinking of using 1 texture per terrain type, is this the way to do it ? or maybe i have to prepare it another way in the cinema 4d package ?

    Thank you
    patricia.
     
  2. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    One way to do this is to cast a ray downwards using the second or fourth version of Physics.Raycast. These two functions will write to a RaycastHit, which will give you a triangle index as well as UV coordinates. The latter could be used with Texture2D.GetPixel to find out what colour the ground is.

    I recommend you use either the 2-layer LayerShader or the TerrainFourLayer shader, and then check the colour on the mixing mask texture. This way your terrain types can be whatever colour you want, and you don't have to worry about determining the terrain type by its actual colour. The other advantage of the multi-layer shaders is that you can change the scale of the tiled textures while still using the full-scale mixing mask. This allows you to have a higher-resolution terrain without using enormous textures.
     
    florianhanke likes this.
  3. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Two approaches come to mind:

    1) use a single mesh representing the whole "ground", and draw a special texture where colors indicate the terrain type. Or draw the same information into vertex colors of that mesh. This mesh could be a simpler version of the visible mesh (i.e. it does not have to be the same as the visible mesh). Then raycast against this mesh, get back the UVs or triangle data, and read the color at that spot in your terrain-types texture (or get vertex colors from the triangle that is hit).

    2) use separate meshes for different terrain types. One mesh that is only the asphalt, one that is only the grass and so on. If joined, those meshes would form the whole "ground". Then raycast, and depending on which mesh is hit you know the terrain type.
     
  4. terransage

    terransage

    Joined:
    Jul 17, 2006
    Posts:
    290
    Welcome, patricia!
    Hm, I guess it kind of depends on what you want to do, like change the way the vehicle rides over the terrain, etc.. You could use a trigger at the start of each terrain, one that stretches across the road. For that you could just use a cube with a function OnTriggerEnter() script that triggers whatever event that you want to happen. Just set the box collider to IsTrigger and make the cube invisible by clicking the renderer off.

    I don't know if that helps, because there might be something else in particular that you're trying to do.

    Edit: Oops, the other answers are better! I was thinking along different lines....
     
  5. patricia

    patricia

    Joined:
    Aug 10, 2007
    Posts:
    73
    Thanks for the suggestions.
    Shaders are not an option, if won't work on my video card.
    I was also thinking of using different mesh, but then it's going to be a nightmare to edit the land :)
    I'd rather just model one big land and texture it. ( maybe i'm wrong and i should use several mesh though, i'm not experienced enough )
    The color idea is doable, that's something that could work.
    Thanks
    patricia.
     
  6. jeremyace

    jeremyace

    Joined:
    Oct 12, 2005
    Posts:
    1,661
    Alpha blending wont even work on your card? Ouch. I used to use an old Emac with a GeForce MX card, and I had similar issues.

    The vertex colors are stored in the mesh though, so you can get them without a shader. Check the Mesh class for Mesh.colors in the scripting reference. You will have to write code that does a raycast and gets the vertex colors though, which might be a pain depending on your coding skill. If you request it, I am sure someone could help guide you in a direction for that.

    An easier way would be the trigger idea that terransage suggested. What I would do is have an index for each terrain/floor type (0=rock, 3=grass, etc) then when you enter the trigger for an area, check a script on the player to see if that index is already set as the current ground cover, and if it isn't set it to the index of this section.

    Then you use that stored index to decide what sound to play. This way you could have several triggers set up to cover the same area without worrying, because if the ground type is already set correctly, nothing get's changed.

    HTH,
    -Jeremy
     
  7. Alpha-Loup

    Alpha-Loup

    Joined:
    Jun 23, 2006
    Posts:
    797
    While doing some mod work on "call of duty" i noticed, that all the texture-names contained somthing like metal@ oder rock@ and so on in their file-name.

    Is it possible to check the terrain by identifying the textures file-name?

    Just an idea from a non-coder.
     
  8. jeremyace

    jeremyace

    Joined:
    Oct 12, 2005
    Posts:
    1,661
    Nope, the Material class doesn't have a texture name property, just the usual inherited object name.

    EDIT: and either way, if you use one texture for a few different terrain types (most likely as her card can't do blending), then you are still screwed.

    -Jeremy
     
  9. Jonathan Czeck

    Jonathan Czeck

    Joined:
    Mar 17, 2005
    Posts:
    1,713
    theMaterial.mainTexture.name

    In response to the original topic, if it were me I would use a splat shader on the terrain (See wiki) and then get the current UV coordinate under the car (using Raycast) and then get the color at that coordinate from the control texture. Once you have the color, you can determine which texture is strongest at that point and proceed from there.

    Cheers,
    -Jon
     
  10. Alpha-Loup

    Alpha-Loup

    Joined:
    Jun 23, 2006
    Posts:
    797
    Thanks again.

    Would that work with only one or also with multiple textures applied to a single object?
     
  11. Jonathan Czeck

    Jonathan Czeck

    Joined:
    Mar 17, 2005
    Posts:
    1,713
    In the case of multiple materials, you can look at each one by getting a list via renderer.materials.

    Code (csharp):
    1. for (var mat in renderer.sharedMaterials) { Debug.Log(mat.mainTexture.name); }
    -Jon

    edit: Oops, got my Unity versions wrong! It's sharedMaterials.
     
  12. Alpha-Loup

    Alpha-Loup

    Joined:
    Jun 23, 2006
    Posts:
    797
    That helps a lot! :D
     
  13. jeremyace

    jeremyace

    Joined:
    Oct 12, 2005
    Posts:
    1,661
    Cool thanks Jon, didn't know you could get a list of all materials. But would this work with an alpha blended terrain shader? (forgive my lack of shader knowledge).

    Anyway, for the vertex color idea, I just whipped up a script to do that.

    It's in C#, but pretty well abstracted. It currently has the debug run still set (to remove it, delete the Update() block)

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System;
    4. using System.Collections;
    5.  
    6. public class GroundType : MonoBehaviour
    7. {
    8.     public Transform characterTransform = null;  //if left unset, it will use the current object's transform
    9.     public Color[] groundTypeColors = null;      //the colors we want to use to specify our ground types
    10.     public Vector3 raycastDirection = Vector3.down;
    11.     public float raycastDistance = 10.0F;
    12.  
    13.     private RaycastHit hit;
    14.     private int triIdx;
    15.     private Vector3[] vertices;
    16.     private int[] tris;
    17.     private Color[] colors;
    18.     private Color p0, p1, p2;
    19.  
    20.     void Start()
    21.     {
    22.         if (characterTransform == null)
    23.             characterTransform = transform;
    24.     }
    25.  
    26.     void Update()
    27.     {
    28.         Debug.Log(GetGroundColor().ToString());
    29.     }
    30.  
    31.     public int GetGroundColor()
    32.     {
    33.         if (Physics.Raycast(characterTransform.position, raycastDirection, out hit, raycastDistance))
    34.         {
    35.             //set up the mesh data
    36.             MeshCollider meshCollider = hit.collider as MeshCollider;
    37.             if (meshCollider == null || meshCollider.sharedMesh == null)
    38.                 return -1; //error
    39.             Mesh mesh = meshCollider.sharedMesh;
    40.             vertices = mesh.vertices;
    41.             tris = mesh.triangles;
    42.             colors = mesh.colors;
    43.  
    44.             //now check the vertex colors
    45.             triIdx = hit.triangleIndex;
    46.             p0 = colors[tris[triIdx * 3 + 0]];
    47.             p1 = colors[tris[triIdx * 3 + 1]];
    48.             p2 = colors[tris[triIdx * 3 + 2]];
    49.  
    50.             for (int i = 0, iLength = groundTypeColors.Length; i < iLength; i++)
    51.             {
    52.                 for (int x = 0, xLength = vertices.Length; x < xLength; x++)
    53.                 {
    54.                     //currently only checks one point of the triangle, if you need to test all 3, do the same test for p1 and p2 as well
    55.                     if (Mathf.Approximately(p0.r, (float)groundTypeColors[i].r)  Mathf.Approximately(p0.g, (float)groundTypeColors[i].g)  Mathf.Approximately(p0.b, (float)groundTypeColors[i].b))
    56.                     {
    57.                         //Debug.Log(i.ToString());
    58.                         return i;
    59.                     }
    60.                 }
    61.             }            
    62.         }
    63.         return -1;
    64.     }
    65. }
    66.  
    Put this on some object and set the following vars in the inspector:

    -Character Transform = drag your character to this slot if this script is _not_ on your character. Otherwise leave blank and the raycast will be fired from the position of the object this script is placed on.

    -Ground Type Colors = is an array of all the vertex colors you want to test against. Set size to the amount of colors you want to test against, and set them accordingly.

    Raycast Direction = set to Vector3.down (0, -1, 0), you should be able to leave this alone.

    Raycast Direction = the distance to fire the raycast. Set to 10 which should be fine, but if your character is not finding any colors, this may need to be set larger.

    It will print the index of the color it found in the console at the bottom (returned values explained below)

    The function GetGroundColor() returns a number which tells you what color (if any) it hit. The returned number is an index to that Ground Type Colors array we set earlier, so a returned index of 1 would be the "Element 1" color.

    GetGroundColor() will return -1 if none of the colors match.

    *please note* This currently only checks against 1 vertex of the hit triangle. If you need to make sure all 3 points have the same color, modify the code where I commented.

    I am sure there is a nicer way to do it, but it works. ;-)

    You should also be able to convert this to JS easy enough.

    HTH,
    -Jeremy
     
  14. joegalaxy

    joegalaxy

    Joined:
    Nov 6, 2008
    Posts:
    16
    Hi, I have the same problem and found that jeremyace code does exactly what was needed. Anyway, even before actually trying jeremyace code, I'm a little concerned about speed. Doing all this stuff all frames would degrade performance? What about building a 2d array using this script at load time to "map" terrain and assign to each array value the correct terrain type? I'm just thinking about this color coded chessboard, that would reduce runtime operations to a simple lookup on the array based on actual player position.
    Would like to know your opinions and many thanks to anyone.
     
  15. jeremyace

    jeremyace

    Joined:
    Oct 12, 2005
    Posts:
    1,661
    Yes, in most cases caching is a good idea if you can afford the memory hit.

    As long as you have a very quick way to map position to your lookup table, you could get the feature almost for free, after preprocessing.

    -Jeremy