define([
    '../Core/Cartesian3',
    '../Core/Cartographic',
    '../Core/Check',
    '../Core/defined',
    '../Core/defineProperties',
    '../Core/destroyObject',
    '../Core/Event',
    '../Core/Iso8601',
    '../Core/Math',
    '../Scene/HeightReference',
    './Property'
], function(
    Cartesian3,
    Cartographic,
    Check,
    defined,
    defineProperties,
    destroyObject,
    Event,
    Iso8601,
    CesiumMath,
    HeightReference,
    Property) {
    'use strict';

    var scratchPosition = new Cartesian3();
    var scratchCarto = new Cartographic();

    /**
     * @private
     */
    function TerrainOffsetProperty(scene, heightReferenceProperty, extrudedHeightReferenceProperty, positionProperty) {
        //>>includeStart('debug', pragmas.debug);
        Check.defined('scene', scene);
        Check.defined('positionProperty', positionProperty);
        //>>includeEnd('debug');

        this._scene = scene;
        this._heightReference = heightReferenceProperty;
        this._extrudedHeightReference = extrudedHeightReferenceProperty;
        this._positionProperty = positionProperty;

        this._position = new Cartesian3();
        this._cartographicPosition = new Cartographic();
        this._normal = new Cartesian3();

        this._definitionChanged = new Event();
        this._terrainHeight = 0;
        this._removeCallbackFunc = undefined;
        this._removeEventListener = undefined;
        this._removeModeListener = undefined;

        var that = this;
        if (defined(scene.globe)) {
            this._removeEventListener = scene.terrainProviderChanged.addEventListener(function() {
                that._updateClamping();
            });
            this._removeModeListener = scene.morphComplete.addEventListener(function() {
                that._updateClamping();
            });
        }

        if (positionProperty.isConstant) {
            var position = positionProperty.getValue(Iso8601.MINIMUM_VALUE, scratchPosition);
            if (!defined(position) || Cartesian3.equals(position, Cartesian3.ZERO) || !defined(scene.globe)) {
                return;
            }
            this._position = Cartesian3.clone(position, this._position);

            this._updateClamping();

            this._normal = scene.globe.ellipsoid.geodeticSurfaceNormal(position, this._normal);
        }
    }

    defineProperties(TerrainOffsetProperty.prototype, {
        /**
         * Gets a value indicating if this property is constant.
         * @memberof TerrainOffsetProperty.prototype
         *
         * @type {Boolean}
         * @readonly
         */
        isConstant : {
            get : function() {
                return false;
            }
        },
        /**
         * Gets the event that is raised whenever the definition of this property changes.
         * @memberof TerrainOffsetProperty.prototype
         *
         * @type {Event}
         * @readonly
         */
        definitionChanged : {
            get : function() {
                return this._definitionChanged;
            }
        }
    });

    /**
     * @private
     */
    TerrainOffsetProperty.prototype._updateClamping = function() {
        if (defined(this._removeCallbackFunc)) {
            this._removeCallbackFunc();
        }

        var scene = this._scene;
        var globe = scene.globe;
        var position = this._position;

        if (!defined(globe) || Cartesian3.equals(position, Cartesian3.ZERO)) {
            this._terrainHeight = 0;
            return;
        }
        var ellipsoid = globe.ellipsoid;
        var surface = globe._surface;

        var that = this;
        var cartographicPosition = ellipsoid.cartesianToCartographic(position, this._cartographicPosition);
        var height = globe.getHeight(cartographicPosition);
        if (defined(height)) {
            this._terrainHeight = height;
        } else {
            this._terrainHeight = 0;
        }

        function updateFunction(clampedPosition) {
            var carto = ellipsoid.cartesianToCartographic(clampedPosition, scratchCarto);
            that._terrainHeight = carto.height;
            that.definitionChanged.raiseEvent();
        }
        this._removeCallbackFunc = surface.updateHeight(cartographicPosition, updateFunction);
    };

    /**
     * Gets the height relative to the terrain based on the positions.
     *
     * @returns {Cartesian3} The offset
     */
    TerrainOffsetProperty.prototype.getValue = function(time, result) {
        var heightReference = Property.getValueOrDefault(this._heightReference, time, HeightReference.NONE);
        var extrudedHeightReference = Property.getValueOrDefault(this._extrudedHeightReference, time, HeightReference.NONE);

        if (heightReference === HeightReference.NONE && extrudedHeightReference !== HeightReference.RELATIVE_TO_GROUND) {
            this._position = Cartesian3.clone(Cartesian3.ZERO, this._position);
            return Cartesian3.clone(Cartesian3.ZERO, result);
        }

        if (this._positionProperty.isConstant) {
            return Cartesian3.multiplyByScalar(this._normal, this._terrainHeight, result);
        }

        var scene = this._scene;
        var position = this._positionProperty.getValue(time, scratchPosition);
        if (!defined(position) || Cartesian3.equals(position, Cartesian3.ZERO) || !defined(scene.globe)) {
            return Cartesian3.clone(Cartesian3.ZERO, result);
        }

        if (Cartesian3.equalsEpsilon(this._position, position, CesiumMath.EPSILON10)) {
            return Cartesian3.multiplyByScalar(this._normal, this._terrainHeight, result);
        }

        this._position = Cartesian3.clone(position, this._position);

        this._updateClamping();

        var normal = scene.globe.ellipsoid.geodeticSurfaceNormal(position, this._normal);
        return Cartesian3.multiplyByScalar(normal, this._terrainHeight, result);
    };

    TerrainOffsetProperty.prototype.isDestroyed = function() {
        return false;
    };

    TerrainOffsetProperty.prototype.destroy = function() {
        if (defined(this._removeEventListener)) {
            this._removeEventListener();
        }
        if (defined(this._removeModeListener)) {
            this._removeModeListener();
        }
        if (defined(this._removeCallbackFunc)) {
            this._removeCallbackFunc();
        }
        return destroyObject(this);
    };

    /**
     * A function which creates one or more providers.
     * @callback TerrainOffsetProperty~PositionFunction
     * @param {JulianDate} time The clock time at which to retrieve the position
     * @param {Cartesian3} result The result position
     * @returns {Cartesian3} The position at which to do the terrain height check
     */

    return TerrainOffsetProperty;
});

@@ -257,16 +257,42 @@
 rence',%0A
+    '../Scene/SceneMode',%0A
     './P
@@ -473,16 +473,31 @@
 erence,%0A
+    SceneMode,%0A
     Prop
@@ -4044,24 +4044,80 @@
 Position) %7B%0A
+            if (scene.mode === SceneMode.SCENE3D) %7B%0A    
             
@@ -4202,24 +4202,28 @@
             
+    
 that._terrai
@@ -4238,32 +4238,124 @@
 = carto.height;%0A
+            %7D else %7B%0A                that._terrainHeight = clampedPosition.x;%0A            %7D%0A
             that

fix height 2D/CV