diff --git a/Apps/Sandcastle/gallery/3D Models.html b/Apps/Sandcastle/gallery/3D Models.html index c7d517302cd5..d02f6d65cc75 100644 --- a/Apps/Sandcastle/gallery/3D Models.html +++ b/Apps/Sandcastle/gallery/3D Models.html @@ -47,7 +47,8 @@ orientation : orientation, model : { uri : url, - minimumPixelSize : 128 + minimumPixelSize : 128, + maximumScale : 20000 } }); viewer.trackedEntity = entity; diff --git a/CHANGES.md b/CHANGES.md index 316b9cd9c74f..304157ff1088 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ Change Log * Fixed an issue where the sun texture is not generated correctly on some mobile devices. [#3141](https://github.com/AnalyticalGraphicsInc/cesium/issues/3141) * Fixed a bug in the deprecated `jsonp` that prevented it from returning a promise. Its replacement, `loadJsonp`, was unaffected. * Fixed glTF implementation to read the version as a string as per the specification and to correctly handle backwards compatibility for axis-angle rotations in glTF 0.8 models. +* Added `Model.maximumScale` and `ModelGraphics.maximumScale` properties, giving an upper limit for minimumPixelSize. ### 1.15 - 2015-11-02 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index dc5f96e9fb38..eb2fc573c319 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -73,5 +73,6 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu * [Cole Murphy](https://github.com/fantasticole) * [Keat Tang](https://github.com/keattang) * [Denver Pierce](https://github.com/denverpierce) +* [Tucker Tibbetts](https://github.com/cttibbetts) Also see [our contributors page](http://cesiumjs.org/contributors.html) for more information. diff --git a/Source/DataSources/ModelGraphics.js b/Source/DataSources/ModelGraphics.js index 1533df353e0c..1e976495eb02 100644 --- a/Source/DataSources/ModelGraphics.js +++ b/Source/DataSources/ModelGraphics.js @@ -31,6 +31,7 @@ define([ * @param {Property} [options.show=true] A boolean Property specifying the visibility of the model. * @param {Property} [options.scale=1.0] A numeric Property specifying a uniform linear scale. * @param {Property} [options.minimumPixelSize=0.0] A numeric Property specifying the approximate minimum pixel size of the model regardless of zoom. + * @param {Property} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize. * * @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} @@ -42,6 +43,8 @@ define([ this._scaleSubscription = undefined; this._minimumPixelSize = undefined; this._minimumPixelSizeSubscription = undefined; + this._maximumScale = undefined; + this._maximumScaleSubscription = undefined; this._uri = undefined; this._uriSubscription = undefined; this._definitionChanged = new Event(); @@ -91,6 +94,15 @@ define([ */ minimumPixelSize : createPropertyDescriptor('minimumPixelSize'), + /** + * Gets or sets the numeric Property specifying the maximum scale + * size of a model. This property is used as an upper limit for + * {@link ModelGraphics#minimumPixelSize}. + * @memberof ModelGraphics.prototype + * @type {Property} + */ + maximumScale : createPropertyDescriptor('maximumScale'), + /** * Gets or sets the string Property specifying the URI of the glTF asset. * @memberof ModelGraphics.prototype @@ -112,6 +124,7 @@ define([ result.show = this.show; result.scale = this.scale; result.minimumPixelSize = this.minimumPixelSize; + result.maximumScale = this.maximumScale; result.uri = this.uri; return result; }; @@ -132,6 +145,7 @@ define([ this.show = defaultValue(this.show, source.show); this.scale = defaultValue(this.scale, source.scale); this.minimumPixelSize = defaultValue(this.minimumPixelSize, source.minimumPixelSize); + this.maximumScale = defaultValue(this.maximumScale, source.maximumScale); this.uri = defaultValue(this.uri, source.uri); }; diff --git a/Source/DataSources/ModelVisualizer.js b/Source/DataSources/ModelVisualizer.js index cd40d0761c8e..f2824c89b2da 100644 --- a/Source/DataSources/ModelVisualizer.js +++ b/Source/DataSources/ModelVisualizer.js @@ -125,6 +125,7 @@ define([ model.show = true; model.scale = Property.getValueOrDefault(modelGraphics._scale, time, defaultScale); model.minimumPixelSize = Property.getValueOrDefault(modelGraphics._minimumPixelSize, time, defaultMinimumPixelSize); + model.maximumScale = Property.getValueOrUndefined(modelGraphics._maximumScale, time); model.modelMatrix = Matrix4.clone(modelMatrix, model.modelMatrix); } return true; diff --git a/Source/Scene/Model.js b/Source/Scene/Model.js index ea72a7a31e33..c277f27bf21d 100644 --- a/Source/Scene/Model.js +++ b/Source/Scene/Model.js @@ -291,6 +291,7 @@ define([ * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Number} [options.scale=1.0] A uniform scale applied to this model. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom. + * @param {Number} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}. * @param {Boolean} [options.allowPicking=true] When true, each glTF mesh and primitive is pickable with {@link Scene#pick}. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded. @@ -415,6 +416,16 @@ define([ this.minimumPixelSize = defaultValue(options.minimumPixelSize, 0.0); this._minimumPixelSize = this.minimumPixelSize; + /** + * The maximum scale size for a model. This can be used to give + * an upper limit to the {@link Model#minimumPixelSize}, ensuring that the model + * is never an unreasonable scale. + * + * @type {Number} + */ + this.maximumScale = options.maximumScale; + this._maximumScale = this.maximumScale; + /** * User-defined object returned when the model is picked. * @@ -605,7 +616,7 @@ define([ /** * The model's bounding sphere in its local coordinate system. This does not take into - * account glTF animations and skins. + * account glTF animations and skins nor does it take into account {@link Model#minimumPixelSize}. * * @memberof Model.prototype * @@ -629,7 +640,8 @@ define([ //>>includeEnd('debug'); var nonUniformScale = Matrix4.getScale(this.modelMatrix, boundingSphereCartesian3Scratch); - Cartesian3.multiplyByScalar(nonUniformScale, this.scale, nonUniformScale); + var scale = defined(this.maximumScale) ? Math.min(this.maximumScale, this.scale) : this.scale; + Cartesian3.multiplyByScalar(nonUniformScale, scale, nonUniformScale); var scaledBoundingSphere = this._scaledBoundingSphere; scaledBoundingSphere.center = Cartesian3.multiplyComponents(this._boundingSphere.center, nonUniformScale, scaledBoundingSphere.center); @@ -823,6 +835,7 @@ define([ * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates. * @param {Number} [options.scale=1.0] A uniform scale applied to this model. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom. + * @param {Number} [options.maxiumumScale] The maximum scale for the model. * @param {Boolean} [options.allowPicking=true] When true, each glTF mesh and primitive is pickable with {@link Scene#pick}. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each {@link DrawCommand} in the model. @@ -849,6 +862,7 @@ define([ * modelMatrix : modelMatrix, * scale : 2.0, // double size * minimumPixelSize : 128, // never smaller than 128 pixels + * maximumScale: 20000, // never larger than 20000 * model size (overrides minimumPixelSize) * allowPicking : false, // not pickable * debugShowBoundingVolume : false, // default * debugWireframe : false @@ -2866,7 +2880,7 @@ define([ } } - return scale; + return defined(model.maximumScale) ? Math.min(model.maximumScale, scale) : scale; } function releaseCachedGltf(model) { @@ -3052,12 +3066,14 @@ define([ // Model's model matrix needs to be updated var modelTransformChanged = !Matrix4.equals(this._modelMatrix, this.modelMatrix) || (this._scale !== this.scale) || - (this._minimumPixelSize !== this.minimumPixelSize) || (this.minimumPixelSize !== 0.0); // Minimum pixel size changed or is enabled + (this._minimumPixelSize !== this.minimumPixelSize) || (this.minimumPixelSize !== 0.0) || // Minimum pixel size changed or is enabled + (this._maximumScale !== this.maximumScale); if (modelTransformChanged || justLoaded) { Matrix4.clone(this.modelMatrix, this._modelMatrix); this._scale = this.scale; this._minimumPixelSize = this.minimumPixelSize; + this._maximumScale = this.maximumScale; var scale = getScale(this, context, frameState); var computedModelMatrix = this._computedModelMatrix; diff --git a/Specs/DataSources/ModelGraphicsSpec.js b/Specs/DataSources/ModelGraphicsSpec.js index cce969078988..5aaef97cb833 100644 --- a/Specs/DataSources/ModelGraphicsSpec.js +++ b/Specs/DataSources/ModelGraphicsSpec.js @@ -13,7 +13,8 @@ defineSuite([ uri : '0', scale : 1, show : false, - minimumPixelSize : 2 + minimumPixelSize : 2, + maximumScale: 200 }; var model = new ModelGraphics(options); @@ -21,11 +22,13 @@ defineSuite([ expect(model.scale).toBeInstanceOf(ConstantProperty); expect(model.show).toBeInstanceOf(ConstantProperty); expect(model.minimumPixelSize).toBeInstanceOf(ConstantProperty); + expect(model.maximumScale).toBeInstanceOf(ConstantProperty); expect(model.uri.getValue()).toEqual(options.uri); expect(model.scale.getValue()).toEqual(options.scale); expect(model.show.getValue()).toEqual(options.show); expect(model.minimumPixelSize.getValue()).toEqual(options.minimumPixelSize); + expect(model.maximumScale.getValue()).toEqual(options.maximumScale); }); it('merge assigns unassigned properties', function() { @@ -34,6 +37,7 @@ defineSuite([ source.show = new ConstantProperty(true); source.scale = new ConstantProperty(1.0); source.minimumPixelSize = new ConstantProperty(2.0); + source.maximumScale = new ConstantProperty(200.0); var target = new ModelGraphics(); target.merge(source); @@ -42,6 +46,7 @@ defineSuite([ expect(target.show).toBe(source.show); expect(target.scale).toBe(source.scale); expect(target.minimumPixelSize).toBe(source.minimumPixelSize); + expect(target.maximumScale).toBe(source.maximumScale); }); it('merge does not assign assigned properties', function() { @@ -50,17 +55,20 @@ defineSuite([ source.show = new ConstantProperty(true); source.scale = new ConstantProperty(1.0); source.minimumPixelSize = new ConstantProperty(2.0); + source.maximumScale = new ConstantProperty(200.0); var uri = new ConstantProperty(''); var show = new ConstantProperty(true); var scale = new ConstantProperty(1.0); var minimumPixelSize = new ConstantProperty(2.0); + var maximumScale = new ConstantProperty(200.0); var target = new ModelGraphics(); target.uri = uri; target.show = show; target.scale = scale; target.minimumPixelSize = minimumPixelSize; + target.maximumScale = maximumScale; target.merge(source); @@ -68,6 +76,7 @@ defineSuite([ expect(target.show).toBe(show); expect(target.scale).toBe(scale); expect(target.minimumPixelSize).toBe(minimumPixelSize); + expect(target.maximumScale).toBe(maximumScale); }); it('clone works', function() { @@ -76,12 +85,14 @@ defineSuite([ source.show = new ConstantProperty(true); source.scale = new ConstantProperty(1.0); source.minimumPixelSize = new ConstantProperty(2.0); + source.maximumScale = new ConstantProperty(200.0); var result = source.clone(); expect(result.uri).toBe(source.uri); expect(result.show).toBe(source.show); expect(result.scale).toBe(source.scale); expect(result.minimumPixelSize).toBe(source.minimumPixelSize); + expect(result.maximumScale).toBe(source.maximumScale); }); it('merge throws if source undefined', function() { diff --git a/Specs/Scene/ModelSpec.js b/Specs/Scene/ModelSpec.js index 753be25a5f26..b9ec15dda770 100644 --- a/Specs/Scene/ModelSpec.js +++ b/Specs/Scene/ModelSpec.js @@ -88,6 +88,7 @@ defineSuite([ })); modelPromises.push(loadModel(cesiumAirUrl, { minimumPixelSize : 1, + maximumScale : 200, asynchronous : false }).then(function(model) { cesiumAirModel = model; @@ -126,6 +127,7 @@ defineSuite([ show : false, scale : options.scale, minimumPixelSize : options.minimumPixelSize, + maximumScale : options.maximumScale, id : url, // for picking tests asynchronous : options.asynchronous, releaseGltfJson : options.releaseGltfJson, @@ -172,6 +174,7 @@ defineSuite([ expect(texturedBoxModel.modelMatrix).toEqual(modelMatrix); expect(texturedBoxModel.scale).toEqual(1.0); expect(texturedBoxModel.minimumPixelSize).toEqual(0.0); + expect(texturedBoxModel.maximumScale).toBeUndefined(); expect(texturedBoxModel.id).toEqual(texturedBoxUrl); expect(texturedBoxModel.allowPicking).toEqual(true); expect(texturedBoxModel.activeAnimations).toBeDefined(); @@ -448,6 +451,20 @@ defineSuite([ texturedBoxModel.scale = originalScale; }); + it('boundingSphere returns the bounding sphere when maximumScale is reached', function() { + var originalScale = texturedBoxModel.scale; + var originalMaximumScale = texturedBoxModel.maximumScale; + texturedBoxModel.scale = 20; + texturedBoxModel.maximumScale = 10; + + var boundingSphere = texturedBoxModel.boundingSphere; + expect(boundingSphere.center).toEqualEpsilon(new Cartesian3(0.0, -2.5, 0.0), CesiumMath.EPSILON3); + expect(boundingSphere.radius).toEqualEpsilon(7.5, CesiumMath.EPSILON3); + + texturedBoxModel.scale = originalScale; + texturedBoxModel.maximumScale = originalMaximumScale; + }); + it('boundingSphere returns the bounding sphere when modelMatrix has non-uniform scale', function() { var originalMatrix = Matrix4.clone(texturedBoxModel.modelMatrix); Matrix4.multiplyByScale(texturedBoxModel.modelMatrix, new Cartesian3(2, 5, 10), texturedBoxModel.modelMatrix); @@ -672,6 +689,7 @@ defineSuite([ it('loads cesiumAir', function() { expect(cesiumAirModel.minimumPixelSize).toEqual(1); + expect(cesiumAirModel.maximumScale).toEqual(200); expect(cesiumAirModel.asynchronous).toEqual(false); });