ECS(DOTS) && Cesium for unity3D

Is there anyone trying to use ecs ?
Has anyone tried using DOTS such as entity for Cesium development?(GitHub - Unity-Technologies/EntityComponentSystemSamples)

Cesium for Unity uses a number of MonoBehaviours. But I don’t know of any reason that other objects in your scene couldn’t be entity-based. I haven’t tried it, though.


I am working on vector data rendering on terrain.

in order to do this. I have to write effective logic for camera frustum culling for shadow volume meshes.

so I am considering to use DOTS for quad tree culling. check this asset below. If you want to check out proper example then this will be good sample for you.

Hi @Kevin_Ring . I just started on a new Unity project using Cesium. This project requires that we visualize microparticles in the ocean. We are using ECS for the particle simulation & rendering. The particle data we have is in global longitude/latitude/depth (in meters) of each particle in the simulation.

I’ve looked into using a subscene for this, but I’m unsure how to translate the lon/lat/depth particle values to the correct coordinates in Unity to visualize them on the map.

Do you have any tips on how to position the ECS particles on the Cesium map?

I’ve looked at the GlobeAnchor and SubScenes but these are managed objects and I’m just not sure how to position the ECS particles using either of these components in an ECS system or EnityJob that is burst compiled.

Thanks in advance!

This is what I have in the EnityJob’s Execute method:

    public float3 GeoToLocalPosition(float3 latitudeLongitudeDepth)
        var ecef = CesiumWgs84Ellipsoid.LongitudeLatitudeHeightToEarthCenteredEarthFixed(latitudeLongitudeDepth);
        var pos = mul(ECEFtoLocal, double4(ecef, 1.0)).xyz;
        return float3(pos);

Where ECEFtoLocal is the ecefToLocalMatrix of the CesiumGeoReference in the scene.

I’m getting really huge numbers as a result, but I’m not sure what I’m doing wrong.

For example, the latitudeLongitudeDepth has a value of (63, 10, -100) which has an ECEF value of (2804072, 5612559, 1143196) and the origin of the CesiumGeoReference is set to (63, 10, 0), it has a ECEF origin of (2812972, 515529, 5684178) the above function produces a local position of (5015134, -3652876, -2842322) which doesn’t seem correct to me. The particles should be closer to the origin in Unity.

Note: The above values are truncated to whole numbers.

Any ideas what I’m doing wrong here? Or is there a better way to convert lon/lat/depth to Unity coordinates?

Any hint what I’m doing wrong?

Hi @jpvanoosten,

Our team isn’t too familiar with ECS, so we may limited in our ability to help. But maybe we can double-check the math for you. What does your scene hierarchy look like, and what is the transform of the CesiumGeoreference?

In the image, the ECS hierarchy is on the left. The GeoReference is the at the root. The ECS subscene is a child of the GeoReference and has a GlobeAnchor component on it (but I’m not sure this is needed in this case).

I’ve selected one of the particles to see what it’s position is at runtime. As you can see in the inspector, the position is some really big numbers in all three axes. This should be the “local” position of the particle, but to be honest, I’ve just started using ECS and I’m not sure if entity positions take the position of the ECS (parent) subscene into consideration.

My basic approach to placing the particles in the scene (using the GeoToLocalPosition function described above):

  1. Read the lon/lat/depth values of the particle that frame
  2. Convert the lon/lat/depth values from lon/lat/depth to ECEF
  3. Convert the ECEF values to Unity coordinates

Obviously, if I can go straight from lon/lat/depth values to Unity local space, that would be ideal, but I’m not a math genius and I can’t quite figure out how to do that (without a week for research on the math). I was hoping there is some magic Cesium functions to do that, but I also can’t find the API documentation for the CesiumUnity package so I have to read the source code to figure it out, but most of it is behind prebuilt binaries (and this is probably in the cesium-native repo, but this is where the search starts getting complex for a newbie like me).

I can’t just use the GlobeAnchors on the ECS entities in the scene. You can’t just use a standard components on entities like you can on GameObjects in Unity. I can read data (like a transform matrix) from the the components and translate that to the ECS world and do the math there (which is what I’m trying), but the numbers just don’t come out to what I expect them to.

Sorry for the long post, but I’m really struggling with this and my project lead is getting impatient :wink:

Just an update, this is what I should see when running (this is using MapBox):

The particles are colored based on depth. Black being 100m below sea level, white being on the surface of the water.

You’re converting coordinate to Earth-Centered, Earth-Fixed, which is a coordinate system centered at the center of the Earth. Any point on the surface of the Earth is going to have huge coordinate values. It’s working as intended.

Presumably you want the Unity coordinates, not the ECEF coordinates. You can compute Unity coordinates with something like this:

var ecef = CesiumWgs84Ellipsoid.LongitudeLatitudeHeightToEarthCenteredEarthFixed(latitudeLongitudeDepth);
var unity = cesiumGeoreference.TransformEarthCenteredEarthFixedPositionToUnity(ecef);

Thanks for your reply, but since I can’t access a managed reference in the Entities world, I can’t just call that function on the CesiumGeoreference object. But if I look into this function, I see:

        public double3
            TransformEarthCenteredEarthFixedPositionToUnity(double3 earthCenteredEarthFixed)
            return math.mul(this._ecefToLocal, new double4(earthCenteredEarthFixed, 1.0)).xyz;

which is a simple transformation that I can do in a burst compiled script in ECS. In order to do that, every frame, I copy the CesiumGeoreference::_ecefToLocal matrix to the ECS world through a component:

       // This is called every frame from GameObject world.
        if (entityQuery.HasSingleton<AbstractMapData>())
            var mapEntity = entityQuery.GetSingletonEntity();

            var abstractMapData = new AbstractMapData();

            abstractMapData.ECEFMatrix = Georeference.ecefToLocalMatrix; // Copy the ecefToLocal matrix to the map component data.

            entityManager.SetComponentData(mapEntity, abstractMapData);

Then in the entity world, I use the ECEF matrix to update the particle position from it’s lon/lat/depth values:

    public float3 GeoToLocalPosition(float3 latitudeLongitudeDepth)
        var ecef = CesiumWgs84Ellipsoid.LongitudeLatitudeHeightToEarthCenteredEarthFixed(latitudeLongitudeDepth); // I can access static methods in a burst compiled entity job.
        var pos = mul(ECEFtoLocal, double4(ecef, 1.0)).xyz; // Transform from ECEF to Unity local coordinates.
        return float3(pos);

So in essence, it should be identical to your purposed solution, but I’m not getting the expected values.
I did get this working with MapBox (see screenshot in previous post), but they have an example that shows how to position objects in the Unity scene without using a Georeference or GlobeAnchors so it was easy to translate that to the ECS world.
I understand that Cesium was not built with ECS in mind and maybe I shouldn’t even be trying to do this, but I really like the Cesium ecosystem and I was hoping I could use the Unity plugins to accomplish this, but this has been a harder than I anticipated.

Thanks for your help so far.

Ok, I think that makes sense to me as well. Though I haven’t used DOTS much so there could be subtleties I’m missing. If the coordinates you’re passing in are near the CesiumGeoreference origin, then they should have small values. Otherwise they can be quite large, of course. So debug it methodically: First, are you passing in the coordinates correctly? As the name implies, LongitudeLatitudeHeightToEarthCenteredEarthFixed expects longitude to be first, in the X coordinate. You’ve named your parameter latitudeLongitudeDepth so maybe you’ve adopted the opposite convention. That would certainly mess up the coordinate values.

Second, call the CesiumGeoreference functions directly. Do they provide the coordinates you expect?

Lastly, do the same but with your DOTS approach. Does it still work?

You were right about the latitude/longitude being swapped. This was a total oversight on my part since there was some confusion about the order of the components with MapBox expecting these components to be lat/log/depth.

So, in the end, the math was right, but the order of the components was mixed up! How did I not see this… It’s so easy to get blinded by these issues (especially when trying to take on so many new things at once :slight_smile:)

Thanks for your support with this.

Totally off topic, but I just got my copy of “3D Engine Design for Virtual Globes” and saw you name on the cover! I’ll be reading this book during my Christmas holidays! :christmas_tree:


Cool, I hope you enjoy it! Maybe someday Patrick and I will get around to a second edition. :grin:

1 Like