using Reinterop;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Mathematics;
using UnityEngine;
namespace CesiumForUnity
{
///
/// Specifies the source of a Cesium dataset.
///
public enum CesiumDataSource
{
///
/// The dataset is from Cesium ion.
///
FromCesiumIon,
///
/// The dataset is from a regular web URL.
///
FromUrl,
///
/// The dataset is generated from the georeference ellipsoid.
///
FromEllipsoid
}
///
/// A tileset in the 3D Tiles format. [3D Tiles](https://github.com/CesiumGS/3d-tiles)
/// is an open specification for sharing, visualizing, fusing, and interacting with massive
/// heterogenous 3D geospatial content across desktop, web, and mobile applications. The tileset is
/// streamed incrementally into Unity based on the current camera view(s).
///
///
///
/// A GameObject with this component must be nested inside one with a
/// component. The georeference controls how this tileset is mapped into the Unity world.
///
///
/// In most cases, the Transform of the GameObject that contains this component, and its ancestors, should
/// be an identity transform: 0 position, 0 rotation, 1 scale. Otherwise, this tileset will be misaligned
/// with other globe tilesets. However, it is sometimes useful to purposely offset a tileset.
///
///
[ExecuteInEditMode]
[ReinteropNativeImplementation("CesiumForUnityNative::Cesium3DTilesetImpl", "Cesium3DTilesetImpl.h")]
[AddComponentMenu("Cesium/Cesium 3D Tileset")]
[IconAttribute("Packages/com.cesium.unity/Editor/Resources/Cesium-24x24.png")]
public partial class Cesium3DTileset : MonoBehaviour, IDisposable, ISerializationCallbackReceiver
{
public void Dispose()
{
this.OnDisable();
this.DisposeImplementation();
}
#region Events and Delegates
///
/// Encapsulates a method that receives details of a tileset load failure.
///
/// The details of the load failure.
public delegate void TilesetLoadFailureDelegate(
Cesium3DTilesetLoadFailureDetails details);
///
/// An event that is raised when the tileset encounters an error that prevents it from loading.
///
public static event TilesetLoadFailureDelegate OnCesium3DTilesetLoadFailure;
internal static void
BroadcastCesium3DTilesetLoadFailure(Cesium3DTilesetLoadFailureDetails details)
{
if (OnCesium3DTilesetLoadFailure != null)
{
OnCesium3DTilesetLoadFailure(details);
}
}
///
/// Occurs when a new GameObject is instantiated for a Tile in the tileset.
///
///
/// This event can be used to customize the Tile GameObjects as they are loaded,
/// such as adding components, changing materials, or applying transformations.
///
public event Action OnTileGameObjectCreated;
internal void BroadcastNewGameObjectCreated(GameObject go)
{
if (OnTileGameObjectCreated != null)
{
OnTileGameObjectCreated(go);
}
}
internal static event Action OnSetShowCreditsOnScreen;
#endregion
#region Fields
[SerializeField]
private bool _showCreditsOnScreen = false;
///
/// Whether or not to force this tileset's credits to be shown on the main screen. If false, the
/// credits are usually only shown on a "Data Attribution" popup.
///
public bool showCreditsOnScreen
{
get => this._showCreditsOnScreen;
set
{
this._showCreditsOnScreen = value;
this.SetShowCreditsOnScreen(this._showCreditsOnScreen);
if (Cesium3DTileset.OnSetShowCreditsOnScreen != null)
{
Cesium3DTileset.OnSetShowCreditsOnScreen();
}
}
}
[SerializeField]
private CesiumDataSource _tilesetSource = CesiumDataSource.FromCesiumIon;
///
/// The source of the data for this tileset: Cesium ion or a regular URL.
///
public CesiumDataSource tilesetSource
{
get => this._tilesetSource;
set
{
this._tilesetSource = value;
this.RecreateTileset();
}
}
[SerializeField]
private string _url = "";
///
/// The URL from which to load the tileset. This property is used only if
/// is set to "FromUrl".
///
public string url
{
get => this._url;
set
{
this._url = value;
this.RecreateTileset();
}
}
[SerializeField]
private long _ionAssetID = 0;
///
/// The Cesium ion asset ID from which to load the tileset. This property is used
/// only if is set to "FromCesiumIon".
///
public long ionAssetID
{
get => this._ionAssetID;
set
{
this._ionAssetID = value;
this.RecreateTileset();
}
}
[SerializeField]
private string _ionAccessToken = "";
///
/// The Cesium ion access token to use when loading the tileset from Cesium ion. This property is used
/// only if is set to "FromCesiumIon".
///
public string ionAccessToken
{
get => this._ionAccessToken;
set
{
this._ionAccessToken = value;
this.RecreateTileset();
}
}
[SerializeField]
private CesiumIonServer _ionServer = null;
///
/// The Cesium ion server from which this tileset is loaded.
///
public CesiumIonServer ionServer
{
get
{
if (this._ionServer == null)
{
#if UNITY_EDITOR
// See OnAfterDeserialize.
if (this._useDefaultServer)
this._ionServer = CesiumIonServer.defaultServer;
else
this._ionServer = CesiumIonServer.serverForNewObjects;
this._useDefaultServer = false;
#else
this._ionServer = CesiumIonServer.serverForNewObjects;
#endif
}
return this._ionServer;
}
set
{
if (value == null) value = CesiumIonServer.serverForNewObjects;
this._ionServer = value;
this.RecreateTileset();
}
}
[SerializeField]
private float _maximumScreenSpaceError = 16.0f;
///
/// The maximum number of pixels of error when rendering this tileset.
///
///
///
/// This is used to select an appropriate level-of-detail: A low value
/// will cause many tiles with a high level of detail to be loaded, causing
/// a finer visual representation of the tiles, but with a higher performance
/// cost for loading and rendering. A higher value will cause a coarser
/// visual representation, with lower performance requirements.
///
///
/// When a tileset uses the older layer.json / quantized-mesh format rather
/// than 3D Tiles, this value is effectively divided by 8.0. So the default
/// value of 16.0 corresponds to the standard value for quantized-mesh
/// terrain of 2.0.
///
///
public float maximumScreenSpaceError
{
get => this._maximumScreenSpaceError;
set
{
this._maximumScreenSpaceError = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _preloadAncestors = true;
///
/// Whether to preload ancestor tiles.
///
///
/// Setting this to true optimizes the zoom-out experience and provides more
/// detail in newly-exposed areas when panning. The down side is that it
/// requires loading more tiles.
///
public bool preloadAncestors
{
get => this._preloadAncestors;
set
{
this._preloadAncestors = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _preloadSiblings = true;
///
/// Whether to preload sibling tiles.
///
///
/// Setting this to true causes tiles with the same parent as a rendered
/// tile to be loaded, even if they are culled. Setting this to true may
/// provide a better panning experience at the cost of loading more tiles.
///
public bool preloadSiblings
{
get => this._preloadSiblings;
set
{
this._preloadSiblings = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _forbidHoles = false;
///
/// Whether to prevent refinement of a parent tile when a child isn't done loading.
///
///
/// When this is set to true, the tileset will guarantee that the tileset will
/// never be rendered with holes in place of tiles that are not yet loaded,
/// even though the tile that is rendered instead may have low resolution.
/// When false, overall loading will be faster, but newly-visible parts of the
/// tileset may initially be blank.
///
public bool forbidHoles
{
get => this._forbidHoles;
set
{
this._forbidHoles = value;
this.RecreateTileset();
}
}
[SerializeField]
private uint _maximumSimultaneousTileLoads = 20;
///
/// The maximum number of tiles that may be loaded simultaneously.
///
///
/// When new parts of the tileset become visible, the tasks to load the
/// corresponding tiles are put into a queue. This value determines how
/// many of these tasks are processed at the same time. A higher value may
/// cause the tiles to be loaded and rendered more quickly, at the cost of
/// a higher network and processing load.
///
public uint maximumSimultaneousTileLoads
{
get => this._maximumSimultaneousTileLoads;
set
{
this._maximumSimultaneousTileLoads = value;
this.RecreateTileset();
}
}
[SerializeField]
private long _maximumCachedBytes = 512 * 1024 * 1024;
///
/// The maximum number of bytes that may be cached for this tileset.
///
///
/// Note that this value, even if 0, will never cause tiles that are needed
/// for rendering to be unloaded. However, if the total number of loaded
/// bytes is greater than this value, tiles will be unloaded until the
/// total is under this number or until only required tiles remain, whichever
/// comes first.
///
public long maximumCachedBytes
{
get => this._maximumCachedBytes;
set
{
this._maximumCachedBytes = value;
this.RecreateTileset();
}
}
[SerializeField]
private uint _loadingDescendantLimit = 20;
///
/// The number of loading descendents a tile should allow before deciding to render itself instead of waiting.
///
///
/// Setting this to 0 will cause each level of detail to be loaded
/// successively. This will increase the overall loading time, but cause
/// additional detail to appear more gradually. Setting this to a high value
/// like 1000 will decrease the overall time until the desired level of detail
/// is achieved, but this high-detail representation will appear at once, as
/// soon as it is loaded completely.
///
public uint loadingDescendantLimit
{
get => this._loadingDescendantLimit;
set
{
this._loadingDescendantLimit = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _enableFrustumCulling = true;
///
/// Whether to cull tiles that are outside the frustum.
///
///
///
/// By default this is true, meaning that tiles that are not visible with
/// the current camera configuration will be ignored. It can be set to false,
/// so that these tiles are still considered for loading, refinement and rendering.
///
///
/// This will cause more tiles to be loaded, but helps to avoid holes and
/// provides a more consistent mesh, which may be helpful for physics and shadows.
///
///
/// Note that frustum calling will be disabled if is set to true.
///
///
public bool enableFrustumCulling
{
get => this._enableFrustumCulling;
set
{
this._enableFrustumCulling = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _enableFogCulling = true;
///
/// Whether to cull tiles that are occluded by fog.
///
///
///
/// This does not refer to the atmospheric fog rendered by Unity, but to an
/// internal representation of fog: Depending on the height of the camera
/// above the ground, tiles that are far away (close to the horizon) will be
/// culled when this flag is enabled.
///
///
/// Note that this will always be disabled if is set to true.
///
///
public bool enableFogCulling
{
get => this._enableFogCulling;
set
{
this._enableFogCulling = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _enforceCulledScreenSpaceError = true;
///
/// Whether a specified screen-space error should be enforced for tiles
/// that are outside the frustum or hidden in fog.
///
///
///
/// When and are both true,
/// tiles outside the view frustum or hidden in fog are effectively ignored,
/// and so their level-of-detail doesn't matter. And in this scenario, this
/// property is ignored.
///
///
/// However, when either of those flags are false, these "would-be-culled"
/// tiles continue to be processed, and the question arises of how to handle
/// their level-of-detail. When this property is false, refinement terminates
/// at these tiles, no matter what their current screen-space error. The tiles
/// are available for physics, shadows, etc., but their level-of-detail may be
/// very low.
///
///
/// When set to true, these tiles are refined until they achieve the specified
/// . This allows control over the minimum quality
/// of these would-be-culled tiles.
///
///
public bool enforceCulledScreenSpaceError
{
get => this._enforceCulledScreenSpaceError;
set
{
this._enforceCulledScreenSpaceError = value;
this.RecreateTileset();
}
}
[SerializeField]
private float _culledScreenSpaceError = 64.0f;
///
/// The screen-space error to be enforced for tiles that are outside the frustum or hidden in fog.
///
///
///
/// When and are both true,
/// tiles outside the view frustum or hidden in fog are effectively ignored,
/// and so their level-of-detail doesn't matter. And in this scenario, this
/// property is ignored.
///
///
/// However, when either of those flags are false, these \"would-be-culled\"
/// tiles continue to be processed, and the question arises of how to handle
/// their level-of-detail. When this property is false, refinement terminates
/// at these tiles, no matter what their current screen-space error. The tiles
/// are available for physics, shadows, etc., but their level-of-detail may be
/// very low.
///
///
/// When set to true, these tiles are refined until they achieve the specified
/// "Culled Screen Space Error". This allows control over the minimum quality
/// of these would-be-culled tiles.
///
///
public float culledScreenSpaceError
{
get => this._culledScreenSpaceError;
set
{
this._culledScreenSpaceError = value;
this.RecreateTileset();
}
}
[SerializeField]
private Material _opaqueMaterial = null;
///
/// The Material to use to render opaque parts of tiles.
///
public Material opaqueMaterial
{
get => this._opaqueMaterial;
set
{
this._opaqueMaterial = value;
this.RecreateTileset();
}
}
//[SerializeField]
//private bool _useLodTransitions = false;
//public bool useLodTransitions
//{
// get => this._useLodTransitions;
// set
// {
// this._useLodTransitions = value;
// this.RecreateTileset();
// }
//}
//[SerializeField]
//private float _lodTransitionLength = 0.5f;
//public float lodTransitionLength
//{
// get => this._lodTransitionLength;
// set
// {
// this._lodTransitionLength = value;
// this.RecreateTileset();
// }
//}
[SerializeField]
private bool _generateSmoothNormals = false;
///
/// Whether to generate smooth normals when normals are missing in the glTF.
///
///
/// According to the glTF spec: "When normals are not specified, client
/// implementations should calculate flat normals." However, calculating flat
/// normals requires duplicating vertices. This option allows the glTFs to be rendered
/// with smooth normals instead when the original glTF is missing normals.
///
public bool generateSmoothNormals
{
get => this._generateSmoothNormals;
set
{
this._generateSmoothNormals = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _ignoreKhrMaterialsUnlit = false;
///
/// Whether to ignore the KHR_materials_unlit extension on the glTF tiles in
/// this tileset, if it exists
///
///
/// Whether to ignore the KHR_materials_unlit extension on the glTF tiles in
/// this tileset, if it exists, and instead render with standard lighting and
/// shadows. This property will have no effect if the tileset does not have any
/// tiles that use this extension.
///
/// The KHR_materials_unlit extension is often applied to photogrammetry
/// tilesets because lighting and shadows are already baked into their
/// textures.
///
public bool ignoreKhrMaterialsUnlit
{
get => this._ignoreKhrMaterialsUnlit;
set
{
this._ignoreKhrMaterialsUnlit = value;
this.RecreateTileset();
}
}
[SerializeField]
private CesiumPointCloudShading _pointCloudShading = new CesiumPointCloudShading();
///
/// The CesiumPointCloudShading attached to this tileset. If the tileset
/// contains points, their appearance can be configured with the point
/// cloud shading parameters.
///
public CesiumPointCloudShading pointCloudShading
{
get => this._pointCloudShading;
}
[SerializeField]
private bool _suspendUpdate = false;
///
/// Pauses level-of-detail and culling updates of this tileset.
///
public bool suspendUpdate
{
get => this._suspendUpdate;
set
{
this._suspendUpdate = value;
}
}
// Normally tilesets are destroyed when anything in the editor changes.
// But if suspendUpdate is the only value that has changed, the tileset
// should not be reloaded, and instead continue updating after the setting
// has been toggled. This variable saves the last value of suspendUpdate,
// so OnValidate() can determine if this property was modified. If so, it
// prevents the tileset from being destroyed.
private bool _previousSuspendUpdate = false;
internal bool previousSuspendUpdate
{
get => this._previousSuspendUpdate;
set
{
this._previousSuspendUpdate = value;
}
}
[SerializeField]
private bool _showTilesInHierarchy = false;
///
/// Whether to show tiles as individual game objects in the hierarchy window.
///
public bool showTilesInHierarchy
{
get => this._showTilesInHierarchy;
set
{
this._showTilesInHierarchy = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _updateInEditor = true;
///
/// If true, this tileset is ticked/updated in the editor. If false, it is only ticked while playing (including Play-in-Editor).
///
public bool updateInEditor
{
get => this._updateInEditor;
set
{
this._updateInEditor = value;
this.RecreateTileset();
}
}
[SerializeField]
private bool _logSelectionStats = false;
///
/// Whether to log details about the tile selection process.
///
public bool logSelectionStats
{
get => this._logSelectionStats;
set { this._logSelectionStats = value; }
}
[SerializeField]
private bool _createPhysicsMeshes = true;
///
/// Whether to generate physics meshes for this tileset.
///
///
/// Disabling this option will improve the performance of tile loading,
/// but it will no longer be possible to collide with the tileset since
/// the physics meshes will not be created.
///
public bool createPhysicsMeshes
{
get => this._createPhysicsMeshes;
set
{
this._createPhysicsMeshes = value;
this.RecreateTileset();
}
}
#endregion
#region Public Methods
///
/// Estimate the percentage of the tiles for the current view that have been loaded.
///
///
/// A float value between 0 and 100 representing the load progress.
///
public partial float ComputeLoadProgress();
///
/// Destroy and recreate the tilset. All tiles are unloaded, and then the tileset is reloaded
/// based on the current view.
///
public partial void RecreateTileset();
///
/// Zoom the Editor camera to this tileset. This method does nothing outside of the Editor.
///
public partial void FocusTileset();
///
/// Initiates an asynchronous query for the height of this tileset at a list of
/// cartographic positions, where the longitude (X) and latitude (Y) are given in degrees.
/// The most detailed available tiles are used to determine each height.
///
///
///
/// The height of the input positions is ignored, unless height sampling fails
/// at that location. The output height is expressed in meters above the ellipsoid
/// (usually WGS84), which should not be confused with a height above mean sea level.
///
///
/// Use inside a coroutine to wait for the asynchronous height
/// query to complete.
///
///
///
/// The cartographic positions for which to sample heights. The X component is the
/// Longitude (degrees), the Y component is the Latitude (degrees), and the Z component
/// is the Height (meters).
///
/// An asynchronous task that will provide the requested heights when complete.
public partial Task SampleHeightMostDetailed(params double3[] longitudeLatitudeHeightPositions);
#endregion
#region Private Methods
private partial void SetShowCreditsOnScreen(bool value);
private partial void Start();
private partial void Update();
private partial void OnValidate();
private partial void OnEnable();
private partial void OnDisable();
internal partial void UpdateOverlayMaterialKeys();
#endregion
#region Backward Compatibility
void ISerializationCallbackReceiver.OnBeforeSerialize()
{
}
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
#if UNITY_EDITOR
// For backward compatibility, tilesets loaded without a server should adopt
// the default one rather than the current one.
if (this._ionServer == null)
this._useDefaultServer = true;
#endif
}
#if UNITY_EDITOR
private bool _useDefaultServer = false;
#endif
#endregion
}
}