Using Unity Terrain with DOTS workflow

Hi,

I am trying to use Unity Terrain with the Unity Physics package.

Currently I am generating a mesh from a given terrain and using that mesh as the custom mesh in the physics shape authoring component. I am using the terrain gameobject for the rendered representation. This works fine, but the work flow is less than ideal (we have to generate a new mesh each time the terrain is altered). I also have some concerns about collision accuracy relative to the visualized terrain in some parts of the terrain.

Is there a more direct way to use Unity Terrains with Unity Physics?

I noticed that there is a TerrainCollider in the Unity.Physics package, but I don’t see any authoring components to use with it. I don’t mind writing my own authoring component if necessary.

Thanks.

Terrain & Physics is currently not supported. We are working on a dedicated dots terrain engine that will fill the hole.

22 Likes

You can easily create a terrain collider from a terrain. Just write your own authoring component or a system that creates it. At least i haven’t found an issue yet :slight_smile:

5 Likes

I hope dedicated dots terrain engine able to add/remove specific feature completely so at runtime it can give best performance for mobile device.

1 Like

Awesome. Thats a totally reasonable usage for the low level API’s in physics in the interim until we have a dedicated dots terrain system.

1 Like

Here is my terrain collider authoring component for physics >= 0.2.1:

public class PhysicsTerrain : MonoBehaviour, IConvertGameObjectToEntity
    {
        [SerializeField] PhysicsCategoryTags belongsTo;
        [SerializeField] PhysicsCategoryTags collidesWith;
        [SerializeField] int groupIndex;
     
        public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
        {
            var terrain = GetComponent<Terrain>();

            if (terrain == null)
            {
                Debug.LogError("No terrain found!");
                return;
            }

            CollisionFilter collisionFilter = new CollisionFilter
            {
                BelongsTo = belongsTo.Value,
                CollidesWith = collidesWith.Value,
                GroupIndex = groupIndex
            };
         
            dstManager.AddComponentData(entity,
                MapHelper.CreateTerrainCollider(terrain.terrainData, collisionFilter));
        }
    }

And the CreateTerrainCollider method:

public static PhysicsCollider CreateTerrainCollider(TerrainData terrainData, CollisionFilter filter)
        {
            var physicsCollider = new PhysicsCollider();
            var size = new int2(terrainData.heightmapWidth, terrainData.heightmapHeight);
            var scale = terrainData.heightmapScale;

            var colliderHeights = new NativeArray<float>(terrainData.heightmapWidth * terrainData.heightmapHeight,
                Allocator.TempJob);

            var terrainHeights = terrainData.GetHeights(0, 0, terrainData.heightmapWidth,
                terrainData.heightmapHeight);

          
            for (int j = 0; j < size.y; j++)
            for (int i = 0; i < size.x; i++)
            {
                var h = terrainHeights[i, j];
                colliderHeights[j + i * size.x] = h;
            }

            physicsCollider.Value = TerrainCollider.Create(colliderHeights, size, scale,
                TerrainCollider.CollisionMethod.Triangles, filter);

            colliderHeights.Dispose();

            return physicsCollider;
        }

UPDATE: I reworked the for loops and array indizes for terrainHeights and colliderHeights. Seems to be more correct now :slight_smile:

30 Likes

@daschatten , great work here. Question: has anyone modified this to create colliders for trees also?
@Joachim_Ante_1 , is there any word regarding the dedicate DOTS terrain system?

1 Like

You can use the default conversion for all colliders except terrain.

1 Like

Thank you! Works well, but collisions with other entities fail to happen.
Is that because the collider needs to be convex? And how would you go about doing that in ECS?

Edit: I was just being an idiot with the “collisions fail to happen” part. Was using the wrong collision masks. Still i would like to hear how one could turn this into a convex collider, as I’m in the process of learning DOTS! :slight_smile:
Thanks alot for the code!

Thank you, this is awesome.

The only thing I bumped into was Objects don’t collide when then terrain is exactly flat in a region, and they fall through it. (I am using Unity 2020.1.1f1, havok physics 0.3.1-preview, Entities 0.14.0-preview.18, with havok physics enabled).

Just in case i forget and need it later :slight_smile: or if someone else has the same issue.

I added a very small random range to each height point in your code:

var randomExtra = UnityEngine.Random.Range(0.00001f, 0.0001f); // NOTE: Added to original code to prevent flat terrain colliders not working

colliderHeights[j + i * size.x] = h + randomExtra;

I saw people strungling in this question in Unity Physics threads because they think the need to use physics shape component and co…
So for poeple who dont want to do any mistake in setup of Unity Physics terrain collision :

https://drive.google.com/drive/folders/1xMqXOHUlsNorgt_Opymeaj4AmTUiODAu?usp=sharing

Just add the two scripts to your project and setup the terrain like on the screenshot include with the two scripts. (Don’t forget to create a dedicated terrain physics tag to be clean)

7 Likes

There’s also Authoring to create DOTS Physics colliders from standard unity terrains · DOTS-Discord/Unity-DOTS-Discord Wiki · GitHub which I made and use in my personal project. It is based upon the authoring that was provided by @daschatten .

3 Likes

nice! , would like to hear release date for the dots terrain engine. is it planned for 2020.2 or 2021? when do u think is coming out.?

You are a legend!

Hello everyone,
this thread is amazing! :slight_smile:

how do you guys renderer the terrain ?

I render the terrain in monobehaviour land.

2 Likes

You can also use DrawMeshInstantiated/Indirect methods.

Sry but I didn’t get it xD ,
How do you render a terrain with DrawMeshInstantiated methods ?

You probably are aware of that link, since you corrected my misspelling :stuck_out_tongue:

For example in Update,

Graphics.DrawMeshInstanced ( myMesh, 0, material, matrices, elementsBatchCount, propertyBlock, UnityEngine.Rendering.ShadowCastingMode.On, true, cameraLayer, camera ) ;
matrices = localToWorld.Value ;
elementsBatchCount = 1 ;
// Use, if you don't need pass shader properties
MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock() ;
// afer this parameter, other are optiional.
2 Likes

Yes I’m aware about DrawMeshInstanced i used it for my custom Hybrid Renderer, but i dont understand how you get a mesh and material from a terrain.