Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Entity.trackingReferenceFrame #12194

Merged
merged 10 commits into from
Nov 15, 2024
4,010 changes: 4,010 additions & 0 deletions Apps/SampleData/tracking.czml

Large diffs are not rendered by default.

32 changes: 4 additions & 28 deletions Apps/Sandcastle/gallery/Callback Position Property.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
}

// Create the entity and bind its position to the callback position property
viewer.entities.add({
const entity = viewer.entities.add({
availability: new Cesium.TimeIntervalCollection([
new Cesium.TimeInterval({
start: start,
Expand All @@ -158,35 +158,11 @@
leadTime: 1,
trailTime: 0.1,
},
trackingReferenceFrame: Cesium.TrackingReferenceFrame.INERTIAL,
viewFrom: new Cesium.Cartesian3(-100, 0, 10),
});

const camera = viewer.camera;
const scene = viewer.scene;

const scratchPosition = new Cesium.Cartesian3();
const scratchOrientation = new Cesium.Quaternion();
const scratchTransform = new Cesium.Matrix4();
const offset = new Cesium.Cartesian3(-100, 0, 10);

// Update camera to follow entity's position and orientation
viewer.clock.onTick.addEventListener(function (clock) {
if (scene.mode === Cesium.Scene.MORPHING) {
return;
}
const time = clock.currentTime;
const entityPosition = position.getValue(time, scratchPosition);
const entityOrientation = orientation.getValue(time, scratchOrientation);
if (entityPosition === undefined || entityOrientation === undefined) {
return;
}
const transform = Cesium.Matrix4.fromTranslationQuaternionRotationScale(
entityPosition,
entityOrientation,
Cesium.Cartesian3.ONE,
scratchTransform,
);
camera.lookAtTransform(transform, offset);
});
viewer.trackedEntity = entity;
//Sandcastle_End
};
if (typeof Cesium !== "undefined") {
Expand Down
110 changes: 110 additions & 0 deletions Apps/Sandcastle/gallery/Entity tracking.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<meta
name="description"
content="Apply different tracking reference frames to tracked entities."
/>
<meta name="cesium-sandcastle-labels" content="Beginner, Showcases" />
<title>Cesium Demo</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script
type="text/javascript"
src="../../../Build/CesiumUnminified/Cesium.js"
nomodule
></script>
<script type="module" src="../load-cesium-es6.js"></script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar"></div>
<script id="cesium_sandcastle_script">
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
// This example illustrates the possible tracking reference frames
// apllied to two different entities: a near surface slow moving
// object and a satellite
const viewer = new Cesium.Viewer("cesiumContainer", {
terrain: Cesium.Terrain.fromWorldTerrain(),
shouldAnimate: true,
});

const startTime = Cesium.JulianDate.fromIso8601("2012-03-15T10:00:00Z");

const satelliteStopTime = Cesium.JulianDate.fromIso8601("2012-03-16T10:00:00Z");

const droneStopTime = Cesium.JulianDate.fromIso8601("2012-03-15T10:00:30Z");

const dataSource = await viewer.dataSources.add(
Cesium.CzmlDataSource.load("../../SampleData/tracking.czml"),
);

const satellite = dataSource.entities.getById("Satellite/ISS");
const drone = dataSource.entities.getById("CesiumDrone");

satellite.viewFrom = new Cesium.Cartesian3(-300, 20, 100);
drone.viewFrom = new Cesium.Cartesian3(-50, 0, 5);

Sandcastle.addDefaultToolbarButton("Satellites", function () {
viewer.clock.stopTime = satelliteStopTime;
viewer.clock.currentTime = startTime;
viewer.clock.multiplier = 30;
viewer.timeline.zoomTo(startTime, satelliteStopTime);
viewer.trackedEntity = satellite;
});

Sandcastle.addToolbarButton("Drone", function () {
viewer.clock.stopTime = droneStopTime;
viewer.clock.currentTime = startTime;
viewer.clock.multiplier = 1;
viewer.timeline.zoomTo(startTime, droneStopTime);
viewer.trackedEntity = drone;
});

Sandcastle.addToolbarMenu([
{
text: "Tracking reference frame: Auto-detect",
onselect: function () {
satellite.trackingReferenceFrame = Cesium.TrackingReferenceFrame.AUTODETECT;
drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.AUTODETECT;
},
},
{
text: "Tracking reference frame: Inertial",
onselect: function () {
satellite.trackingReferenceFrame = Cesium.TrackingReferenceFrame.INERTIAL;
drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.INERTIAL;
},
},
{
text: "Tracking reference frame: Velocity",
onselect: function () {
satellite.trackingReferenceFrame = Cesium.TrackingReferenceFrame.VELOCITY;
drone.trackingReferenceFrame = Cesium.TrackingReferenceFrame.VELOCITY;
},
},
]);
//Sandcastle_End
};
if (typeof Cesium !== "undefined") {
window.startupCalled = true;
window.startup(Cesium).catch((error) => {
"use strict";
console.error(error);
});
Sandcastle.finishedLoading();
}
</script>
</body>
</html>
Binary file added Apps/Sandcastle/gallery/Entity tracking.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Apps/Sandcastle/gallery/Interpolation.html
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@
viewer.trackedEntity = entity;
});

Sandcastle.addToolbarMenu([
{
text: "Tracking reference frame: East-North-Up",
onselect: function () {
entity.trackingReferenceFrame = Cesium.TrackingReferenceFrame.EAST_NORTH_UP;
},
},
{
text: "Tracking reference frame: Inertial",
onselect: function () {
entity.trackingReferenceFrame = Cesium.TrackingReferenceFrame.INERTIAL;
},
},
]);

//Add a combo box for selecting each interpolation mode.
Sandcastle.addToolbarMenu(
[
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

#### @cesium/engine

##### Additions :tada:

- Added `Entity.trackingReferenceFrame` property to allow tracking entities in their own inertial reference frame. [#12194](https://github.com/CesiumGS/cesium/pull/12194)

##### Fixes :wrench:

- Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301)
Expand Down
40 changes: 40 additions & 0 deletions packages/engine/Source/Core/TrackingReferenceFrame.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Constants for identifying well-known tracking reference frames.
*
* @enum {number}
*/
const TrackingReferenceFrame = {
/**
* Auto-detect algorithm. The reference frame used to track the Entity will
* be automatically selected based on its trajectory: near-surface slow moving
* objects will be tracked in the entity's local east-north-up reference
* frame, while faster objects like satellites will use VVLH (Vehicle Velocity,
* Local Horizontal).
*
* @type {number}
* @constant
*/
AUTODETECT: 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many of our enums use NONE for an undefined or default state. Does that make sense here>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the enum with a NONE value I've seen are reflecting something meaningful:

  • ArcType.NONE
  • ExtrapolationType.NONE
  • Visibility.NONE
  • HeightReference.NONE
  • ...
    I'm not sure a TrackingReferenceFrame.NONE value would be of any interest. This would represent an invalid value that should be tested against, and add more noise to the code IMO.
    Unless you would prefer me to replace the TrackingReferenceFrame.AUTODETECT by TrackingReferenceFrame.NONE?


/**
* The entity's inertial reference frame. If entity has no defined orientation
* property, a {@link VelocityOrientationProperty} is used instead, thus
* falling back to <code>TrackingReferenceFrame.VELOCITY</code>.
* When selected, the auto-detect algorithm is overridden.
*
* @type {number}
* @constant
*/
INERTIAL: 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to fall back to autodetect in the case where an entity has no defined orientation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree on that point, as we already have TrackingReferenceFrame.VELOCITY. I'll change that!


/**
* The entity's inertial reference frame with orientation fixed to its
* {@link VelocityOrientationProperty}, ignoring its own orientation.
* When selected, the auto-detect algorithm is overridden.
*
* @type {number}
* @constant
*/
VELOCITY: 2,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when an entity's velocity is 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mentioned in #8900: The issue is that if the vehicle stops, the orientation becomes undefined, causing an abrupt change in the vehicle's orientation
The behavior will be the same here, causing the camera position to change abruptly following the entity's orientation

};
export default Object.freeze(TrackingReferenceFrame);
16 changes: 15 additions & 1 deletion packages/engine/Source/DataSources/Entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CesiumMath from "../Core/Math.js";
import Matrix3 from "../Core/Matrix3.js";
import Matrix4 from "../Core/Matrix4.js";
import Quaternion from "../Core/Quaternion.js";
import TrackingReferenceFrame from "../Core/TrackingReferenceFrame.js";
import Transforms from "../Core/Transforms.js";
import GroundPolylinePrimitive from "../Scene/GroundPolylinePrimitive.js";
import GroundPrimitive from "../Scene/GroundPrimitive.js";
Expand Down Expand Up @@ -73,8 +74,9 @@ function createPropertyTypeDescriptor(name, Type) {
* @property {string} [name] A human readable name to display to users. It does not have to be unique.
* @property {TimeIntervalCollection} [availability] The availability, if any, associated with this object.
* @property {boolean} [show] A boolean value indicating if the entity and its children are displayed.
* @property {TrackingReferenceFrame} [trackingReferenceFrame=TrackingReferenceFrame.AUTODETECT] The reference frame used when this entity is being tracked. <br/> If <code>undefined</code>, reference frame is determined based on entity velocity: near-surface slow moving entities are tracked using the local east-north-up reference frame, whereas fast moving entities such as satellites are tracked using VVLH (Vehicle Velocity, Local Horizontal).
* @property {Property | string} [description] A string Property specifying an HTML description for this entity.
* @property {PositionProperty | Cartesian3 | CallbackProperty} [position] A Property specifying the entity position.
* @property {PositionProperty | Cartesian3 | CallbackPositionProperty} [position] A Property specifying the entity position.
* @property {Property | Quaternion} [orientation=Transforms.eastNorthUpToFixedFrame(position)] A Property specifying the entity orientation in respect to Earth-fixed-Earth-centered (ECEF). If undefined, east-north-up at entity position is used.
* @property {Property | Cartesian3} [viewFrom] A suggested initial offset for viewing this object.
* @property {Entity} [parent] A parent entity to associate with this entity.
Expand Down Expand Up @@ -122,6 +124,10 @@ function Entity(options) {
this._definitionChanged = new Event();
this._name = options.name;
this._show = defaultValue(options.show, true);
this._trackingReferenceFrame = defaultValue(
options.trackingReferenceFrame,
TrackingReferenceFrame.AUTODETECT,
);
this._parent = undefined;
this._propertyNames = [
"billboard",
Expand Down Expand Up @@ -296,6 +302,14 @@ Object.defineProperties(Entity.prototype, {
this._definitionChanged.raiseEvent(this, "show", value, !value);
},
},
/**
* Gets or sets the entity's tracking reference frame.
* @demo {@link https://sandcastle.cesium.com/index.html?src=Entity tracking.html|Cesium Sandcastle Entity tracking Demo}
*
* @memberof Entity.prototype
* @type {TrackingReferenceFrame}
*/
trackingReferenceFrame: createRawPropertyDescriptor("trackingReferenceFrame"),
/**
* Gets whether this entity is being displayed, taking into account
* the visibility of any ancestor entities.
Expand Down
51 changes: 50 additions & 1 deletion packages/engine/Source/DataSources/EntityView.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import JulianDate from "../Core/JulianDate.js";
import CesiumMath from "../Core/Math.js";
import Matrix3 from "../Core/Matrix3.js";
import Matrix4 from "../Core/Matrix4.js";
import Quaternion from "../Core/Quaternion.js";
import TrackingReferenceFrame from "../Core/TrackingReferenceFrame.js";
import Transforms from "../Core/Transforms.js";
import SceneMode from "../Scene/SceneMode.js";
import VelocityVectorProperty from "./VelocityVectorProperty.js";

const updateTransformMatrix3Scratch1 = new Matrix3();
const updateTransformMatrix3Scratch2 = new Matrix3();
Expand All @@ -21,6 +24,9 @@ const updateTransformCartesian3Scratch3 = new Cartesian3();
const updateTransformCartesian3Scratch4 = new Cartesian3();
const updateTransformCartesian3Scratch5 = new Cartesian3();
const updateTransformCartesian3Scratch6 = new Cartesian3();
const updateTransformOrientationScratch = new Quaternion();
const velocityScratch = new Cartesian3();
const rotationScratch = new Matrix3();
const deltaTime = new JulianDate();
const northUpAxisFactor = 1.25; // times ellipsoid's maximum radius

Expand All @@ -30,6 +36,9 @@ function updateTransform(
updateLookAt,
saveCamera,
positionProperty,
velocityProperty,
orientationProperty,
trackingReferenceFrame,
time,
ellipsoid,
) {
Expand Down Expand Up @@ -228,7 +237,39 @@ function updateTransform(
}

const transform = updateTransformMatrix4Scratch;
if (hasBasis) {

let orientation;
if (defined(orientationProperty)) {
orientation = orientationProperty.getValue(
time,
updateTransformOrientationScratch,
);
}

const velocity = velocityProperty.getValue(time, velocityScratch);

if (
trackingReferenceFrame === TrackingReferenceFrame.INERTIAL &&
defined(orientation)
) {
Matrix4.fromTranslationQuaternionRotationScale(
cartesian,
orientation,
Cartesian3.ONE,
transform,
);
} else if (
trackingReferenceFrame === TrackingReferenceFrame.VELOCITY &&
defined(velocity)
) {
const rotation = Transforms.rotationMatrixFromPositionVelocity(
cartesian,
velocity,
ellipsoid,
rotationScratch,
);
Matrix4.fromRotationTranslation(rotation, cartesian, transform);
} else if (hasBasis) {
transform[0] = xBasis.x;
transform[1] = xBasis.y;
transform[2] = xBasis.z;
Expand Down Expand Up @@ -316,6 +357,8 @@ function EntityView(entity, scene, ellipsoid) {
this._lastCartesian = new Cartesian3();
this._defaultOffset3D = undefined;

this._velocityProperty = new VelocityVectorProperty(entity.position, true);

this._offset3D = new Cartesian3();
}

Expand Down Expand Up @@ -362,10 +405,13 @@ EntityView.prototype.update = function (time, boundingSphere) {
}

const entity = this.entity;
const trackingReferenceFrame = entity.trackingReferenceFrame;
const positionProperty = entity.position;
if (!defined(positionProperty)) {
return;
}
const velocityProperty = this._velocityProperty;
const orientationProperty = entity.orientation;
const objectChanged = entity !== this._lastEntity;
const sceneModeChanged = sceneMode !== this._mode;

Expand Down Expand Up @@ -419,6 +465,9 @@ EntityView.prototype.update = function (time, boundingSphere) {
updateLookAt,
saveCamera,
positionProperty,
velocityProperty,
orientationProperty,
trackingReferenceFrame,
time,
ellipsoid,
);
Expand Down
Loading