-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Fixes issue #12465 (Entity Tracking broken) #12467
base: main
Are you sure you want to change the base?
Conversation
Thank you for the pull request, @jfayot! ✅ We can confirm we have a CLA on file for you. |
/** | ||
* The bounding sphere of the object. | ||
* @type {BoundingSphere} | ||
*/ | ||
this.boundingSphere = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why this was removed? More than it just works now? The correlation between this and the scratch value and the entities actual bounding sphere all still feels a little funky to me.
If we do still want to remove this we will need to go through the deprecation process as this is part of the public API and people may be relying on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no answer for you @jjspace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jjspace I recognize that I have no idea why "it just works now!"
Even this bounding sphere parameter inside EntityView.update
looks weird: why would you offer this as a parameter when there is no way to get a bounding sphere from an Entity
in the public API?
In #3954, the bounding sphere was moved from EntityView
constructor to update
method, but again, why would you impose to provide a bounding sphere to EntityView
(which is related to the high level Entity API) when you don't have the ability to get this bounding sphere from Entity's high level API?
Before #3954, the bounding sphere parameter in the constructor was documented as:
@param {BoundingSphere} [boundingSphere] An initial bounding sphere for setting the default view.
This "initial" aspect has completely disappeared. My guess is that it should be restored, AND that the Entity's bounding sphere should be computed from inside the EntityView
implementation and should disappear from the public API.
Otherwise provide a public Entity.computeBoundingSphere
method...
@jfayot I'm sorry this took so long to get back to. It turned out to be a bit of a "land mine" in the code for me to try (read: struggle) to wrap my head around... I did some digging into this to try and understand it more completely. First I'll say this whole sequence of functions is a total mess imo with way to many "hard to follow" side effects. Ideally it'd be nice to do a refactor pass on all this camera tracking logic but that's way to big for this PR right now (the camera system itself needs some love too) Sorry this turned into a wall of text, I tried to condense but it's important context that I think needs written down somewhere The first important thing to realize is that The If Given all that, an alternative fix for this that works for all the provided examples without changing the const boundingSphereScratch = new BoundingSphere();
/**
* @private
*/
CesiumWidget.prototype._onTick = function (clock) {
const time = clock.currentTime;
const isUpdated = this._dataSourceDisplay.update(time);
if (this._allowDataSourcesToSuspendAnimation) {
this._canAnimateUpdateCallback(isUpdated);
}
const entityView = this._entityView;
if (defined(entityView)) {
const trackedEntity = this._trackedEntity;
const trackedState = this._dataSourceDisplay.getBoundingSphere(
trackedEntity,
false,
entityView.boundingSphere ?? boundingSphereScratch,
);
if (trackedState === BoundingSphereState.DONE) {
entityView.update(time, undefined);
}
}
}; This is a more minimal code change. However, in some ways this feels pretty bad too and requires understanding all the interactions in how we're calling I think removing Alternatively we could keep PS: this is one of my top priorities to hopefully still get it in for the release on Monday. I'll be able to take a look over the weekend too to try and keep it moving without more delays on my part |
Thanks for the analysis @jjspace !
I'm not sure to understand this: is it the call |
@jfayot Yes that's where I meant. We really only care about the return value from that function. We need it to update the passed in
This is one of the things that makes this whole sequence of code so confusing. If you actually follow the code through the way it's run the call to |
Yes you're right, the bounding sphere parameter doesn't change anything! Am I getting it right? |
@jfayot This code still does something when the |
@jjspace I get your point now ! However, while testing the different code path, I'm convinced that the call to CesiumWidget.prototype._onTick = function (clock) {
const time = clock.currentTime;
const isUpdated = this._dataSourceDisplay.update(time);
if (this._allowDataSourcesToSuspendAnimation) {
this._canAnimateUpdateCallback(isUpdated);
}
const entityView = this._entityView;
if (defined(entityView)) {
entityView.update(time);
}
}; While, off course, keeping the bounding sphere call and parameter in function const bs =
state !== BoundingSphereState.FAILED
? trackedEntityBoundingSphereScratch
: undefined;
widget._entityView = new EntityView(trackedEntity, scene, scene.ellipsoid);
widget._entityView.update(currentTime, bs); Concerning the deprecation of
/**
* The bounding sphere of the object.
* @type {BoundingSphere}
* @deprecated This property has been deprecated and will be removed in Cesium 1.xxx
*/
this.boundingSphere = undefined; ... or should I make move this to Object.defineProperties(EntityView, {
/**
* The bounding sphere of the object.
* @type {BoundingSphere}
* @deprecated This property has been deprecated and will be removed in Cesium 1.xxx
*/
boundingSphere: {
get: function () {
deprecationWarning(
"EntityView.boundingSphere",
"EntityView.boundingSphere has been deprecated and will be removed in Cesium 1.xxx",
);
return this._boundingSphere;
},
set: function (boundingSphere) {
deprecationWarning(
"EntityView.boundingSphere",
"EntityView.boundingSphere has been deprecated and will be removed in Cesium 1.xxx",
);
this._boundingSphere = boundingSphere;
},
},
}); |
@jfayot I think it might still be needed simply to make the check against the
Please also remember to update the specs to cover these use cases so we can remain confident the tracking is working as expected in all cases |
@jfayot is that the local docs on this branch where you already removed the property? If so then that's why it's not there! Check the hosted version or locally in |
Just a wrong jsdoc implementation on my side... sorry for that! |
@jjspace this should be ready for another review pass (although still the same test failures) |
Thanks for the changes @jfayot, I think these look good now. I can confirm all 3 of the test sandcastle's we've seen issues with are still working as expected and I tested a handful of other tracked entity instances as well. @ggetz would you be able to do some testing on this as well? Specifically around any other entity tracking cases you can think of that aren't in those sandcastles. I think the code is good but would like a second pass on actually running it so we can be sure we didn't introduce another bug that I'm not spotting. |
@@ -372,7 +372,8 @@ const getBoundingSphereBoundingSphereScratch = new BoundingSphere(); | |||
* @param {Entity} entity The entity whose bounding sphere to compute. | |||
* @param {boolean} allowPartial If true, pending bounding spheres are ignored and an answer will be returned from the currently available data. | |||
* If false, the the function will halt and return pending if any of the bounding spheres are pending. | |||
* @param {BoundingSphere} result The bounding sphere onto which to store the result. | |||
* @param {BoundingSphere} [result] The bounding sphere onto which to store the result. | |||
* Result parameter may not be provided if one is only interested into the bounding sphere state. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My one concern here is that this would create a new object even when it's not needed, which then needs to get garbage collected later. That's generally fine if the function is not called too frequently, but I think this could be called once every frame, right?
Would it make sense to have another new function getBoundingSphereState
which can be used without a result parameter, which getBoundingSphere
then uses under the hood?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ggetz, @jjspace I'm not sure to clearly understand your concern: if no result
is passed to DataSourceDisplay.getBoundingSphere
then the test by the end of the function should handle this correctly?
...
boundingSpheres.length = count;
if (defined(result)) {
BoundingSphere.fromBoundingSpheres(boundingSpheres, result);
}
return BoundingSphereState.DONE;
};
What am I missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I missed that. Because our library has the convention that when a result is undefined, a new object is created, I assumed this did the same.
Perhaps only to avoid assumptions like mine, we should still create a new function that matches our convention then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ggetz I understand what you mean.
However, optimizing ONLY the returned value wouldn't be of great benefit IMO.
If you look at the function, you'll see that it loops over the vizualizers of the data source and call another getBoundingSphere on each one.
If you would really like to optimize and save created objects and GC, then this would require you to create getBoundingSphereState methods for each vizualizer...
Then some vizualizers themselves rely on updaters to compute their bounding spheres... so again you have to create getBoundingSphereState on updaters...
I'm not sure all these efforts (and associated regression risk) are worth this PR.
However, in order to match the convention that when a result is undefined, a new object is created, I have duplicated code inside getBoundingSphere to create getBoundingSphereState as suggested.
As you can see, we're still creating objects in this function because of the vizualizers. And I was not able to make getBoundingSphere rely on getBoundingSphereState without breaking too much existing code.
CC @jjspace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure all these efforts (and associated regression risk) are worth this PR.
Thanks @jfayot, I agree. I only wanted to make sure we were being careful about new code added.
The code here, and all of the testing plan looks great to me. I did notice some trouble tracking an entity which is clamped to ground / 3D Tiles on initial load in the Clamp Entities to Ground Sandcastle. It loads so that the camera is underground for me. Does anyone else see the same? |
This comment was marked as off-topic.
This comment was marked as off-topic.
If I'm the only one seeing that problem for whatever reason, I can open a separate issue and we can merge this! |
@ggetz I can see it too... |
@ggetz I see that problem on this branch but not |
I see it too, I was not set up correctly before |
@jjspace I know you won't like it, but it looks like this |
#12495 changed handling asynchronous updates for billboards and labels. Perhaps it "fixed" something related to timing here? |
Given there is still some uncertainty here, let's hold off on getting this into the release today. Let's make sure we're fixing this once and for all. 👍 |
@ggetz @jfayot I believe the issue is that If you switch to I think this means we can't deprecate the |
@ggetz I've tested this branch rebased on top of 1.126 (so without #12495) and I still can see the camera going underground. I do agree with your analysis @jjspace, we can clearly see the camera jumping quickly to the new entity's bounding sphere. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes @jfayot, sorry this couldn't make it out with the release yesterday. I can confirm the 3 previous sandcastle examples and the clamping one are all now working correctly as expected so I think this is probably working how we want it now 🤞
Just had one comment about the specs.
Also @ggetz would you be able to re-test as well just to double check?
it("uses provided bounding sphere", function () { | ||
const bs = new BoundingSphere(new Cartesian3(3, 4, 5), 6); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test that modifies the bounding sphere to prove that the camera will follow it on future calls to update
?
(I didn't check all the other specs so if it already exists just lmk)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jjspace, done! This should be ready for another review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
view.update(JulianDate.now(), bs1); | ||
expect(scene.camera.positionWC).toEqualEpsilon(positionWC1, 1e-10); | ||
|
||
view.boundingSphere = bs2; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is valuable to make sure changing EntityView.boundingSphere
works but doesn't mimic the behavior of changing the actual bounding sphere it was set up with originally like calling getBoundingSphere
does. Maybe another that replaces this line with bs1.center = new Cartesian3(3, 4, 5);
would also be valuable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch @jjspace. Added new test. Should be ready for a final (hopefully 😅) review!
…tion that when a result is undefined, a new object is created
Description
This is an attempt to fix broken entity tracking when viewFrom is set:
Restored
boundingSphereScratch
instead ofentityView.boundingSphere
insideCesiumWidget._onTick
.Issue number and link
Fixes #12465
Testing plan
Author checklist
CONTRIBUTORS.md
CHANGES.md
with a short summary of my change