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

Apply transform to "region" bounding volume type #6755

Merged
merged 6 commits into from
Jul 11, 2018
Merged

Conversation

lilleyse
Copy link
Contributor

@lilleyse lilleyse commented Jun 29, 2018

Fixes #5984

Local Sandcastle link

Before / after
transforms

As noted in the Sandcastle comments, this will break tilesets that expect the geometry to be transformed but not the region. We have one test tileset that does this, but I don't think I've ever seen this is practice.

This will also require changes to the 3D Tiles spec - this section disallows the transform from being applied to regions.

To do:

@cesium-concierge
Copy link

Signed CLA is on file.

@lilleyse, thanks for the pull request! Maintainers, we have a signed CLA from @lilleyse, so you can review this at any time.

⚠️ I noticed that CHANGES.md has not been updated. If this change updates the public API in any way, fixes a bug, or makes any non-trivial update, please add a bullet point to CHANGES.md and comment on this pull request so we know it was updated. For more info, see the Pull Request Guidelines.


I am a bot who helps you make Cesium awesome! Contributions to my configuration are welcome.

🌍 🌎 🌏

@lilleyse
Copy link
Contributor Author

lilleyse commented Jul 8, 2018

Updated. @ggetz can you review?

Modified an existing test to check that a tileset with region bounding volumes can move and still be rendered correctly.

@ggetz ggetz self-assigned this Jul 9, 2018
Copy link
Contributor

@ggetz ggetz left a comment

Choose a reason for hiding this comment

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

Tests are failing with RTC_CENTER tilesets now, are we applying the transforms in the correct order there?

function createRegion(region, transform, initialTransform, result) {
if (!Matrix4.equalsEpsilon(transform, initialTransform, CesiumMath.EPSILON8)) {
return createBoxFromTransformedRegion(region, transform, initialTransform, result);
}

if (defined(result)) {
// Don't update regions when the transform changes
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this still needed?

var rectangleRegion = Rectangle.unpack(region, 0, scratchRectangle);
function createBoxFromTransformedRegion(region, transform, initialTransform, result) {
var rectangle = Rectangle.unpack(region, 0, scratchRectangle);
var minimumHeight = region[4];
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we just created an instance of a Rectangle, I think it would be more readable to use east, north, etc class members rather than the accessor. Here and in createRegion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason for the region[4] and region[5] is Rectangle doesn't contain min/max height properties.

@lilleyse
Copy link
Contributor Author

Tests are failing with RTC_CENTER tilesets now, are we applying the transforms in the correct order there?

Ah sorry for not catching that earlier. It was a small change in the test. The tile was out of view and never updated its computedTransform. It worked before because it was considered in view, only because the region didn't get transformed.

@lilleyse
Copy link
Contributor Author

Thanks for the review @ggetz! Should be ready now.

@ggetz
Copy link
Contributor

ggetz commented Jul 11, 2018

Thanks @lilleyse!

@ggetz ggetz merged commit 122d3e7 into master Jul 11, 2018
@ggetz ggetz deleted the transform-region branch July 11, 2018 13:33
@liujingjie
Copy link

liujingjie commented Sep 27, 2018

I modified the '3D Tiles Adjust Height.html' in the Apps/Sandcastle/gallery, i want to rotate the 3D Tiles,the code works in 1.47 but in 1.48 and 1.49 the 3D Tiles disappear when i rotate the 3D Tiles. Is this related to the change?

the code is

<!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="Adjust the height of a 3D Tiles tileset.">
    <meta name="cesium-sandcastle-labels" content="3D Tiles">
    <title>Cesium Demo</title>
    <script type="text/javascript" src="../Sandcastle-header.js"></script>
    <script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.20/require.js"></script>
    <script type="text/javascript">
        if(typeof require === "function") {
            require.config({
                baseUrl : '../../../Source',
                waitSeconds : 120
            });
        }
    </script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
    @import url(../templates/bucket.css);
    #toolbar {
        background: rgba(42, 42, 42, 0.8);
        padding: 4px;
        border-radius: 4px;
    }
    #toolbar input {
        vertical-align: middle;
        padding-top: 2px;
        padding-bottom: 2px;
    }
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
    <div>Height-offset</div>
    <input type="range" min="-100.0" max="100.0" step="1" data-bind="value: height, valueUpdate: 'input'">
    <input type="text" size="5" data-bind="value: height">

<div>RotateX</div>
    <input type="range" min="-100.0" max="100.0" step="1" data-bind="value: RotateX, valueUpdate: 'input'">
    <input type="text" size="5" data-bind="value: RotateX">

<div>RotateY</div>
    <input type="range" min="-100.0" max="100.0" step="1" data-bind="value: RotateY, valueUpdate: 'input'">
    <input type="text" size="5" data-bind="value: RotateY">

<div>RotateZ</div>
    <input type="range" min="-100.0" max="100.0" step="1" data-bind="value: RotateZ, valueUpdate: 'input'">
    <input type="text" size="5" data-bind="value: RotateZ">
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
    'use strict';
//Sandcastle_Begin
var viewer = new Cesium.Viewer('cesiumContainer', {
    shadows : true
});

viewer.scene.globe.depthTestAgainstTerrain = true;

var viewModel = {
    height: 0,
    RotateX:0,
    RotateY:0,
    RotateZ:0
};

Cesium.knockout.track(viewModel);

var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);

var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
    url : '../../../Specs/Data/Cesium3DTiles/Tilesets/Tileset/tileset.json',
    modelMatrix:Cesium.Matrix4.IDENTITY
}));

tileset.readyPromise.then(function() {
    var boundingSphere = tileset.boundingSphere;
    viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0.0, -0.5, boundingSphere.radius * 2));
    viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
}).otherwise(function(error) {
    throw(error);
});

Cesium.knockout.getObservable(viewModel, 'height').subscribe(function(height) {
    height = Number(height);
    if (isNaN(height)) {
        return;
    }
    var m = tileset.modelMatrix;
    var translation=Cesium.Cartesian3.fromArray([0, 0, height]);

    Cesium.Matrix4.multiplyByTranslation(m,translation,m);
    tileset.modelMatrix = m;
});


Cesium.knockout.getObservable(viewModel, 'RotateX').subscribe(function(RotateX) {
    RotateX = Number(RotateX);
    if (isNaN(RotateX)) {
        return;
    }
    var m = tileset.modelMatrix;
    var m1 = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(RotateX));
    Cesium.Matrix4.multiplyByMatrix3(m,m1,m);
    tileset.modelMatrix = m;
});

Cesium.knockout.getObservable(viewModel, 'RotateY').subscribe(function(RotateY) {
    RotateY = Number(RotateY);
    if (isNaN(RotateY)) {
        return;
    }
    var m = tileset.modelMatrix;
    var m1 = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(RotateY));
    Cesium.Matrix4.multiplyByMatrix3(m,m1,m);
    tileset.modelMatrix = m;
});

Cesium.knockout.getObservable(viewModel, 'RotateZ').subscribe(function(RotateZ) {
    RotateZ = Number(RotateZ);
    if (isNaN(RotateZ)) {
        return;
    }
    var m = tileset.modelMatrix;
    var m1 = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(RotateZ));
    Cesium.Matrix4.multiplyByMatrix3(m,m1,m);
    tileset.modelMatrix = m;
});//Sandcastle_End
    Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
    startup(Cesium);
} else if (typeof require === "function") {
    require(["Cesium"], startup);
}
</script>
</body>
</html>

@lilleyse
Copy link
Contributor Author

Hey @liujingjie, it wasn't this change that caused the problem.

But the tileset in ../../../Specs/Data/Cesium3DTiles/Tilesets/Tileset/tileset.json did change in 1.48 and uses a different method of positioning the tileset (RTC_CENTER rather than CESIUM_RTC) which internally applies matrices in a different order. It might be possible to preserve the original ordering and I'll open an issue for that.

@lilleyse
Copy link
Contributor Author

Looking at this closer the difference in matrix order is intentional. RTC_CENTER it is expected to apply the translation before the model matrix. What ends up happening is the rotation acts on the tileset as if its origin is at the Earth's center, so any small change to rotation will throw it off the screen.

https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification#order-of-transformations

CESIUM_RTC applies the translation last, which is better for this example, but can still produce incorrect behavior - notice that the child tiles are not aligned on the same plane.

wrong-order

In general it is much easier to transform a tileset if it uses a tile transform on the root tile rather than CESIUM_RTC or RTC_CENTER, which aren't easily movable.

@wer200897
Copy link

Looking at this closer the difference in matrix order is intentional. RTC_CENTER it is expected to apply the translation before the model matrix. What ends up happening is the rotation acts on the tileset as if its origin is at the Earth's center, so any small change to rotation will throw it off the screen.

https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification#order-of-transformations

CESIUM_RTC applies the translation last, which is better for this example, but can still produce incorrect behavior - notice that the child tiles are not aligned on the same plane.

wrong-order

In general it is much easier to transform a tileset if it uses a tile transform on the root tile rather than CESIUM_RTC or RTC_CENTER, which aren't easily movable.

I also encountered the same problem. Versions prior to 1.47 can be used, and later versions, 3dtiles model rotation and resizing will not work.

I use Google translate, because I cannot understand English. The explanation is too complicated (Sorry). Is there any chance you can provide the correct code, or point out which part of the code is wrong? Thanks a lot!

@lilleyse
Copy link
Contributor Author

Ok.. it looks like this PR did break the previous behavior, though I think the previous behavior was working accidentally.

The quick workaround is in Cesium3DTile to change:

center = Matrix4.multiplyByPoint(transform, center, center);

to

center = Cartesian3.add(Matrix4.getTranslation(transform, scratchCenter), center, center);

This fixes the example tileset in 1.47 and any other tilesets that use CESIUM_RTC. But it will break behavior for tilesets using RTC_CENTER, usually newer tilesets, which is why I'm hesitant to open a PR just yet. I think it may be possible to support both though.

@wer200897
Copy link

thanks so much! i will give it a try

@binfenshijie
Copy link

thanks so much! i will give it a try

哥们,搞定了吗,这个问题,向您请教下

@binfenshijie
Copy link

I modified the '3D Tiles Adjust Height.html' in the Apps/Sandcastle/gallery, i want to rotate the 3D Tiles,the code works in 1.47 but in 1.48 and 1.49 the 3D Tiles disappear when i rotate the 3D Tiles. Is this related to the change?

the code is

<!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="Adjust the height of a 3D Tiles tileset.">
    <meta name="cesium-sandcastle-labels" content="3D Tiles">
    <title>Cesium Demo</title>
    <script type="text/javascript" src="../Sandcastle-header.js"></script>
    <script type="text/javascript" src="../../../ThirdParty/requirejs-2.1.20/require.js"></script>
    <script type="text/javascript">
        if(typeof require === "function") {
            require.config({
                baseUrl : '../../../Source',
                waitSeconds : 120
            });
        }
    </script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
    @import url(../templates/bucket.css);
    #toolbar {
        background: rgba(42, 42, 42, 0.8);
        padding: 4px;
        border-radius: 4px;
    }
    #toolbar input {
        vertical-align: middle;
        padding-top: 2px;
        padding-bottom: 2px;
    }
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
    <div>Height-offset</div>
    <input type="range" min="-100.0" max="100.0" step="1" data-bind="value: height, valueUpdate: 'input'">
    <input type="text" size="5" data-bind="value: height">

<div>RotateX</div>
    <input type="range" min="-100.0" max="100.0" step="1" data-bind="value: RotateX, valueUpdate: 'input'">
    <input type="text" size="5" data-bind="value: RotateX">

<div>RotateY</div>
    <input type="range" min="-100.0" max="100.0" step="1" data-bind="value: RotateY, valueUpdate: 'input'">
    <input type="text" size="5" data-bind="value: RotateY">

<div>RotateZ</div>
    <input type="range" min="-100.0" max="100.0" step="1" data-bind="value: RotateZ, valueUpdate: 'input'">
    <input type="text" size="5" data-bind="value: RotateZ">
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
    'use strict';
//Sandcastle_Begin
var viewer = new Cesium.Viewer('cesiumContainer', {
    shadows : true
});

viewer.scene.globe.depthTestAgainstTerrain = true;

var viewModel = {
    height: 0,
    RotateX:0,
    RotateY:0,
    RotateZ:0
};

Cesium.knockout.track(viewModel);

var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);

var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
    url : '../../../Specs/Data/Cesium3DTiles/Tilesets/Tileset/tileset.json',
    modelMatrix:Cesium.Matrix4.IDENTITY
}));

tileset.readyPromise.then(function() {
    var boundingSphere = tileset.boundingSphere;
    viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0.0, -0.5, boundingSphere.radius * 2));
    viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
}).otherwise(function(error) {
    throw(error);
});

Cesium.knockout.getObservable(viewModel, 'height').subscribe(function(height) {
    height = Number(height);
    if (isNaN(height)) {
        return;
    }
    var m = tileset.modelMatrix;
    var translation=Cesium.Cartesian3.fromArray([0, 0, height]);

    Cesium.Matrix4.multiplyByTranslation(m,translation,m);
    tileset.modelMatrix = m;
});


Cesium.knockout.getObservable(viewModel, 'RotateX').subscribe(function(RotateX) {
    RotateX = Number(RotateX);
    if (isNaN(RotateX)) {
        return;
    }
    var m = tileset.modelMatrix;
    var m1 = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(RotateX));
    Cesium.Matrix4.multiplyByMatrix3(m,m1,m);
    tileset.modelMatrix = m;
});

Cesium.knockout.getObservable(viewModel, 'RotateY').subscribe(function(RotateY) {
    RotateY = Number(RotateY);
    if (isNaN(RotateY)) {
        return;
    }
    var m = tileset.modelMatrix;
    var m1 = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(RotateY));
    Cesium.Matrix4.multiplyByMatrix3(m,m1,m);
    tileset.modelMatrix = m;
});

Cesium.knockout.getObservable(viewModel, 'RotateZ').subscribe(function(RotateZ) {
    RotateZ = Number(RotateZ);
    if (isNaN(RotateZ)) {
        return;
    }
    var m = tileset.modelMatrix;
    var m1 = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(RotateZ));
    Cesium.Matrix4.multiplyByMatrix3(m,m1,m);
    tileset.modelMatrix = m;
});//Sandcastle_End
    Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
    startup(Cesium);
} else if (typeof require === "function") {
    require(["Cesium"], startup);
}
</script>
</body>
</html>

哥们,问题解决了吗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants