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

Make it trivial to relocate a Cesium3DTileset #6394

Open
mramato opened this issue Mar 30, 2018 · 12 comments
Open

Make it trivial to relocate a Cesium3DTileset #6394

mramato opened this issue Mar 30, 2018 · 12 comments

Comments

@mramato
Copy link
Contributor

mramato commented Mar 30, 2018

Right now, you need to jump through a bunch of hoops to relocate an already geolocated tileset. We should be able to turn this into a one-liner, something like tileset.setPositionHeadingPitchRoll

Here's an example of what I had to do today to move a tileset. Do we have an easier way that I'm already missing? CC @lilleyse @likangning93

var setCartoScratch = new Cesium.Cartesian3();
var setCartoPosScratch = new Cesium.Cartesian3();
var setCartoTransformScratch = new Cesium.Matrix4();

function setWorldPositionFromCartographic(transform, positionCartographic) {
    var oldPositionCartesian = Cesium.Matrix4.multiplyByPoint(transform, Cesium.Cartesian3.ZERO, setCartoScratch);
    var newPositionCartesian = Cesium.Ellipsoid.WGS84.cartographicToCartesian(positionCartographic, setCartoPosScratch);
    newPositionCartesian = Cesium.Cartesian3.subtract(newPositionCartesian, oldPositionCartesian, newPositionCartesian);
    var translationMatrix = Cesium.Matrix4.fromTranslation(newPositionCartesian, setCartoTransformScratch);

    return Cesium.Matrix4.multiply(translationMatrix, transform, transform);
}

var setHprQuaternion = new Cesium.Quaternion();
var setHprQuaternion2 = new Cesium.Quaternion();
var setHprTranslation = new Cesium.Cartesian3();
var setHprScale = new Cesium.Cartesian3();
var setHprCenter = new Cesium.Cartesian3();
var setHprTransform = new Cesium.Matrix4();
var setHprRotation = new Cesium.Matrix3();

function setRotationFromHeadingPitchRoll(transform, headingPitchRoll) {
    var rotationQuaternion = Cesium.Quaternion.fromHeadingPitchRoll(headingPitchRoll, setHprQuaternion);
    var translation = Cesium.Matrix4.getTranslation(transform, setHprTranslation);
    var scale = Cesium.Matrix4.getScale(transform, setHprScale);
    var center = Cesium.Matrix4.multiplyByPoint(transform, Cesium.Cartesian3.ZERO, setHprCenter);
    var backTransform = Cesium.Transforms.eastNorthUpToFixedFrame(center, undefined, setHprTransform);

    var rotationFixed = Cesium.Matrix4.getRotation(backTransform, setHprRotation);
    var quaternionFixed = Cesium.Quaternion.fromRotationMatrix(rotationFixed, setHprQuaternion2);
    var rotation = Cesium.Quaternion.multiply(quaternionFixed, rotationQuaternion, rotationFixed);

    return Cesium.Matrix4.fromTranslationQuaternionRotationScale(translation, rotation, scale, transform);
}

tileset.readyPromise.then(function(tileset) {
    var offset = Cesium.Cartographic.fromDegrees(-76.0, 40.0, 40);
    tileset._root.transform = setWorldPositionFromCartographic(tileset._root.transform, offset);
    
    var hpr = Cesium.HeadingPitchRoll.fromDegrees(0,0,0);
    tileset._root.transform = setRotationFromHeadingPitchRoll(tileset._root.transform, hpr);
    
    viewer.scene.primitives.add(tileset);
    viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(0.0, -0.5, tileset.boundingSphere.radius * 2.0));
}).otherwise(function(error) {
    console.log(error);
});
@likangning93
Copy link
Contributor

My first instinct was to invert the existing transform and then build a more ordinary ENU -> CBF/world matrix.

@mramato
Copy link
Contributor Author

mramato commented Mar 30, 2018

Thanks @likangning93, the above was the code I had on hand; but I agree your idea is probably better. Do you have a snippet of your approach?

@likangning93
Copy link
Contributor

Ah wait... I think I came up with that idea because I didn't realize the root transform is different from the tileset model matrix. Couldn't we just clear that tileset root transform and then use Transforms.headingPitchRollToFixedFrame?

@likangning93
Copy link
Contributor

likangning93 commented Apr 2, 2018

Maybe something as simple as this, but with actual HPR:

tileset.readyPromise.then(function(tileset) {
    viewer.scene.primitives.add(tileset);

    // Position the tileset
    var longitude = 115.75901903352568;
    var latitude = -32.094407571028384;
    var height = 50;
    tileset._root.transform = Cesium.Matrix4.IDENTITY;
    tileset.modelMatrix = computeTransform(latitude, longitude, height); // or set tileset._root.transform directly

    return tileset.readyPromise;
})
.otherwise(function (error) {
    console.log(error);
});

function computeTransform(latitude, longitude, height) {
    var offset = height;
    var cartesianOffset = Cesium.Cartesian3.fromDegrees(longitude, latitude, offset);
    return Cesium.Transforms.headingPitchRollToFixedFrame(cartesianOffset, new Cesium.HeadingPitchRoll());
}

I guess the one worrisome thing here is direct access of the private tileset._root.transform.
Maybe we could avoid that by using the inverse strategy and approximating that inverse based on the tileset's bounding sphere. That might even help us with the problem where local center for tilesets is sometimes several kilometers away.

@ProjectBarks
Copy link
Contributor

ProjectBarks commented Aug 20, 2019

Since this was posted, we now have modelMatrix and it seems like a very minimal amount of code to move or translate the model. My fear is that more forms of offset of transformation would be expected as time goes on; bloating the API. Considering, the model matrix opens the potential for any kind of transformation and it is a minimal amount of code to actually get results I feel as if this is an unneeded API change.

We could however change the sandbox to provide more examples of transforming 3D Tilesets. I already expounded a little bit.

@mramato
Copy link
Contributor Author

mramato commented Aug 20, 2019

This is a common request from a lot of users, I wouldn't consider it API bloat. I think tileset.position property in ECF makes complete sense even if it just a getter/setter wrapper around the model matrix. If there are too many edge cases that make the hard to do for all tilesets, then we probably need to figure how next steps for improving 3D Tiles (or our implementation) to remove those cases.

@ProjectBarks
Copy link
Contributor

ProjectBarks commented Aug 21, 2019

So I was looking around and noticed there is no way to only set the rotation component of the Matrix4. Does it make sense to add a fromRotation function? The

There is this function:
https://github.com/AnalyticalGraphicsInc/cesium/blob/1.60/Source/Core/Matrix4.js#L293

but it requires you set the transformation component.

@lilleyse @likangning93 what do you think?

@hpinkos
Copy link
Contributor

hpinkos commented Aug 21, 2019

@ProjectBarks translation is optional in the Matrix4.fromRotationTranlsation function so you can just use that

@ProjectBarks
Copy link
Contributor

ProjectBarks commented Aug 21, 2019

@hpinkos it sets the components to zero. If you pass in a result matrix you don't want it to change your translation components.

You could get the translation (Matrix4.fromTranslation) pass it into Matrix4.fromRotationTranslation but that would be an unneeded complexity.

A fromRotation function would only change the rotation component and could be used within Matrix4.fromRotationTranslation

@hpinkos
Copy link
Contributor

hpinkos commented Aug 21, 2019

Ah, that makes sense. In that case, I think it would be find to add a Matrix4.setRotation function to complement Matrix4.getRotation

@vsergey3d
Copy link

vsergey3d commented Oct 7, 2019

const offsetInModelSpace = new Cesium.Cartesian3(0, 0, 100); // moving tileset up
const cartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center);
const origCenter = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(origCenter);
const movedCenter = Cesium.Matrix4.multiplyByPoint(transform, offsetInModelSpace, new Cesium.Cartesian3());
const translation = Cesium.Cartesian3.subtract(movedCenter, origCenter, new Cesium.Cartesian3());
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment