To get started with the One World SDK for Unity, at least two concepts need to be addressed:
- Floating Origin
- Terrain Tiles
In Unity, conventionally 3D coordinates are expressed as single-precision floating point (float) units representing meters. Due to the size of the areas being represented in the owsdk, usage of float is insufficient to express object coordinates, and leads to "jitter" of mesh vertices, incorrect collision results, and other oddities.
In order to have the ability to accurately represent the entire globe, the OWSDK instead represents 3D coordinates as double-precision (double) values. In order to convert to 'Unity' coordinates, a relative origin system is used. This is represented by the IRTOcontext (Relative-To-Origin). This interface represents a floating origin for object coordinates to be calculated from.
The IRTOContext simply consists of a Vector3d WorldOrigin, designating the position to which points are relative to, and an event WorldOriginChanged to alert when this point is changed.
The WorldOrigin to be a position close to an area of interest, such as the game's camera.
Note: Care should be taken to not invoke the WorldOriginChanged event too frequently, as often this will cause listeners to re-calculate and move their position in response.
To translate between OWSDK Vector3d to Unity Vector3:
Vector3d worldPos;
IRTOContext rtoContext;
//...
Vector3 unityPos = (worldPos - rtoContext.WorldOrigin).ToVector3();
To translate between Unity Vector3 and OWSDK Vector3d:
Vector3 unityPos;
IRTOContext rtoContext;
//...
Vector3d worldPos = unityPos.ToVector3d() + rtoContext.WorldOrigin;
The OWSDK has the ability to dynamically render globe terrain at various levels of detail by use of a tiling system. The functionality of selecting which tiles are active, and their levels of detail is provided by creating an instance of TerrainTileChunker.
The basic concept of a TerrainTileIndex is based on a simple tile system:
Each tile is identified by three integers: row, column, and lod. Each tile has four children, which represent the top-left, top-right, bottom-left, and bottom-right of the tile at the next level of detail. Levels of detail start at 1 and go as high as necessary to represent the area of interest. Additionally, each tile is further divided into pixels. Within the same LOD, every tile has the same Pixel Width, and Pixel Height. The exact width and height are given by a ITileMapper.
The interface ITileMapper maps terrain tile indices and pixels into Geodetic coordinates. The ITileMapper can be used to "shape" tiles to a specific projection system in order to natively support rasterizing data.
For most usages, the WMSTileMapper is used, in which tiles are laid out using the same tiling system used by Bing, Google Maps, and OpenStreetmap.
The TerrainTileChunker is the class responsible for controlling which terrain tiles are considered active, and their resolution. It controls spawning and placing terrain geometry within the Unity scene.
The TerrainTileChunker is primarily provided with:
A camera - Used to calculate level-of-detail
A ITerrainTileProvider - Used to display imagery on the tiles
A ITileMapper - Used to calculate terrain tile resolution and bounds
A ITileMeshProvider - Used to generate terrain meshes
Additionally it contains quality settings:
The number of tiles to keep in in-memory cache.
The percentage of MaxNumTiles to load during the PreLoad stage.
If true, terrain tiles will contain no colliders.
The maximum number of tiles to load concurrently.
The highest LOD to load. Inclusive.
The highest LOD to use for Physics purposes.
A "budget" to constrain time spent updating tiles each frame, in milliseconds. The chunker may regularly exceed this budget depending on load.
The size, in tiles, of each texture atlas used by the tiles.
The TerrainTileChunker makes use of texture atlases to reduce the number of SetPass calls during the Unity rendering phase.
The section on floating origin is required reading.
There are various ways to position an object in the OWSDK.
If you have the Geocentric coordinates of an object you wish to place in the world, then placing it is a matter of calculating its Unity position using the WorldOrigin of the IRTOContext. For stationary objects, the MonoBehaviour GeocentricTransform may be used.
When using lat, lon, the process is the same, but additionally requires using the Ellipsoid in use to calculate the Geocentric position. A MonoBehaviour GeoTransform allows setting object position using Geodetic3d coordinates directly. Additionally, GeoTransform will calculate the appropriate Up orientation to establish a local-to-the-ground Up direction for the GameObject.
The settings for the GeoTransform MonoBehaviour are shown above. Don't forget to set the WorldContext on the GeoTransform!
Create a parent GameObject which has the GeoTransform script attached and reset its Position Rotation and Scale. Set the GeoTransform, Latitude, and Longitude.
Then add as a Child of this GameObject, the prefab or object.
Just like in the picture, the GeoTransform is attached to the parent object, named "Tiles". Then the actual tiles are children.
In this way, the Position, Rotation, and Scale of the child tiles could possibly be modified. These transform values are relative to the GeoTransform position.
To transform from a WGS84 coordinate (expressed as lat,lon,elevation):
Geodetic3d wgs84Pos;
Vector3d worldPos = ellipsoid.ToVector3d(wgs84Pos);
Vector3 unityPos = (worldPos - rtoContext.WorldOrigin).ToVector3();
Converting from a WGS84 coordinate to a Unity scene Vector3
And to transform from a Unity scene coordinate to WGS84:
Vector3 unityPos;
Vector3d worldPos = unityPos.ToVector3d() + rtoContext.WorldOrigin;
Geodetic3d wgs84Pos = ellipsoid.ToGeodetic3d(worldPos);
Converting from a Unity Vector3 position to a WGS84 position
To configure elevation via an SQL database, add an object for elevationProviderSettings.sql
:
"elevationProviderType": "sql",
"elevationProviderSettings": {
"sql": {
"databaseFile": "<PATH-TO-DATABASE-FILE>",
"baseLOD": 13,
"useDownsamples": true
}
}
Note: Querying elevations directly from a SQL file is a bit faster than from Bing, but setting up a local elevations database is required.
Please follow these instructions to setup a local SQL database.
An extension method to Ellipsoid is available to calculate the "NED" (North East Down) rotation of an object as a Unity Quaternion:
Transform unityTransform;
unityTransform.rotation = ellipsoid.NEDRotation(wgs84Coordinate);
If you are experiencing jitters or what seems to be slow performance issues it may be because VSync is turned off. Turning this setting on in Unity's Project Settings under the Quality section will most likely fix this issue. You will get a steady 60 frames per second.
When you build the game, you will need to make sure that the One World SDK Viewer is in a directory in which it can locate the config file.
When the executable is run, it will look for the config file in the location "../../data/OneWorldSDK_Viewer.config.json".
So, for example, if you have your exe built here:
"\build\OneWorldSDK_Viewer\OneWorldSDK_Viewer.exe"
the config file must be in the location:
"\data\OneWorldSDK_Viewer.config.json"
We use Unity's Input Manager to control the globe which makes it easy for you to make your own controls. It can be found by going to Edit -> Project Settings... The axis labled Horizontal is used for moving moving left and right (increasing or decreasing longitude). You can modify the Positive/Negative buttons. Similarly, the UpDown axis is used for decreasing/increasing altitude. The source code that manipulates camera movement is src/OneWorldSDK_UnityDemo/Assets/UnityDemo/Scripts/WorldObjectMovement.cs.
In order to enable VR, go to Edit -> Project Settings and then install XR Plug-in Management. If using Oculus, click on Oculus Plug-in Provider. Then in the OneWorldDemo scene, add the Tracked Pose Driver component to the EyepointCamera GameObject. Also add a Tracked Pose Driver to the SkyboxCamera GameObject. That should be it.
If the sky becomes glitchy, adjusting the near-plane distance in the OneWorldSDK_Viewer.config.json file may fix the issue.
{
"distance": 50000,
"near": 0.3,
"far": 800000
},
Change the near plane from 0.3 to either 0.2 or 0.4.