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

Clipping planes for models, tilesets, point clouds #5913

Merged
merged 20 commits into from
Nov 2, 2017
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions Apps/Sandcastle/gallery/Clipping Planes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<!DOCTYPE html>
Copy link
Contributor

Choose a reason for hiding this comment

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

Add an image for the demo.

<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"> <!-- Use Chrome Frame in IE -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<meta name="description" content="A sample BIM dataset rendered with 3D Tiles.">
Copy link
Contributor

Choose a reason for hiding this comment

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

Fix description.

<meta name="cesium-sandcastle-labels" content="Showcases, 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">
require.config({
baseUrl : '../../../Source',
waitSeconds : 60
});
</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;
}
#toolbar .header {
font-weight: bold;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<table><tbody>
<tr>
<td>Type</td>
<td><select data-bind="options: exampleTypes, value: exampleType"></select></td>
</tr>
<tr>
<td>+-x</td>
Copy link
Contributor

Choose a reason for hiding this comment

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

This name looks confusing. Just "x" is probably fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah the sandcastle example still needs updating. Should have put that on the TODO, sorry.

<td>
<input type="range" min="-100" max="100" step="1" data-bind="value: xOffset, valueUpdate: 'input'">
Copy link
Contributor

Choose a reason for hiding this comment

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

Reset x, y, and z when a new model is selected. After playing around with the BIM tileset I switched to the airplane and couldn't see it because the values were just too big.

It might even be good to limit the range of these values per-model. The sliders are too shifty for the airplane model. Or just scale up the airplane if that's easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So setting the scale on the model causes the clipping planes to behave differently- It will not be relative to the model anymore and move around when the camera changes position. But only when the model is set to a scale other than 1. Do you know why that might be?

Copy link
Contributor

Choose a reason for hiding this comment

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

The scale component of the modelView3D must be shifting the distance portion of the plane in createClippingPlanesFunction. Try removing the scale from the matrix, like: https://gamedev.stackexchange.com/questions/119702/fastest-way-to-neutralize-scale-in-the-transform-matrix

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Instead I inverted, muliplied by scale, and inverted back. Is that ok?

<input type="text" size="5" data-bind="value: xOffset">
</td>
</tr>
</tbody></table>
</div>

<script id="cesium_sandcastle_script">
function startup(Cesium) {
'use strict';
//Sandcastle_Begin
// Power Plant design model provided by Bentley Systems
var viewer = new Cesium.Viewer('cesiumContainer');

var viewModel = {
exampleType : 'BIM',
exampleTypes : ['BIM', 'Model', 'Point Cloud'],
xOffset : 0.0
};
Cesium.knockout.track(viewModel);

// Bind the viewModel to the DOM elements of the UI that call for it.
var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);

var bimUrl = 'https://beta.cesium.com/api/assets/1459?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzNjUyM2I5Yy01YmRhLTQ0MjktOGI0Zi02MDdmYzBjMmY0MjYiLCJpZCI6NDQsImFzc2V0cyI6WzE0NTldLCJpYXQiOjE0OTkyNjQ3ODF9.SW_rwY-ic0TwQBeiweXNqFyywoxnnUBtcVjeCmDGef4';
var pointCloudUrl = 'https://beta.cesium.com/api/assets/1460?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIyMzk2YzJiOS1jZGFmLTRlZmYtYmQ4MS00NTA3NjEwMzViZTkiLCJpZCI6NDQsImFzc2V0cyI6WzE0NjBdLCJpYXQiOjE0OTkyNjQ3NTV9.oWjvN52CRQ-dk3xtvD4e8ZnOHZhoWSpJLlw115mbQJM';
Copy link
Contributor

Choose a reason for hiding this comment

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

When I switch from Model to Point Cloud the camera is placed far away. I think this has to do with the model not being destroyed and still having focus. This is related to the comment below about a common cleanup function.


var clippingPlanes = loadTileset(bimUrl);

function loadTileset (url) {
var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url : url
}));

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

tileset.clippingPlanes = [ new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0) ];
Copy link
Contributor

Choose a reason for hiding this comment

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

Eventually this demo may require different tilesets having different clipping planes. There should probably an array of clipping planes per-example above rather than here in tileset creation.

return tileset.clippingPlanes;
}

function loadModel (url, height) {
viewer.entities.removeAll();
Copy link
Contributor

Choose a reason for hiding this comment

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

Also need to call viewer.scene.primitives.removeAll() otherwise tilesets will continue to stay loaded.

To keep is simple, these cleanup lines can go in their own function called from within Cesium.knockout.getObservable(viewModel, 'exampleType').subscribe(


var position = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);
var heading = Cesium.Math.toRadians(135);
var pitch = 0;
var roll = 0;
var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
var orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr);

var entity = viewer.entities.add({
name : url,
position : position,
orientation : orientation,
model : {
uri : url,
minimumPixelSize : 128,
maximumScale : 20000
}
});

viewer.trackedEntity = entity;

entity.model.clippingPlanes = [ new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0) ];
return entity.model.clippingPlanes.getValue();
}

Cesium.knockout.getObservable(viewModel, 'xOffset').subscribe(
function(newValue) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In other demos these two lines are on the same line:

Cesium.knockout.getObservable(viewModel, 'xOffset').subscribe(function(newValue) {

clippingPlanes[0].distance = parseFloat(newValue);
}
);

Cesium.knockout.getObservable(viewModel, 'exampleType').subscribe(
function(newValue) {
if (newValue === 'BIM') {
clippingPlanes = loadTileset(bimUrl);
} else if (newValue === 'Point Cloud') {
clippingPlanes = loadTileset(pointCloudUrl);
} else {
clippingPlanes = loadModel('../../SampleData/models/CesiumAir/Cesium_Air.glb', 20.0);
}
}
);

//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
} else if (typeof require === "function") {
require(["Cesium"], startup);
}
</script>
</body>
</html>
15 changes: 14 additions & 1 deletion Source/DataSources/ModelGraphics.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ define([
* @param {Property} [options.color=Color.WHITE] A Property specifying the {@link Color} that blends with the model's rendered color.
* @param {Property} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] An enum Property specifying how the color blends with the model.
* @param {Property} [options.colorBlendAmount=0.5] A numeric Property specifying the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two.
* @param {Property} [options.clippingPlanes=[]] A list of planes to clip the model by.
Copy link
Contributor

Choose a reason for hiding this comment

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

A better description: A Property specifying an array of {@link Plane} used to clip the model.

*
* @see {@link http://cesiumjs.org/2014/03/03/Cesium-3D-Models-Tutorial/|3D Models Tutorial}
* @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=3D%20Models.html|Cesium Sandcastle 3D Models Demo}
Expand Down Expand Up @@ -91,6 +92,8 @@ define([
this._colorBlendModeSubscription = undefined;
this._colorBlendAmount = undefined;
this._colorBlendAmountSubscription = undefined;
this._clippingPlanes = undefined;
this._clippingPlanesSubscription = undefined;
this._definitionChanged = new Event();

this.merge(defaultValue(options, defaultValue.EMPTY_OBJECT));
Expand Down Expand Up @@ -242,7 +245,15 @@ define([
* @type {Property}
* @default 0.5
*/
colorBlendAmount : createPropertyDescriptor('colorBlendAmount')
colorBlendAmount : createPropertyDescriptor('colorBlendAmount'),

/**
* The list of planes to clip the tileset.
Copy link
Contributor

Choose a reason for hiding this comment

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

Tweak description here too.

* @memberof ModelGraphics.prototype
* @type {Property}
* @default []
*/
clippingPlanes: createPropertyDescriptor('clippingPlanes')
});

/**
Expand Down Expand Up @@ -271,6 +282,7 @@ define([
result.color = this.color;
result.colorBlendMode = this.colorBlendMode;
result.colorBlendAmount = this.colorBlendAmount;
result.clippingPlanes = this.clippingPlanes;

return result;
};
Expand Down Expand Up @@ -303,6 +315,7 @@ define([
this.color = defaultValue(this.color, source.color);
this.colorBlendMode = defaultValue(this.colorBlendMode, source.colorBlendMode);
this.colorBlendAmount = defaultValue(this.colorBlendAmount, source.colorBlendAmount);
this.clippingPlanes = defaultValue(this.clippingPlanes, source.clippingPlanes);

var sourceNodeTransformations = source.nodeTransformations;
if (defined(sourceNodeTransformations)) {
Expand Down
2 changes: 2 additions & 0 deletions Source/DataSources/ModelVisualizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ define([
var defaultColor = Color.WHITE;
var defaultColorBlendMode = ColorBlendMode.HIGHLIGHT;
var defaultColorBlendAmount = 0.5;
var defaultClippingPlanes = [];

var modelMatrixScratch = new Matrix4();
var nodeMatrixScratch = new Matrix4();
Expand Down Expand Up @@ -152,6 +153,7 @@ define([
model.color = Property.getValueOrDefault(modelGraphics._color, time, defaultColor, model._color);
model.colorBlendMode = Property.getValueOrDefault(modelGraphics._colorBlendMode, time, defaultColorBlendMode);
model.colorBlendAmount = Property.getValueOrDefault(modelGraphics._colorBlendAmount, time, defaultColorBlendAmount);
model.clippingPlanes = Property.getValueOrDefault(modelGraphics._clippingPlanes, time, defaultClippingPlanes);
Copy link
Contributor

Choose a reason for hiding this comment

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

Make sure to double-check that everything works correctly when two models are added that use different clipping planes. It seems like it should be fine.


if (model.ready) {
var runAnimations = Property.getValueOrDefault(modelGraphics._runAnimations, time, true);
Expand Down
4 changes: 3 additions & 1 deletion Source/Scene/Batched3DModel3DTileContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,8 @@ define([
pickFragmentShaderLoaded : batchTable.getPickFragmentShaderCallback(),
pickUniformMapLoaded : batchTable.getPickUniformMapCallback(),
addBatchIdToGeneratedShaders : (batchLength > 0), // If the batch table has values in it, generated shaders will need a batchId attribute
pickObject : pickObject
pickObject : pickObject,
clippingPlanes : tileset._clippingPlanes
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be tileset.clippingPlanes.

});
}

Expand Down Expand Up @@ -447,6 +448,7 @@ define([
this._model.modelMatrix = this._tile.computedTransform;
this._model.shadows = this._tileset.shadows;
this._model.debugWireframe = this._tileset.debugWireframe;
this._model.clippingPlanes = this._tileset.clippingPlanes;
this._model.update(frameState);

// If any commands were pushed, add derived commands
Expand Down
25 changes: 25 additions & 0 deletions Source/Scene/Cesium3DTileset.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ define([
* @param {Number} [options.skipLevels=1] When <code>skipLevelOfDetail</code> is <code>true</code>, a constant defining the minimum number of levels to skip when loading tiles. When it is 0, no levels are skipped. Used in conjunction with <code>skipScreenSpaceErrorFactor</code> to determine which tiles to load.
* @param {Boolean} [options.immediatelyLoadDesiredLevelOfDetail=false] When <code>skipLevelOfDetail</code> is <code>true</code>, only tiles that meet the maximum screen space error will ever be downloaded. Skipping factors are ignored and just the desired tiles are loaded.
* @param {Boolean} [options.loadSiblings=false] When <code>skipLevelOfDetail</code> is <code>true</code>, determines whether siblings of visible tiles are always downloaded during traversal.
* @param {Plane[]} [options.clippingPlanes=[]] List of planes by which to clip the model.
Copy link
Contributor

Choose a reason for hiding this comment

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

model -> tileset.

The description below is better: The list of planes to clip the tileset.

* @param {Boolean} [options.debugFreezeFrame=false] For debugging only. Determines if only the tiles from last frame should be used for rendering.
* @param {Boolean} [options.debugColorizeTiles=false] For debugging only. When true, assigns a random color to each tile.
* @param {Boolean} [options.debugWireframe=false] For debugging only. When true, render's each tile's content as a wireframe.
Expand Down Expand Up @@ -522,6 +523,14 @@ define([
*/
this.loadSiblings = defaultValue(options.loadSiblings, false);

/**
* The list of planes to clip the tileset.
*
* @type {Plane[]}
* @default []
*/
this.clippingPlanes = defaultValue(options.clippingPlanes, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor, but here and below we usually would not use defaultValue when the second parameter is an allocation. Instead used defined and ?.

However, couldn't clippingPlanes also just be undefined? I'm pretty sure we allow that in other places where undefined and empty are treated the same.

If not, please make sure that we throw a DeveloperError when it is undefined.


/**
* This property is for debugging only; it is not optimized for production use.
* <p>
Expand Down Expand Up @@ -1029,6 +1038,22 @@ define([
}
},

/**
* The list of planes to clip the tileset.
*
* @memberof Cesium3DTileset.prototype
*
* @type {Plane[]}
*/
clippingPlanes : {
get : function() {
return this._clippingPlanes;
},
set : function(value) {
this._clippingPlanes = value;
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

This getter/setter isn't needed since it isn't doing any special operations and this.clippingPlanes is already public.


/**
* @private
*/
Expand Down
Loading