Skip to content

Commit

Permalink
Merge pull request #4299 from AnalyticalGraphicsInc/fix-transform
Browse files Browse the repository at this point in the history
3D Tiles - Fix transform
  • Loading branch information
pjcozzi authored Sep 9, 2016
2 parents cd48ffb + 8291717 commit a458bc6
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 40 deletions.
62 changes: 36 additions & 26 deletions Source/Scene/Cesium3DTile.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,18 @@ define([
*/
this.transform = defined(header.transform) ? Matrix4.unpack(header.transform) : Matrix4.clone(Matrix4.IDENTITY);

var parentTransform = defined(parent) ? parent.computedTransform : tileset.modelMatrix;
var computedTransform = Matrix4.multiply(parentTransform, this.transform, new Matrix4());

/**
* The final computed transform of this tile
* @type {Matrix4}
*/
var parentTransform = defined(parent) ? parent.computedTransform : tileset.modelMatrix;
this.computedTransform = Matrix4.multiply(parentTransform, this.transform, new Matrix4());
this._computedTransform = Matrix4.clone(this.computedTransform);
this.computedTransform = computedTransform;

this._transformDirty = true;

this._boundingVolume = this.createBoundingVolume(header.boundingVolume, this.computedTransform);
this._boundingVolume = this.createBoundingVolume(header.boundingVolume, computedTransform);

var contentBoundingVolume;

Expand All @@ -106,7 +107,7 @@ define([
// but not for culling for traversing the tree since it is not spatial coherence, i.e.,
// since it only bounds models in the tile, not the entire tile, children may be
// outside of this box.
contentBoundingVolume = this.createBoundingVolume(contentHeader.boundingVolume, this.computedTransform);
contentBoundingVolume = this.createBoundingVolume(contentHeader.boundingVolume, computedTransform);
}
this._contentBoundingVolume = contentBoundingVolume;

Expand Down Expand Up @@ -605,6 +606,35 @@ define([
}
};

var scratchTransform = new Matrix4();

/**
* Update the tile's transform. The transform is applied to the tile's bounding volumes.
*
* @private
*/
Cesium3DTile.prototype.updateTransform = function(parentTransform) {
parentTransform = defaultValue(parentTransform, Matrix4.IDENTITY);
var computedTransform = Matrix4.multiply(parentTransform, this.transform, scratchTransform);
var transformDirty = !Matrix4.equals(computedTransform, this.computedTransform);
if (transformDirty) {
this._transformDirty = true;
Matrix4.clone(computedTransform, this.computedTransform);

// Update the bounding volumes
var header = this._header;
var content = this._header.content;
this._boundingVolume = this.createBoundingVolume(header.boundingVolume, computedTransform, this._boundingVolume);
if (defined(this._contentBoundingVolume)) {
this._contentBoundingVolume = this.createBoundingVolume(content.boundingVolume, computedTransform, this._contentBoundingVolume);
}

// Destroy the debug bounding volumes. They will be generated fresh.
this._debugBoundingVolume = this._debugBoundingVolume && this._debugBoundingVolume.destroy();
this._debugContentBoundingVolume = this._debugContentBoundingVolume && this._debugContentBoundingVolume.destroy();
}
};

function applyDebugSettings(tile, tileset, frameState) {
// Tiles do not have a content.boundingVolume if it is the same as the tile's boundingVolume.
var hasContentBoundingVolume = defined(tile._header.content) && defined(tile._header.content.boundingVolume);
Expand Down Expand Up @@ -637,35 +667,15 @@ define([
}
}

function updateTransform(tile) {
var transformDirty = !Matrix4.equals(tile.computedTransform, tile._computedTransform);
if (transformDirty) {
Matrix4.clone(tile.computedTransform, tile._computedTransform);

// Update the bounding volumes
var header = tile._header;
var content = tile._header.content;
tile._boundingVolume = tile.createBoundingVolume(header.boundingVolume, tile.computedTransform, tile._boundingVolume);
if (defined(tile._contentBoundingVolume)) {
tile._contentBoundingVolume = tile.createBoundingVolume(content.boundingVolume, tile.computedTransform, tile._contentBoundingVolume);
}

// Destroy the debug bounding volumes. They will be generated fresh.
tile._debugBoundingVolume = tile._debugBoundingVolume && tile._debugBoundingVolume.destroy();
tile._debugContentBoundingVolume = tile._debugContentBoundingVolume && tile._debugContentBoundingVolume.destroy();
}
tile._transformDirty = transformDirty;
}

/**
* Get the draw commands needed to render this tile.
*
* @private
*/
Cesium3DTile.prototype.update = function(tileset, frameState) {
updateTransform(this);
applyDebugSettings(this, tileset, frameState);
this._content.update(tileset, frameState);
this._transformDirty = false;
};

var scratchCommandList = [];
Expand Down
46 changes: 36 additions & 10 deletions Source/Scene/Cesium3DTileset.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,7 @@ define([
this._maximumNumberOfLoadedTiles = defaultValue(options.maximumNumberOfLoadedTiles, 256);
this._styleEngine = new Cesium3DTileStyleEngine();

/**
* A 4x4 transformation matrix that transforms the tileset's root tile.
*
* @type {Matrix4}
* @default Matrix4.IDENTITY
*/
this.modelMatrix = defined(options.modelMatrix) ? options.modelMatrix : Matrix4.clone(Matrix4.IDENTITY);
this._modelMatrix = defined(options.modelMatrix) ? Matrix4.clone(options.modelMatrix) : Matrix4.clone(Matrix4.IDENTITY);

/**
* This property is for debugging only; it is not optimized for production use.
Expand Down Expand Up @@ -635,6 +629,26 @@ define([
}
},

/**
* A 4x4 transformation matrix that transforms the tileset's root tile.
*
* @type {Matrix4}
* @default Matrix4.IDENTITY
*/
modelMatrix : {
get : function() {
return this._modelMatrix;
},
set : function(value) {
this._modelMatrix = Matrix4.clone(value, this._modelMatrix);
if (defined(this._root)) {
// Update the root transform right away instead of waiting for the next update loop.
// Useful, for example, when setting the modelMatrix and then having the camera view the tileset.
this._root.updateTransform(this._modelMatrix);
}
}
},

/**
* @private
*/
Expand Down Expand Up @@ -797,6 +811,14 @@ define([
}
}

function updateTransforms(children, parentTransform) {
var length = children.length;
for (var i = 0; i < length; ++i) {
var child = children[i];
child.updateTransform(parentTransform);
}
}

// PERFORMANCE_IDEA: is it worth exploiting frame-to-frame coherence in the sort, i.e., the
// list of children are probably fully or mostly sorted unless the camera moved significantly?
function sortChildrenByDistanceToCamera(a, b) {
Expand Down Expand Up @@ -884,6 +906,7 @@ define([
replacementList.splice(replacementList.tail, tileset._replacementSentinel);

var root = tileset._root;
root.updateTransform(tileset._modelMatrix);
root.distanceToCamera = root.distanceToTile(frameState);

if (getScreenSpaceError(tileset._geometricError, root, frameState) <= maximumScreenSpaceError) {
Expand Down Expand Up @@ -912,9 +935,6 @@ define([
t.replaced = false;
++stats.visited;

var parentTransform = defined(t.parent) ? t.parent.computedTransform : tileset.modelMatrix;
t.computedTransform = Matrix4.multiply(parentTransform, t.transform, t.computedTransform);

var visibilityPlaneMask = t.visibilityPlaneMask;
var fullyVisible = (visibilityPlaneMask === CullingVolume.MASK_INSIDE);

Expand All @@ -938,6 +958,7 @@ define([
child = t.children[0];
child.visibilityPlaneMask = t.visibilityPlaneMask;
child.distanceToCamera = t.distanceToCamera;
child.updateTransform(t.computedTransform);
if (child.contentUnloaded) {
requestContent(tileset, child, outOfCore);
} else {
Expand All @@ -959,6 +980,8 @@ define([
// children are loaded or request slots are available.
var anyChildrenLoaded = (t.numberOfChildrenWithoutContent < childrenLength);
if (anyChildrenLoaded || t.canRequestContent()) {
updateTransforms(children, t.computedTransform);

// Distance is used for sorting now and for computing SSE when the tile comes off the stack.
computeDistanceToCamera(children, frameState);

Expand Down Expand Up @@ -1004,6 +1027,8 @@ define([

var allChildrenLoaded = t.numberOfChildrenWithoutContent === 0;
if (allChildrenLoaded || t.canRequestContent()) {
updateTransforms(children, t.computedTransform);

// Distance is used for sorting now and for computing SSE when the tile comes off the stack.
computeDistanceToCamera(children, frameState);

Expand Down Expand Up @@ -1058,6 +1083,7 @@ define([
var someVisibleChildrenLoaded = false;
for (k = 0; k < childrenLength; ++k) {
child = children[k];
child.updateTransform(t.computedTransform);
child.visibilityPlaneMask = child.visibility(frameState.cullingVolume, visibilityPlaneMask);
if (isVisible(child.visibilityPlaneMask)) {
if (child.contentReady) {
Expand Down
4 changes: 2 additions & 2 deletions Specs/Scene/Cesium3DTileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@ defineSuite([
// Change the transform
var newLongitude = -1.012;
var newLatitude = 0.698874;
tile.computedTransform = getTileTransform(newLongitude, newLatitude);
tile.update(mockTileset);
tile.transform = getTileTransform(newLongitude, newLatitude);
tile.updateTransform();

// Check the new transform
var newCenter = Cartesian3.fromRadians(newLongitude, newLatitude);
Expand Down
21 changes: 20 additions & 1 deletion Specs/Scene/Cesium3DTilesetSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1522,14 +1522,15 @@ defineSuite([
it('propagates tile transform down the tree', function() {
return Cesium3DTilesTester.loadTileset(scene, tilesetWithTransformsUrl).then(function(tileset) {
scene.renderForSpecs();
var stats = tileset._statistics;
var root = tileset._root;
var rootTransform = Matrix4.unpack(root._header.transform);

var child = root.children[0];
var childTransform = Matrix4.unpack(child._header.transform);
var computedTransform = Matrix4.multiply(rootTransform, childTransform, new Matrix4());

expect(tileset._selectedTiles.length).toBe(2);
expect(stats.numberOfCommands).toBe(2);
expect(root.computedTransform).toEqual(rootTransform);
expect(child.computedTransform).toEqual(computedTransform);

Expand All @@ -1539,6 +1540,24 @@ defineSuite([
computedTransform = Matrix4.multiply(tilesetTransform, computedTransform, computedTransform);
scene.renderForSpecs();
expect(child.computedTransform).toEqual(computedTransform);

// Set the modelMatrix somewhere off screen
tileset.modelMatrix = Matrix4.fromTranslation(new Cartesian3(0.0, 100000.0, 0.0));
scene.renderForSpecs();
expect(stats.numberOfCommands).toBe(0);

// Now bring it back
tileset.modelMatrix = Matrix4.IDENTITY;
scene.renderForSpecs();
expect(stats.numberOfCommands).toBe(2);

// Do the same steps for a tile transform
child.transform = Matrix4.fromTranslation(new Cartesian3(0.0, 100000.0, 0.0));
scene.renderForSpecs();
expect(stats.numberOfCommands).toBe(1);
child.transform = Matrix4.IDENTITY;
scene.renderForSpecs();
expect(stats.numberOfCommands).toBe(2);
});
});

Expand Down
1 change: 0 additions & 1 deletion Specs/Scene/PointCloud3DTileContentSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ defineSuite([

// Update tile transform
tileset._root.transform = newTransform;
scene.renderForSpecs();

// Move the camera to the new location
setCamera(newLongitude, newLatitude);
Expand Down

0 comments on commit a458bc6

Please sign in to comment.