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

CZML model node transformations #3316

Merged
merged 24 commits into from
Dec 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c3903fc
initial implementation of model transformations in czml
gbeatty Jun 18, 2015
7a65133
Fixed ModelVisualizer and tests
gbeatty Jun 19, 2015
818529e
initial working czml implementation for model transformations
gbeatty Jun 21, 2015
895c914
changed ModelGraphics nodeTransformations from a raw property to a re…
gbeatty Jun 21, 2015
9b48563
remove automatic model animation when a model is loaded via czml
gbeatty Jun 22, 2015
4462be8
include the model node's default transformation matrix when adding ad…
gbeatty Jun 22, 2015
f005d20
Update CZML node transformation names
gbeatty Aug 25, 2015
06ad0e8
code formatting cleanup
gbeatty Sep 14, 2015
9359a01
Added a runAnimations property to ModelGraphics.js to allow the user …
gbeatty Sep 14, 2015
22f6ef3
Merge branch 'master' into czmlModelAnimations
gbeatty Sep 29, 2015
f2e0d0e
Merge remote-tracking branch 'origin/master' into gbeatty-czmlModelAn…
shunter Dec 3, 2015
8e4f397
Further work on node transformations.
shunter Dec 8, 2015
46b8234
Merge remote-tracking branch 'origin/master' into czmlModelNodeTransf…
shunter Dec 8, 2015
7a321e1
cleanup
shunter Dec 8, 2015
7755e22
cleanup
shunter Dec 8, 2015
8d38198
load runAnimations from CZML.
shunter Dec 8, 2015
4a3b0b7
Add tests for PropertyBag.
shunter Dec 9, 2015
781952f
Rename NodeTransformation to TranslationRotationScale and move to Cor…
shunter Dec 11, 2015
a90ddbc
clean up module paths.
shunter Dec 11, 2015
8ea6732
originalNodeMatrixHash needs to be per-model
shunter Dec 11, 2015
21ee2ae
Add sandcastle examples
shunter Dec 15, 2015
5bbf684
Updates after review
mramato Dec 18, 2015
ac342c1
Merge remote-tracking branch 'origin/master' into czmlModelNodeTransf…
mramato Dec 18, 2015
3147d89
Update CHANGES.
mramato Dec 18, 2015
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
98 changes: 98 additions & 0 deletions Apps/Sandcastle/gallery/CZML Model - Node Transformations.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<!DOCTYPE html>
<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="CZML Model - Node Transformations">
<meta name="cesium-sandcastle-labels" content="CZML">
<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);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar"></div>

<script id="cesium_sandcastle_script">
function startup(Cesium) {
"use strict";
//Sandcastle_Begin
var czml = [
{
"id" : "document",
"name" : "CZML Model",
"version" : "1.0",
"clock": {
"interval": "2015-01-01T00:00:00Z/2015-01-01T00:00:20Z",
"currentTime": "2015-01-01T00:00:00Z",
"multiplier": 20
}
}, {
"id" : "model",
"position" : {
"cartographicDegrees" : [
-77, 37, 100000
]
},
"viewFrom" : {
"cartesian" : [
4.3, 0.1, 2.6
]
},
"model": {
"gltf" : "../../SampleData/models/CesiumMan/Cesium_Man.glb",
"runAnimations": false,
"nodeTransformations": {
"Skeleton_arm_joint_L__3_": {
"rotation": {
"epoch": "2015-01-01T00:00:00Z",
"unitQuaternion":[
0,-0.7762877147481148, -0.16428915803853994, 0.3706585477262082, 0.482699386243276,
10,-0.6840422631464792, -0.40211904424847345, 0.05175867757399086, 0.6063888981321548,
20,-0.7762877147481148, -0.16428915803853994, 0.3706585477262082, 0.482699386243276
]
}
},
"Skeleton_arm_joint_R__2_": {
"rotation": {
"unitQuaternion": [
0.31933321618140015, 0.5055578277509731, -0.5903490075872426, 0.5421490838170975
]
}
}
}
}
}
];

var viewer = new Cesium.Viewer('cesiumContainer');
var promise = viewer.dataSources.add(Cesium.CzmlDataSource.load(czml));

promise.then(function(dataSource){
viewer.trackedEntity = dataSource.entities.getById('model');
}).otherwise(function(error){
window.alert(error);
});

//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
} else if (typeof require === "function") {
require(["Cesium"], startup);
}
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
244 changes: 244 additions & 0 deletions Apps/Sandcastle/gallery/development/3D Models Node Explorer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
<!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="Explore node transformations of 3D models.">
<meta name="cesium-sandcastle-labels" content="Development">
<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;
}
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<table><tbody>
<tr>
<td colspan="2" data-bind="click: function() { showTranslation = !showTranslation }"><span data-bind="text: showTranslation ? '-' : '+'">+</span> Translation</td>
</tr>
<tr data-bind="visible: showTranslation">
<td>X</td>
<td>
<input type="range" min="-3" max="3" step="0.01" data-bind="value: translationX, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: translationX">
</td>
</tr>
<tr data-bind="visible: showTranslation">
<td>Y</td>
<td>
<input type="range" min="-3" max="3" step="0.01" data-bind="value: translationY, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: translationY">
</td>
</tr>
<tr data-bind="visible: showTranslation">
<td>Z</td>
<td>
<input type="range" min="-3" max="3" step="0.01" data-bind="value: translationZ, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: translationZ">
</td>
</tr>
<tr>
<td colspan="2" data-bind="click: function() { showRotation = !showRotation }"><span data-bind="text: showRotation ? '-' : '+'">+</span> Rotation</td>
</tr>
<tr data-bind="visible: showRotation">
<td>H</td>
<td>
<input type="range" min="-3" max="3" step="0.01" data-bind="value: rotationHeading, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: rotationHeading">
</td>
</tr>
<tr data-bind="visible: showRotation">
<td>P</td>
<td>
<input type="range" min="-3" max="3" step="0.01" data-bind="value: rotationPitch, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: rotationPitch">
</td>
</tr>
<tr data-bind="visible: showRotation">
<td>R</td>
<td>
<input type="range" min="-3" max="3" step="0.01" data-bind="value: rotationRoll, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: rotationRoll">
</td>
</tr>
<tr>
<td colspan="2" data-bind="click: function() { showScale = !showScale }"><span data-bind="text: showScale ? '-' : '+'">+</span> Scale</td>
</tr>
<tr data-bind="visible: showScale">
<td>X</td>
<td>
<input type="range" min="0.01" max="3" step="0.01" data-bind="value: scaleX, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: scaleX">
</td>
</tr>
<tr data-bind="visible: showScale">
<td>Y</td>
<td>
<input type="range" min="0.01" max="3" step="0.01" data-bind="value: scaleY, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: scaleY">
</td>
</tr>
<tr data-bind="visible: showScale">
<td>Z</td>
<td>
<input type="range" min="0.01" max="3" step="0.01" data-bind="value: scaleZ, valueUpdate: 'input'">
<input type="text" size="2" data-bind="value: scaleZ">
</td>
</tr>
</tbody></table>
</div>
<script id="cesium_sandcastle_script">
function startup(Cesium) {
"use strict";
//Sandcastle_Begin
// this can be changed to any glTF model
var modelUrl = '../../SampleData/models/CesiumMan/Cesium_Man.glb';

var viewModel = {
nodeName : undefined,
showTranslation : false,
showRotation : false,
showScale : false,
transformations: {}
};

Cesium.knockout.track(viewModel);

// transformation is a computed property returning the values storage for the current node name
Cesium.knockout.defineProperty(viewModel, 'transformation', function() {
var transformations = viewModel.transformations;
var nodeName = viewModel.nodeName;
if (!Cesium.defined(transformations[nodeName])) {
transformations[nodeName] = {
translationX : 0.0,
translationY : 0.0,
translationZ : 0.0,
rotationHeading : 0.0,
rotationPitch : 0.0,
rotationRoll : 0.0,
scaleX : 1.0,
scaleY : 1.0,
scaleZ : 1.0
};
Cesium.knockout.track(transformations[nodeName]);
}
return transformations[nodeName];
});

// these writable computed properties produce individual values for use in the UI
['translationX', 'translationY', 'translationZ', 'rotationHeading', 'rotationPitch', 'rotationRoll', 'scaleX', 'scaleY', 'scaleZ'].forEach(function(p) {
Cesium.knockout.defineProperty(viewModel, p, {
get: function() {
return viewModel.transformation[p];
},
set: function(value) {
// coerce values to number
viewModel.transformation[p] = +value;
}
});
});

// these computed properties return each element of the transform
Cesium.knockout.defineProperty(viewModel, 'translation', function() {
return new Cesium.Cartesian3(viewModel.translationX, viewModel.translationY, viewModel.translationZ);
});
Cesium.knockout.defineProperty(viewModel, 'rotation', function() {
return Cesium.Quaternion.fromHeadingPitchRoll(viewModel.rotationHeading, viewModel.rotationPitch, viewModel.rotationRoll);
});
Cesium.knockout.defineProperty(viewModel, 'scale', function() {
return new Cesium.Cartesian3(viewModel.scaleX, viewModel.scaleY, viewModel.scaleZ);
});
// this computed property combines the above properties into a single matrix to be applied to the node
Cesium.knockout.defineProperty(viewModel, 'matrix', function() {
return Cesium.Matrix4.fromTranslationQuaternionRotationScale(viewModel.translation, viewModel.rotation, viewModel.scale);
});

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

var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;

var height = 250000.0;
var heading = 0.0;
var pitch = 0.0;
var roll = 0.0;
var origin = Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706, height);
var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(origin, heading, pitch, roll);

var model = scene.primitives.add(Cesium.Model.fromGltf({
url : modelUrl,
modelMatrix : modelMatrix,
minimumPixelSize : 128
}));

model.readyPromise.then(function(model) {
var camera = viewer.camera;

// Zoom to model
var controller = scene.screenSpaceCameraController;
var r = 2.0 * Math.max(model.boundingSphere.radius, camera.frustum.near);
controller.minimumZoomDistance = r * 0.5;

var center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());
var heading = Cesium.Math.toRadians(230.0);
var pitch = Cesium.Math.toRadians(-20.0);
camera.lookAt(center, new Cesium.HeadingPitchRange(heading, pitch, r * 2.0));

// enumerate nodes and add options
var options = Object.keys(model._runtime.nodesByName).map(function(nodeName) {
return {
text : nodeName,
onselect : function() {
viewModel.nodeName = nodeName;
}
};
});
options[0].onselect();
Sandcastle.addToolbarMenu(options);

// respond to viewmodel changes by applying the computed matrix
Cesium.knockout.getObservable(viewModel, 'matrix').subscribe(function(newValue) {
var node = model.getNode(viewModel.nodeName);
if (!Cesium.defined(node.originalMatrix)) {
node.originalMatrix = node.matrix.clone();
}
node.matrix = Cesium.Matrix4.multiply(node.originalMatrix, newValue, new Cesium.Matrix4());
});

}).otherwise(function(error){
window.alert(error);
});

//Sandcastle_End
Sandcastle.finishedLoading();
}
if (typeof Cesium !== "undefined") {
startup(Cesium);
} else if (typeof require === "function") {
require(["Cesium"], startup);
}
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ Change Log
* Added `QuadtreePrimitive#tileLoadProgressEvent` and `Globe#tileLoadProgressEvent`. These event are raised when the length of the tile load queue changes, enabling incremental loading indicators.
* Added missing points to `EllipseGeometry` and `EllipseOutlineGeometry` [#3078](https://github.com/AnalyticalGraphicsInc/cesium/issues/3078)
* Added dynamic `enableFeaturePicking` toggle to all ImageryProviders that support feature picking.
* Added the ability to manipulate `Model` node transformations via CZML and the Entity API. [#3316](https://github.com/AnalyticalGraphicsInc/cesium/pull/3316)
* Added `TranslationRotationScale` property, which as the name suggests, represents an affine transformation defined by a translation, rotation, and scale.
* Added `Matrix4.fromTranslationRotationScale`.
* Added `NodeTransformationProperty`, which is a `Property` value that is defined by independent `translation`, `rotation`, and `scale` `Property` instances.
* Added `PropertyBag`, which is a `Property` whose value is a key-value mapping of property names to the computed value of other properties.
* Added `ModelGraphics.runAnimations` which is a boolean `Property` indicating if all model animations should be started after the model is loaded.
* Added `ModelGraphics.nodeTransformations` which is a `PropertyBag` of `TranslationRotationScale` properties to be applied to a loaded model.
* Added CZML support for new `runAnimations` and `nodeTransformations` properties on the `model` packet.
* Added a new Sandcastle example: [CZML Model - Node Transformations](http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=CZML%20Model%20-%20Node%20Transformations.html&label=CZML).

### 1.16 - 2015-12-01

Expand Down
18 changes: 18 additions & 0 deletions Source/Core/Matrix4.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ define([
* @see Matrix4.fromColumnMajorArray
* @see Matrix4.fromRowMajorArray
* @see Matrix4.fromRotationTranslation
* @see Matrix4.fromTranslationRotationScale
* @see Matrix4.fromTranslationQuaternionRotationScale
* @see Matrix4.fromTranslation
* @see Matrix4.fromScale
Expand Down Expand Up @@ -410,6 +411,23 @@ define([
return result;
};

/**
* Creates a Matrix4 instance from a {@link TranslationRotationScale} instance.
*
* @param {TranslationRotationScale} translationRotationScale The instance.
* @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created.
* @returns {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided.
*/
Matrix4.fromTranslationRotationScale = function(translationRotationScale, result) {
//>>includeStart('debug', pragmas.debug);
if (!defined(translationRotationScale)) {
throw new DeveloperError('translationRotationScale is required.');
}
//>>includeEnd('debug');

return Matrix4.fromTranslationQuaternionRotationScale(translationRotationScale.translation, translationRotationScale.rotation, translationRotationScale.scale, result);
};

/**
* Creates a Matrix4 instance from a Cartesian3 representing the translation.
*
Expand Down
Loading