diff --git a/Apps/Sandcastle/gallery/development/Geometry Offset Attribute.html b/Apps/Sandcastle/gallery/development/Geometry Offset Attribute.html new file mode 100644 index 000000000000..19c38c511ef9 --- /dev/null +++ b/Apps/Sandcastle/gallery/development/Geometry Offset Attribute.html @@ -0,0 +1,195 @@ + + + + + + + + + Cesium Demo + + + + + + +
+

Loading...

+
+ + + diff --git a/Apps/Sandcastle/gallery/development/Geometry Offset Attribute.jpg b/Apps/Sandcastle/gallery/development/Geometry Offset Attribute.jpg new file mode 100644 index 000000000000..d0420c87503a Binary files /dev/null and b/Apps/Sandcastle/gallery/development/Geometry Offset Attribute.jpg differ diff --git a/Source/Core/CorridorGeometry.js b/Source/Core/CorridorGeometry.js index 476a1c3a3058..132d9e8d291e 100644 --- a/Source/Core/CorridorGeometry.js +++ b/Source/Core/CorridorGeometry.js @@ -1,8 +1,10 @@ define([ + './arrayFill', './arrayRemoveDuplicates', './BoundingSphere', './Cartesian3', './Cartographic', + './Check', './ComponentDatatype', './CornerType', './CorridorGeometryLibrary', @@ -14,6 +16,7 @@ define([ './Geometry', './GeometryAttribute', './GeometryAttributes', + './GeometryOffsetAttribute', './IndexDatatype', './Math', './PolygonPipeline', @@ -21,10 +24,12 @@ define([ './Rectangle', './VertexFormat' ], function( + arrayFill, arrayRemoveDuplicates, BoundingSphere, Cartesian3, Cartographic, + Check, ComponentDatatype, CornerType, CorridorGeometryLibrary, @@ -36,6 +41,7 @@ define([ Geometry, GeometryAttribute, GeometryAttributes, + GeometryOffsetAttribute, IndexDatatype, CesiumMath, PolygonPipeline, @@ -619,6 +625,21 @@ define([ attributes.normal = undefined; } } + if (defined(params.offsetAttribute)) { + var applyOffset = new Uint8Array(size * 6); + if (params.offsetAttribute === GeometryOffsetAttribute.TOP) { + applyOffset = arrayFill(applyOffset, 1, 0, size); // top face + applyOffset = arrayFill(applyOffset, 1, size*2, size * 4); // top wall + } else { + var applyOffsetValue = params.offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + applyOffset = arrayFill(applyOffset, applyOffsetValue); + } + attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } var iLength = indices.length; var twoSize = size + size; @@ -826,13 +847,14 @@ define([ this._granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE); this._shadowVolume = defaultValue(options.shadowVolume, false); this._workerName = 'createCorridorGeometry'; + this._offsetAttribute = options.offsetAttribute; this._rectangle = undefined; /** * The number of elements used to pack the object into an array. * @type {Number} */ - this.packedLength = 1 + positions.length * Cartesian3.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 6; + this.packedLength = 1 + positions.length * Cartesian3.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 7; } /** @@ -875,7 +897,8 @@ define([ array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._cornerType; array[startingIndex++] = value._granularity; - array[startingIndex] = value._shadowVolume ? 1.0 : 0.0; + array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0; + array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; @@ -891,7 +914,8 @@ define([ extrudedHeight : undefined, cornerType : undefined, granularity : undefined, - shadowVolume: undefined + shadowVolume: undefined, + offsetAttribute: undefined }; /** @@ -929,7 +953,8 @@ define([ var extrudedHeight = array[startingIndex++]; var cornerType = array[startingIndex++]; var granularity = array[startingIndex++]; - var shadowVolume = array[startingIndex] === 1.0; + var shadowVolume = array[startingIndex++] === 1.0; + var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions.positions = positions; @@ -939,6 +964,8 @@ define([ scratchOptions.cornerType = cornerType; scratchOptions.granularity = granularity; scratchOptions.shadowVolume = shadowVolume; + scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; + return new CorridorGeometry(scratchOptions); } @@ -951,6 +978,7 @@ define([ result._cornerType = cornerType; result._granularity = granularity; result._shadowVolume = shadowVolume; + result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; @@ -991,11 +1019,24 @@ define([ params.height = height; params.extrudedHeight = extrudedHeight; params.shadowVolume = corridorGeometry._shadowVolume; + params.offsetAttribute = corridorGeometry._offsetAttribute; attr = computePositionsExtruded(params, vertexFormat); } else { var computedPositions = CorridorGeometryLibrary.computePositions(params); attr = combine(computedPositions, vertexFormat, ellipsoid); attr.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(attr.attributes.position.values, height, ellipsoid); + + if (defined(corridorGeometry._offsetAttribute)) { + var applyOffsetValue = corridorGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + var length = attr.attributes.position.values.length; + var applyOffset = new Uint8Array(length / 3); + arrayFill(applyOffset, applyOffsetValue); + attr.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } } var attributes = attr.attributes; var boundingSphere = BoundingSphere.fromVertices(attributes.position.values, undefined, 3); diff --git a/Source/Core/CorridorOutlineGeometry.js b/Source/Core/CorridorOutlineGeometry.js index 3c8281963c34..069da5df4f1b 100644 --- a/Source/Core/CorridorOutlineGeometry.js +++ b/Source/Core/CorridorOutlineGeometry.js @@ -1,4 +1,5 @@ define([ + './arrayFill', './arrayRemoveDuplicates', './BoundingSphere', './Cartesian3', @@ -12,11 +13,13 @@ define([ './Geometry', './GeometryAttribute', './GeometryAttributes', + './GeometryOffsetAttribute', './IndexDatatype', './Math', './PolygonPipeline', './PrimitiveType' ], function( + arrayFill, arrayRemoveDuplicates, BoundingSphere, Cartesian3, @@ -30,6 +33,7 @@ define([ Geometry, GeometryAttribute, GeometryAttributes, + GeometryOffsetAttribute, IndexDatatype, CesiumMath, PolygonPipeline, @@ -282,6 +286,22 @@ define([ attributes.position.values = newPositions; length /= 3; + if (defined(params.offsetAttribute)) { + var applyOffset = new Uint8Array(length * 2); + if (params.offsetAttribute === GeometryOffsetAttribute.TOP) { + applyOffset = arrayFill(applyOffset, 1, 0, length); + } else { + var applyOffsetValue = params.offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + applyOffset = arrayFill(applyOffset, applyOffsetValue); + } + + attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } + var i; var iLength = indices.length; var newIndices = IndexDatatype.createTypedArray(newPositions.length / 3, (iLength + wallIndices.length) * 2); @@ -351,13 +371,14 @@ define([ this._extrudedHeight = Math.min(height, extrudedHeight); this._cornerType = defaultValue(options.cornerType, CornerType.ROUNDED); this._granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE); + this._offsetAttribute = options.offsetAttribute; this._workerName = 'createCorridorOutlineGeometry'; /** * The number of elements used to pack the object into an array. * @type {Number} */ - this.packedLength = 1 + positions.length * Cartesian3.packedLength + Ellipsoid.packedLength + 5; + this.packedLength = 1 + positions.length * Cartesian3.packedLength + Ellipsoid.packedLength + 6; } /** @@ -392,7 +413,8 @@ define([ array[startingIndex++] = value._height; array[startingIndex++] = value._extrudedHeight; array[startingIndex++] = value._cornerType; - array[startingIndex] = value._granularity; + array[startingIndex++] = value._granularity; + array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; @@ -405,7 +427,8 @@ define([ height : undefined, extrudedHeight : undefined, cornerType : undefined, - granularity : undefined + granularity : undefined, + offsetAttribute: undefined }; /** @@ -437,7 +460,8 @@ define([ var height = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; var cornerType = array[startingIndex++]; - var granularity = array[startingIndex]; + var granularity = array[startingIndex++]; + var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions.positions = positions; @@ -446,6 +470,7 @@ define([ scratchOptions.extrudedHeight = extrudedHeight; scratchOptions.cornerType = cornerType; scratchOptions.granularity = granularity; + scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return new CorridorOutlineGeometry(scratchOptions); } @@ -456,6 +481,7 @@ define([ result._extrudedHeight = extrudedHeight; result._cornerType = cornerType; result._granularity = granularity; + result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; @@ -494,11 +520,24 @@ define([ if (extrude) { params.height = height; params.extrudedHeight = extrudedHeight; + params.offsetAttribute = corridorOutlineGeometry._offsetAttribute; attr = computePositionsExtruded(params); } else { var computedPositions = CorridorGeometryLibrary.computePositions(params); attr = combine(computedPositions, params.cornerType); attr.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(attr.attributes.position.values, height, ellipsoid); + + if (defined(corridorOutlineGeometry._offsetAttribute)) { + var length = attr.attributes.position.values.length; + var applyOffset = new Uint8Array(length / 3); + var offsetValue = corridorOutlineGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + arrayFill(applyOffset, offsetValue); + attr.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } } var attributes = attr.attributes; var boundingSphere = BoundingSphere.fromVertices(attributes.position.values, undefined, 3); diff --git a/Source/Core/EllipseGeometry.js b/Source/Core/EllipseGeometry.js index 246d41320cbb..7db56b0ce18d 100644 --- a/Source/Core/EllipseGeometry.js +++ b/Source/Core/EllipseGeometry.js @@ -1,4 +1,5 @@ define([ + './arrayFill', './BoundingSphere', './Cartesian2', './Cartesian3', @@ -15,6 +16,7 @@ define([ './GeometryAttribute', './GeometryAttributes', './GeometryInstance', + './GeometryOffsetAttribute', './GeometryPipeline', './IndexDatatype', './Math', @@ -24,6 +26,7 @@ define([ './Rectangle', './VertexFormat' ], function( + arrayFill, BoundingSphere, Cartesian2, Cartesian3, @@ -40,6 +43,7 @@ define([ GeometryAttribute, GeometryAttributes, GeometryInstance, + GeometryOffsetAttribute, GeometryPipeline, IndexDatatype, CesiumMath, @@ -244,6 +248,22 @@ define([ }); } + if (extrude && defined(options.offsetAttribute)) { + var offsetAttribute = new Uint8Array(size); + if (options.offsetAttribute === GeometryOffsetAttribute.TOP) { + offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); + } else { + var offsetValue = options.offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + offsetAttribute = arrayFill(offsetAttribute, offsetValue); + } + + attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values : offsetAttribute + }); + } + return attributes; } @@ -561,6 +581,21 @@ define([ }); } + if (defined(options.offsetAttribute)) { + var offsetAttribute = new Uint8Array(size); + if (options.offsetAttribute === GeometryOffsetAttribute.TOP) { + offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); + } else { + var offsetValue = options.offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + offsetAttribute = arrayFill(offsetAttribute, offsetValue); + } + attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values : offsetAttribute + }); + } + return attributes; } @@ -753,6 +788,7 @@ define([ this._extrudedHeight = Math.min(extrudedHeight, height); this._shadowVolume = defaultValue(options.shadowVolume, false); this._workerName = 'createEllipseGeometry'; + this._offsetAttribute = options.offsetAttribute; this._rectangle = undefined; this._textureCoordinateRotationPoints = undefined; @@ -762,7 +798,7 @@ define([ * The number of elements used to pack the object into an array. * @type {Number} */ - EllipseGeometry.packedLength = Cartesian3.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 8; + EllipseGeometry.packedLength = Cartesian3.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 9; /** * Stores the provided instance into the provided array. @@ -801,7 +837,8 @@ define([ array[startingIndex++] = value._height; array[startingIndex++] = value._granularity; array[startingIndex++] = value._extrudedHeight; - array[startingIndex] = value._shadowVolume ? 1.0 : 0.0; + array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0; + array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; @@ -820,7 +857,8 @@ define([ height : undefined, granularity : undefined, extrudedHeight : undefined, - shadowVolume: undefined + shadowVolume: undefined, + offsetAttribute: undefined }; /** @@ -856,7 +894,8 @@ define([ var height = array[startingIndex++]; var granularity = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; - var shadowVolume = array[startingIndex] === 1.0; + var shadowVolume = array[startingIndex++] === 1.0; + var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions.height = height; @@ -867,6 +906,8 @@ define([ scratchOptions.semiMajorAxis = semiMajorAxis; scratchOptions.semiMinorAxis = semiMinorAxis; scratchOptions.shadowVolume = shadowVolume; + scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; + return new EllipseGeometry(scratchOptions); } @@ -881,6 +922,7 @@ define([ result._granularity = granularity; result._extrudedHeight = extrudedHeight; result._shadowVolume = shadowVolume; + result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; @@ -916,9 +958,22 @@ define([ if (extrude) { options.extrudedHeight = extrudedHeight; options.shadowVolume = ellipseGeometry._shadowVolume; + options.offsetAttribute = ellipseGeometry._offsetAttribute; geometry = computeExtrudedEllipse(options); } else { geometry = computeEllipse(options); + + if (defined(ellipseGeometry._offsetAttribute)) { + var length = geometry.attributes.position.values.length; + var applyOffset = new Uint8Array(length / 3); + var offsetValue = ellipseGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + arrayFill(applyOffset, offsetValue); + geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } } return new Geometry({ diff --git a/Source/Core/EllipseOutlineGeometry.js b/Source/Core/EllipseOutlineGeometry.js index 0bf0e434357c..6ed83c0d2e99 100644 --- a/Source/Core/EllipseOutlineGeometry.js +++ b/Source/Core/EllipseOutlineGeometry.js @@ -1,4 +1,5 @@ define([ + './arrayFill', './BoundingSphere', './Cartesian3', './ComponentDatatype', @@ -10,10 +11,12 @@ define([ './Geometry', './GeometryAttribute', './GeometryAttributes', + './GeometryOffsetAttribute', './IndexDatatype', './Math', './PrimitiveType' ], function( + arrayFill, BoundingSphere, Cartesian3, ComponentDatatype, @@ -25,6 +28,7 @@ define([ Geometry, GeometryAttribute, GeometryAttributes, + GeometryOffsetAttribute, IndexDatatype, CesiumMath, PrimitiveType) { @@ -89,6 +93,23 @@ define([ positions = attributes.position.values; var boundingSphere = BoundingSphere.union(topBoundingSphere, bottomBoundingSphere); var length = positions.length/3; + + if (defined(options.offsetAttribute)) { + var applyOffset = new Uint8Array(length); + if (options.offsetAttribute === GeometryOffsetAttribute.TOP) { + applyOffset = arrayFill(applyOffset, 1, 0, length / 2); + } else { + var offsetValue = options.offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + applyOffset = arrayFill(applyOffset, offsetValue); + } + + attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } + var numberOfVerticalLines = defaultValue(options.numberOfVerticalLines, 16); numberOfVerticalLines = CesiumMath.clamp(numberOfVerticalLines, 0, length/2); @@ -194,6 +215,7 @@ define([ this._granularity = granularity; this._extrudedHeight = Math.min(extrudedHeight, height); this._numberOfVerticalLines = Math.max(defaultValue(options.numberOfVerticalLines, 16), 0); + this._offsetAttribute = options.offsetAttribute; this._workerName = 'createEllipseOutlineGeometry'; } @@ -201,7 +223,7 @@ define([ * The number of elements used to pack the object into an array. * @type {Number} */ - EllipseOutlineGeometry.packedLength = Cartesian3.packedLength + Ellipsoid.packedLength + 7; + EllipseOutlineGeometry.packedLength = Cartesian3.packedLength + Ellipsoid.packedLength + 8; /** * Stores the provided instance into the provided array. @@ -236,7 +258,8 @@ define([ array[startingIndex++] = value._height; array[startingIndex++] = value._granularity; array[startingIndex++] = value._extrudedHeight; - array[startingIndex] = value._numberOfVerticalLines; + array[startingIndex++] = value._numberOfVerticalLines; + array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; @@ -252,7 +275,8 @@ define([ height : undefined, granularity : undefined, extrudedHeight : undefined, - numberOfVerticalLines : undefined + numberOfVerticalLines : undefined, + offsetAttribute: undefined }; /** @@ -284,7 +308,8 @@ define([ var height = array[startingIndex++]; var granularity = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; - var numberOfVerticalLines = array[startingIndex]; + var numberOfVerticalLines = array[startingIndex++]; + var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions.height = height; @@ -294,6 +319,8 @@ define([ scratchOptions.semiMajorAxis = semiMajorAxis; scratchOptions.semiMinorAxis = semiMinorAxis; scratchOptions.numberOfVerticalLines = numberOfVerticalLines; + scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; + return new EllipseOutlineGeometry(scratchOptions); } @@ -306,6 +333,7 @@ define([ result._granularity = granularity; result._extrudedHeight = extrudedHeight; result._numberOfVerticalLines = numberOfVerticalLines; + result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; @@ -339,9 +367,22 @@ define([ var geometry; if (extrude) { options.extrudedHeight = extrudedHeight; + options.offsetAttribute = ellipseGeometry._offsetAttribute; geometry = computeExtrudedEllipse(options); } else { geometry = computeEllipse(options); + + if (defined(ellipseGeometry._offsetAttribute)) { + var length = geometry.attributes.position.values.length; + var applyOffset = new Uint8Array(length / 3); + var offsetValue = ellipseGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + arrayFill(applyOffset, offsetValue); + geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } } return new Geometry({ diff --git a/Source/Core/GeometryOffsetAttribute.js b/Source/Core/GeometryOffsetAttribute.js new file mode 100644 index 000000000000..63bd42a70504 --- /dev/null +++ b/Source/Core/GeometryOffsetAttribute.js @@ -0,0 +1,18 @@ +define([ + '../Core/freezeObject' + ], function( + freezeObject) { + 'use strict'; + + /** + * Represents which vertices should have a value of `true` for the `applyOffset` attribute + * @private + */ + var GeometryOffsetAttribute = { + NONE : 0, + TOP : 1, + ALL : 2 + }; + + return freezeObject(GeometryOffsetAttribute); +}); diff --git a/Source/Core/GeometryPipeline.js b/Source/Core/GeometryPipeline.js index c0684102473a..25ab70f16775 100644 --- a/Source/Core/GeometryPipeline.js +++ b/Source/Core/GeometryPipeline.js @@ -816,7 +816,6 @@ define([ } var boundingSphere = instance.geometry.boundingSphere; - if (defined(boundingSphere)) { instance.geometry.boundingSphere = BoundingSphere.transform(boundingSphere, modelMatrix, boundingSphere); } @@ -1035,6 +1034,7 @@ define([ var length = instances.length; for (var i = 0; i < length; ++i) { var instance = instances[i]; + if (defined(instance.geometry)) { instanceGeometry.push(instance); } else if (defined(instance.westHemisphereGeometry) && defined(instance.eastHemisphereGeometry)) { @@ -1944,12 +1944,19 @@ define([ var interpolateAndPackCartesian4 = generateBarycentricInterpolateFunction(Cartesian4, 4); var interpolateAndPackCartesian3 = generateBarycentricInterpolateFunction(Cartesian3, 3); var interpolateAndPackCartesian2 = generateBarycentricInterpolateFunction(Cartesian2, 2); + var interpolateAndPackBoolean = function(i0, i1, i2, coords, sourceValues, currentValues, insertedIndex) { + var v1 = sourceValues[i0] * coords.x; + var v2 = sourceValues[i1] * coords.y; + var v3 = sourceValues[i2] * coords.z; + currentValues[insertedIndex] = (v1 + v2 + v3) > CesiumMath.EPSILON6 ? 1 : 0; + }; var p0Scratch = new Cartesian3(); var p1Scratch = new Cartesian3(); var p2Scratch = new Cartesian3(); var barycentricScratch = new Cartesian3(); - function computeTriangleAttributes(i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, currentAttributes, customAttributeNames, customAttributesLength, allAttributes, insertedIndex) { + + function computeTriangleAttributes(i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, allAttributes, insertedIndex) { if (!defined(normals) && !defined(tangents) && !defined(bitangents) && !defined(texCoords) && !defined(extrudeDirections) && customAttributesLength === 0) { return; } @@ -1986,6 +1993,10 @@ define([ Cartesian3.pack(direction, currentAttributes.extrudeDirection.values, insertedIndex * 3); } + if (defined(applyOffset)) { + interpolateAndPackBoolean(i0, i1, i2, coords, applyOffset, currentAttributes.applyOffset.values, insertedIndex); + } + if (defined(tangents)) { interpolateAndPackCartesian3(i0, i1, i2, coords, tangents, currentAttributes.tangent.values, insertedIndex, true); } @@ -2054,7 +2065,8 @@ define([ bitangent : true, tangent : true, st : true, - extrudeDirection : true + extrudeDirection : true, + applyOffset: true }; function splitLongitudeTriangles(instance) { var geometry = instance.geometry; @@ -2065,6 +2077,7 @@ define([ var tangents = (defined(attributes.tangent)) ? attributes.tangent.values : undefined; var texCoords = (defined(attributes.st)) ? attributes.st.values : undefined; var extrudeDirections = (defined(attributes.extrudeDirection)) ? attributes.extrudeDirection.values : undefined; + var applyOffset = defined(attributes.applyOffset) ? attributes.applyOffset.values : undefined; var indices = geometry.indices; var customAttributeNames = []; @@ -2126,7 +2139,7 @@ define([ } insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, resultIndex < 3 ? i + resultIndex : -1, point); - computeTriangleAttributes(i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex); + computeTriangleAttributes(i0, i1, i2, point, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex); } } else { if (defined(result)) { @@ -2146,13 +2159,13 @@ define([ } insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i, p0); - computeTriangleAttributes(i0, i1, i2, p0, positions, normals, tangents, bitangents, texCoords, extrudeDirections, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex); + computeTriangleAttributes(i0, i1, i2, p0, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex); insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1); - computeTriangleAttributes(i0, i1, i2, p1, positions, normals, tangents, bitangents, texCoords, extrudeDirections, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex); + computeTriangleAttributes(i0, i1, i2, p1, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex); insertedIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 2, p2); - computeTriangleAttributes(i0, i1, i2, p2, positions, normals, tangents, bitangents, texCoords, extrudeDirections, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex); + computeTriangleAttributes(i0, i1, i2, p2, positions, normals, tangents, bitangents, texCoords, extrudeDirections, applyOffset, currentAttributes, customAttributeNames, customAttributesLength, attributes, insertedIndex); } } @@ -2164,10 +2177,25 @@ define([ var offsetScratch = new Cartesian3(); var offsetPointScratch = new Cartesian3(); + function computeLineAttributes(i0, i1, point, positions, insertIndex, currentAttributes, applyOffset) { + if (!defined(applyOffset)) { + return; + } + + var p0 = Cartesian3.fromArray(positions, i0 * 3, p0Scratch); + if (Cartesian3.equalsEpsilon(p0, point, CesiumMath.EPSILON10)) { + currentAttributes.applyOffset.values[insertIndex] = applyOffset[i0]; + } else { + currentAttributes.applyOffset.values[insertIndex] = applyOffset[i1]; + } + + } + function splitLongitudeLines(instance) { var geometry = instance.geometry; var attributes = geometry.attributes; var positions = attributes.position.values; + var applyOffset = defined(attributes.applyOffset) ? attributes.applyOffset.values : undefined; var indices = geometry.indices; var eastGeometry = copyGeometryForSplit(geometry); @@ -2193,6 +2221,7 @@ define([ var p0 = Cartesian3.fromArray(positions, i0 * 3, p0Scratch); var p1 = Cartesian3.fromArray(positions, i1 * 3, p1Scratch); + var insertIndex; if (Math.abs(p0.y) < CesiumMath.EPSILON6){ if (p0.y < 0.0) { @@ -2233,13 +2262,20 @@ define([ } var offsetPoint = Cartesian3.add(intersection, offset, offsetPointScratch); - insertSplitPoint(p0Attributes, p0Indices, p0IndexMap, indices, i, p0); - insertSplitPoint(p0Attributes, p0Indices, p0IndexMap, indices, -1, offsetPoint); + + insertIndex = insertSplitPoint(p0Attributes, p0Indices, p0IndexMap, indices, i, p0); + computeLineAttributes(i0, i1, p0, positions, insertIndex, p0Attributes, applyOffset); + + insertIndex = insertSplitPoint(p0Attributes, p0Indices, p0IndexMap, indices, -1, offsetPoint); + computeLineAttributes(i0, i1, offsetPoint, positions, insertIndex, p0Attributes, applyOffset); Cartesian3.negate(offset, offset); Cartesian3.add(intersection, offset, offsetPoint); - insertSplitPoint(p1Attributes, p1Indices, p1IndexMap, indices, -1, offsetPoint); - insertSplitPoint(p1Attributes, p1Indices, p1IndexMap, indices, i + 1, p1); + insertIndex = insertSplitPoint(p1Attributes, p1Indices, p1IndexMap, indices, -1, offsetPoint); + computeLineAttributes(i0, i1, offsetPoint, positions, insertIndex, p1Attributes, applyOffset); + + insertIndex = insertSplitPoint(p1Attributes, p1Indices, p1IndexMap, indices, i + 1, p1); + computeLineAttributes(i0, i1, p1, positions, insertIndex, p1Attributes, applyOffset); } else { var currentAttributes; var currentIndices; @@ -2255,8 +2291,11 @@ define([ currentIndexMap = eastGeometryIndexMap; } - insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i, p0); - insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1); + insertIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i, p0); + computeLineAttributes(i0, i1, p0, positions, insertIndex, currentAttributes, applyOffset); + + insertIndex = insertSplitPoint(currentAttributes, currentIndices, currentIndexMap, indices, i + 1, p1); + computeLineAttributes(i0, i1, p1, positions, insertIndex, currentAttributes, applyOffset); } } diff --git a/Source/Core/OffsetGeometryInstanceAttribute.js b/Source/Core/OffsetGeometryInstanceAttribute.js new file mode 100644 index 000000000000..5988c062437a --- /dev/null +++ b/Source/Core/OffsetGeometryInstanceAttribute.js @@ -0,0 +1,152 @@ +define([ + './Check', + './ComponentDatatype', + './defaultValue', + './defined', + './defineProperties' + ], function( + Check, + ComponentDatatype, + defaultValue, + defined, + defineProperties) { + 'use strict'; + + /** + * Value and type information for per-instance geometry attribute that determines if the geometry instance has a distance display condition. + * + * @alias OffsetGeometryInstanceAttribute + * @constructor + * + * @param {Number} [x=0] The x translation + * @param {Number} [y=0] The y translation + * @param {Number} [z=0] The z translation + * + * @example + * var center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883); + * var offset = Cartesian3.multiplyByScalar(ellipsoid.geodeticSurfaceNormal(center), 1000, new Cartesian3()); + * var instance = new Cesium.GeometryInstance({ + * geometry : new Cesium.BoxGeometry({ + * vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL, + * minimum : new Cesium.Cartesian3(-250000.0, -250000.0, -250000.0), + * maximum : new Cesium.Cartesian3(250000.0, 250000.0, 250000.0) + * }), + * modelMatrix : Cesium.Transforms.eastNorthUpToFixedFrame(center), + * id : 'box', + * attributes : { + * offset : Cesium.OffsetGeometryInstanceAttribute.fromCartesian3(offset) + * } + * }); + * + * @private + * @see GeometryInstance + * @see GeometryInstanceAttribute + */ + function OffsetGeometryInstanceAttribute(x, y, z) { + x = defaultValue(x, 0); + y = defaultValue(y, 0); + z = defaultValue(z, 0); + + /** + * The values for the attributes stored in a typed array. + * + * @type Float32Array + */ + this.value = new Float32Array([x, y, z]); + } + + defineProperties(OffsetGeometryInstanceAttribute.prototype, { + /** + * The datatype of each component in the attribute, e.g., individual elements in + * {@link OffsetGeometryInstanceAttribute#value}. + * + * @memberof OffsetGeometryInstanceAttribute.prototype + * + * @type {ComponentDatatype} + * @readonly + * + * @default {@link ComponentDatatype.FLOAT} + */ + componentDatatype : { + get : function() { + return ComponentDatatype.FLOAT; + } + }, + + /** + * The number of components in the attributes, i.e., {@link OffsetGeometryInstanceAttribute#value}. + * + * @memberof OffsetGeometryInstanceAttribute.prototype + * + * @type {Number} + * @readonly + * + * @default 3 + */ + componentsPerAttribute : { + get : function() { + return 3; + } + }, + + /** + * When true and componentDatatype is an integer format, + * indicate that the components should be mapped to the range [0, 1] (unsigned) + * or [-1, 1] (signed) when they are accessed as floating-point for rendering. + * + * @memberof OffsetGeometryInstanceAttribute.prototype + * + * @type {Boolean} + * @readonly + * + * @default false + */ + normalize : { + get : function() { + return false; + } + } + }); + + /** + * Creates a new {@link OffsetGeometryInstanceAttribute} instance given the provided an enabled flag and {@link DistanceDisplayCondition}. + * + * @param {Cartesian3} offset The cartesian offset + * @returns {OffsetGeometryInstanceAttribute} The new {@link OffsetGeometryInstanceAttribute} instance. + */ + OffsetGeometryInstanceAttribute.fromCartesian3 = function(offset) { + //>>includeStart('debug', pragmas.debug); + Check.defined('offset', offset); + //>>includeEnd('debug'); + + return new OffsetGeometryInstanceAttribute(offset.x, offset.y, offset.z); + }; + + /** + * Converts a distance display condition to a typed array that can be used to assign a distance display condition attribute. + * + * @param {Cartesian3} offset The cartesian offset + * @param {Float32Array} [result] The array to store the result in, if undefined a new instance will be created. + * @returns {Float32Array} The modified result parameter or a new instance if result was undefined. + * + * @example + * var attributes = primitive.getGeometryInstanceAttributes('an id'); + * attributes.modelMatrix = Cesium.OffsetGeometryInstanceAttribute.toValue(modelMatrix, attributes.modelMatrix); + */ + OffsetGeometryInstanceAttribute.toValue = function(offset, result) { + //>>includeStart('debug', pragmas.debug); + Check.defined('offset', offset); + //>>includeEnd('debug'); + + if (!defined(result)) { + result = new Float32Array([offset.x, offset.y, offset.z]); + } + + result[0] = offset.x; + result[1] = offset.y; + result[2] = offset.z; + return result; + }; + + return OffsetGeometryInstanceAttribute; +}); diff --git a/Source/Core/PolygonGeometry.js b/Source/Core/PolygonGeometry.js index ddd97f6c05a1..5c214d2573f5 100644 --- a/Source/Core/PolygonGeometry.js +++ b/Source/Core/PolygonGeometry.js @@ -1,4 +1,5 @@ define([ + './arrayFill', './BoundingRectangle', './BoundingSphere', './Cartesian2', @@ -15,6 +16,7 @@ define([ './Geometry', './GeometryAttribute', './GeometryInstance', + './GeometryOffsetAttribute', './GeometryPipeline', './IndexDatatype', './Math', @@ -27,6 +29,7 @@ define([ './VertexFormat', './WindingOrder' ], function( + arrayFill, BoundingRectangle, BoundingSphere, Cartesian2, @@ -43,6 +46,7 @@ define([ Geometry, GeometryAttribute, GeometryInstance, + GeometryOffsetAttribute, GeometryPipeline, IndexDatatype, CesiumMath, @@ -127,6 +131,11 @@ define([ var vertexFormat = options.vertexFormat; var geometry = options.geometry; var shadowVolume = options.shadowVolume; + var flatPositions = geometry.attributes.position.values; + var length = flatPositions.length; + var wall = options.wall; + var top = options.top || wall; + var bottom = options.bottom || wall; if (vertexFormat.st || vertexFormat.normal || vertexFormat.tangent || vertexFormat.bitangent || shadowVolume) { // PERFORMANCE_IDEA: Compute before subdivision, then just interpolate during subdivision. // PERFORMANCE_IDEA: Compute with createGeometryFromPositions() for fast path when there's no holes. @@ -134,18 +143,12 @@ define([ var tangentPlane = options.tangentPlane; var ellipsoid = options.ellipsoid; var stRotation = options.stRotation; - var wall = options.wall; - var top = options.top || wall; - var bottom = options.bottom || wall; var perPositionHeight = options.perPositionHeight; var origin = appendTextureCoordinatesOrigin; origin.x = boundingRectangle.x; origin.y = boundingRectangle.y; - var flatPositions = geometry.attributes.position.values; - var length = flatPositions.length; - var textureCoordinates = vertexFormat.st ? new Float32Array(2 * (length / 3)) : undefined; var normals; if (vertexFormat.normal) { @@ -366,6 +369,29 @@ define([ }); } } + + if (options.extrude && defined(options.offsetAttribute)) { + var size = flatPositions.length / 3; + var offsetAttribute = new Uint8Array(size); + + if (options.offsetAttribute === GeometryOffsetAttribute.TOP) { + if ((top && bottom) || wall) { + offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); + } else if (top) { + offsetAttribute = arrayFill(offsetAttribute, 1); + } + } else { + var offsetValue = options.offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + offsetAttribute = arrayFill(offsetAttribute, offsetValue); + } + + geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values : offsetAttribute + }); + } + return geometry; } @@ -429,7 +455,6 @@ define([ geos.topAndBottom = new GeometryInstance({ geometry : topGeo }); - } var outerRing = hierarchy.outerRing; @@ -595,6 +620,7 @@ define([ this._perPositionHeightExtrude = perPositionHeightExtrude; this._shadowVolume = defaultValue(options.shadowVolume, false); this._workerName = 'createPolygonGeometry'; + this._offsetAttribute = options.offsetAttribute; this._rectangle = undefined; this._textureCoordinateRotationPoints = undefined; @@ -603,7 +629,7 @@ define([ * The number of elements used to pack the object into an array. * @type {Number} */ - this.packedLength = PolygonGeometryLibrary.computeHierarchyPackedLength(polygonHierarchy) + Ellipsoid.packedLength + VertexFormat.packedLength + 10; + this.packedLength = PolygonGeometryLibrary.computeHierarchyPackedLength(polygonHierarchy) + Ellipsoid.packedLength + VertexFormat.packedLength + 11; } /** @@ -657,7 +683,8 @@ define([ granularity : options.granularity, perPositionHeight : options.perPositionHeight, closeTop : options.closeTop, - closeBottom: options.closeBottom + closeBottom : options.closeBottom, + offsetAttribute : options.offsetAttribute }; return new PolygonGeometry(newOptions); }; @@ -696,6 +723,7 @@ define([ array[startingIndex++] = value._closeTop ? 1.0 : 0.0; array[startingIndex++] = value._closeBottom ? 1.0 : 0.0; array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0; + array[startingIndex++] = defaultValue(value._offsetAttribute, -1); array[startingIndex] = value.packedLength; return array; @@ -704,7 +732,7 @@ define([ var scratchEllipsoid = Ellipsoid.clone(Ellipsoid.UNIT_SPHERE); var scratchVertexFormat = new VertexFormat(); - //Only used to avoid inaability to default construct. + //Only used to avoid inability to default construct. var dummyOptions = { polygonHierarchy : {} }; @@ -742,6 +770,7 @@ define([ var closeTop = array[startingIndex++] === 1.0; var closeBottom = array[startingIndex++] === 1.0; var shadowVolume = array[startingIndex++] === 1.0; + var offsetAttribute = array[startingIndex++]; var packedLength = array[startingIndex]; if (!defined(result)) { @@ -760,6 +789,7 @@ define([ result._closeTop = closeTop; result._closeBottom = closeBottom; result._shadowVolume = shadowVolume; + result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; result.packedLength = packedLength; return result; }; @@ -798,7 +828,6 @@ define([ outerPositions = hierarchy[0].outerRing; var boundingRectangle = computeBoundingRectangle(tangentPlane, outerPositions, stRotation, scratchBoundingRectangle); - var geometry; var geometries = []; var height = polygonGeometry._height; @@ -815,29 +844,31 @@ define([ stRotation: stRotation, bottom: false, top: true, - wall: false + wall: false, + extrude: false }; var i; if (extrude) { + options.extrude = true; options.top = closeTop; options.bottom = closeBottom; options.shadowVolume = polygonGeometry._shadowVolume; - + options.offsetAttribute = polygonGeometry._offsetAttribute; for (i = 0; i < polygons.length; i++) { - geometry = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], granularity, hierarchy[i], perPositionHeight, closeTop, closeBottom, vertexFormat); + var splitGeometry = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], granularity, hierarchy[i], perPositionHeight, closeTop, closeBottom, vertexFormat); var topAndBottom; if (closeTop && closeBottom) { - topAndBottom = geometry.topAndBottom; + topAndBottom = splitGeometry.topAndBottom; options.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(topAndBottom.geometry, height, extrudedHeight, ellipsoid, perPositionHeight); } else if (closeTop) { - topAndBottom = geometry.topAndBottom; + topAndBottom = splitGeometry.topAndBottom; topAndBottom.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(topAndBottom.geometry.attributes.position.values, height, ellipsoid, !perPositionHeight); options.geometry = topAndBottom.geometry; } else if (closeBottom) { - topAndBottom = geometry.topAndBottom; + topAndBottom = splitGeometry.topAndBottom; topAndBottom.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(topAndBottom.geometry.attributes.position.values, extrudedHeight, ellipsoid, true); options.geometry = topAndBottom.geometry; } @@ -847,7 +878,7 @@ define([ geometries.push(topAndBottom); } - var walls = geometry.walls; + var walls = splitGeometry.walls; options.wall = true; for ( var k = 0; k < walls.length; k++) { var wall = walls[k]; @@ -858,17 +889,30 @@ define([ } } else { for (i = 0; i < polygons.length; i++) { - geometry = new GeometryInstance({ + var geometryInstance = new GeometryInstance({ geometry : PolygonGeometryLibrary.createGeometryFromPositions(ellipsoid, polygons[i], granularity, perPositionHeight, vertexFormat) }); - geometry.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(geometry.geometry.attributes.position.values, height, ellipsoid, !perPositionHeight); - options.geometry = geometry.geometry; - geometry.geometry = computeAttributes(options); - geometries.push(geometry); + geometryInstance.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(geometryInstance.geometry.attributes.position.values, height, ellipsoid, !perPositionHeight); + options.geometry = geometryInstance.geometry; + geometryInstance.geometry = computeAttributes(options); + + if (defined(polygonGeometry._offsetAttribute)) { + var length = geometryInstance.geometry.attributes.position.values.length; + var applyOffset = new Uint8Array(length / 3); + var offsetValue = polygonGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + arrayFill(applyOffset, offsetValue); + geometryInstance.geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } + + geometries.push(geometryInstance); } } - geometry = GeometryPipeline.combineInstances(geometries)[0]; + var geometry = GeometryPipeline.combineInstances(geometries)[0]; geometry.attributes.position.values = new Float64Array(geometry.attributes.position.values); geometry.indices = IndexDatatype.createTypedArray(geometry.attributes.position.values.length / 3, geometry.indices); diff --git a/Source/Core/PolygonOutlineGeometry.js b/Source/Core/PolygonOutlineGeometry.js index be5fe8c82881..ed02b8938d89 100644 --- a/Source/Core/PolygonOutlineGeometry.js +++ b/Source/Core/PolygonOutlineGeometry.js @@ -1,4 +1,5 @@ define([ + './arrayFill', './arrayRemoveDuplicates', './BoundingSphere', './Cartesian3', @@ -13,6 +14,7 @@ define([ './GeometryAttribute', './GeometryAttributes', './GeometryInstance', + './GeometryOffsetAttribute', './GeometryPipeline', './IndexDatatype', './Math', @@ -22,6 +24,7 @@ define([ './Queue', './WindingOrder' ], function( + arrayFill, arrayRemoveDuplicates, BoundingSphere, Cartesian3, @@ -36,6 +39,7 @@ define([ GeometryAttribute, GeometryAttributes, GeometryInstance, + GeometryOffsetAttribute, GeometryPipeline, IndexDatatype, CesiumMath, @@ -317,13 +321,14 @@ define([ this._polygonHierarchy = polygonHierarchy; this._perPositionHeight = perPositionHeight; this._perPositionHeightExtrude = perPositionHeightExtrude; + this._offsetAttribute = options.offsetAttribute; this._workerName = 'createPolygonOutlineGeometry'; /** * The number of elements used to pack the object into an array. * @type {Number} */ - this.packedLength = PolygonGeometryLibrary.computeHierarchyPackedLength(polygonHierarchy) + Ellipsoid.packedLength + 6; + this.packedLength = PolygonGeometryLibrary.computeHierarchyPackedLength(polygonHierarchy) + Ellipsoid.packedLength + 7; } /** @@ -353,6 +358,7 @@ define([ array[startingIndex++] = value._granularity; array[startingIndex++] = value._perPositionHeightExtrude ? 1.0 : 0.0; array[startingIndex++] = value._perPositionHeight ? 1.0 : 0.0; + array[startingIndex++] = defaultValue(value._offsetAttribute, -1); array[startingIndex] = value.packedLength; return array; @@ -390,6 +396,7 @@ define([ var granularity = array[startingIndex++]; var perPositionHeightExtrude = array[startingIndex++] === 1.0; var perPositionHeight = array[startingIndex++] === 1.0; + var offsetAttribute = array[startingIndex++]; var packedLength = array[startingIndex]; if (!defined(result)) { @@ -403,6 +410,7 @@ define([ result._granularity = granularity; result._perPositionHeight = perPositionHeight; result._perPositionHeightExtrude = perPositionHeightExtrude; + result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; result.packedLength = packedLength; return result; @@ -451,7 +459,8 @@ define([ extrudedHeight : options.extrudedHeight, ellipsoid : options.ellipsoid, granularity : options.granularity, - perPositionHeight : options.perPositionHeight + perPositionHeight : options.perPositionHeight, + offsetAttribute : options.offsetAttribute }; return new PolygonOutlineGeometry(newOptions); }; @@ -509,29 +518,58 @@ define([ return undefined; } - var geometry; + var geometryInstance; var geometries = []; var minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius); var height = polygonGeometry._height; var extrudedHeight = polygonGeometry._extrudedHeight; var extrude = polygonGeometry._perPositionHeightExtrude || !CesiumMath.equalsEpsilon(height, extrudedHeight, 0, CesiumMath.EPSILON2); - + var offsetValue; if (extrude) { for (i = 0; i < polygons.length; i++) { - geometry = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], minDistance, perPositionHeight); - geometry.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(geometry.geometry, height, extrudedHeight, ellipsoid, perPositionHeight); - geometries.push(geometry); + geometryInstance = createGeometryFromPositionsExtruded(ellipsoid, polygons[i], minDistance, perPositionHeight); + geometryInstance.geometry = PolygonGeometryLibrary.scaleToGeodeticHeightExtruded(geometryInstance.geometry, height, extrudedHeight, ellipsoid, perPositionHeight); + if (defined(polygonGeometry._offsetAttribute)) { + var size = geometryInstance.geometry.attributes.position.values.length / 3; + var offsetAttribute = new Uint8Array(size); + if (polygonGeometry._offsetAttribute === GeometryOffsetAttribute.TOP) { + offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); + } else { + offsetValue = polygonGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + offsetAttribute = arrayFill(offsetAttribute, offsetValue); + } + + geometryInstance.geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values : offsetAttribute + }); + } + geometries.push(geometryInstance); } } else { for (i = 0; i < polygons.length; i++) { - geometry = createGeometryFromPositions(ellipsoid, polygons[i], minDistance, perPositionHeight); - geometry.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(geometry.geometry.attributes.position.values, height, ellipsoid, !perPositionHeight); - geometries.push(geometry); + geometryInstance = createGeometryFromPositions(ellipsoid, polygons[i], minDistance, perPositionHeight); + geometryInstance.geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(geometryInstance.geometry.attributes.position.values, height, ellipsoid, !perPositionHeight); + + if (defined(polygonGeometry._offsetAttribute)) { + var length = geometryInstance.geometry.attributes.position.values.length; + var applyOffset = new Uint8Array(length / 3); + offsetValue = polygonGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + arrayFill(applyOffset, offsetValue); + geometryInstance.geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } + + geometries.push(geometryInstance); } } - geometry = GeometryPipeline.combineInstances(geometries)[0]; + var geometry = GeometryPipeline.combineInstances(geometries)[0]; var boundingSphere = BoundingSphere.fromVertices(geometry.attributes.position.values); return new Geometry({ diff --git a/Source/Core/RectangleGeometry.js b/Source/Core/RectangleGeometry.js index 0f5016d3a0c6..4dfec55be646 100644 --- a/Source/Core/RectangleGeometry.js +++ b/Source/Core/RectangleGeometry.js @@ -1,4 +1,5 @@ define([ + './arrayFill', './BoundingSphere', './Cartesian2', './Cartesian3', @@ -14,6 +15,7 @@ define([ './GeometryAttribute', './GeometryAttributes', './GeometryInstance', + './GeometryOffsetAttribute', './GeometryPipeline', './IndexDatatype', './Math', @@ -26,6 +28,7 @@ define([ './RectangleGeometryLibrary', './VertexFormat' ], function( + arrayFill, BoundingSphere, Cartesian2, Cartesian3, @@ -41,6 +44,7 @@ define([ GeometryAttribute, GeometryAttributes, GeometryInstance, + GeometryOffsetAttribute, GeometryPipeline, IndexDatatype, CesiumMath, @@ -335,6 +339,7 @@ define([ function constructExtrudedRectangle(options) { var shadowVolume = options.shadowVolume; + var offsetAttributeValue = options.offsetAttribute; var vertexFormat = options.vertexFormat; var minHeight = options.extrudedHeight; var maxHeight = options.surfaceHeight; @@ -392,6 +397,25 @@ define([ }); } + var offsetValue; + var hasOffsets = defined(offsetAttributeValue); + if (hasOffsets) { + var size = length / 3 * 2; + var offsetAttribute = new Uint8Array(size); + if (offsetAttributeValue === GeometryOffsetAttribute.TOP) { + offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); + } else { + offsetValue = offsetAttributeValue === GeometryOffsetAttribute.NONE ? 0 : 1; + offsetAttribute = arrayFill(offsetAttribute, offsetValue); + } + + topBottomGeo.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values : offsetAttribute + }); + } + if (vertexFormat.tangent) { var topTangents = topBottomGeo.attributes.tangent.values; tangents.set(topTangents); @@ -431,11 +455,19 @@ define([ var wallPositions = new Float64Array(wallCount * 3); var wallExtrudeNormals = shadowVolume ? new Float32Array(wallCount * 3) : undefined; + var wallOffsetAttribute = hasOffsets ? new Uint8Array(wallCount) : undefined; var wallTextures = (vertexFormat.st) ? new Float32Array(wallCount * 2) : undefined; + var computeTopOffsets = offsetAttributeValue === GeometryOffsetAttribute.TOP; + if (hasOffsets && !computeTopOffsets) { + offsetValue = offsetAttributeValue === GeometryOffsetAttribute.ALL ? 1 : 0; + wallOffsetAttribute = arrayFill(wallOffsetAttribute, offsetValue); + } + var posIndex = 0; var stIndex = 0; var extrudeNormalIndex = 0; + var wallOffsetIndex = 0; var area = width * height; var threeI; for (i = 0; i < area; i += width) { @@ -452,6 +484,10 @@ define([ wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } + if (computeTopOffsets) { + wallOffsetAttribute[wallOffsetIndex++] = 1; + wallOffsetIndex += 1; + } } for (i = area - width; i < area; i++) { @@ -468,6 +504,11 @@ define([ wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } + if (computeTopOffsets) { + wallOffsetAttribute[wallOffsetIndex++] = 1; + wallOffsetIndex += 1; + } + } for (i = area - 1; i > 0; i -= width) { @@ -484,6 +525,11 @@ define([ wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } + if (computeTopOffsets) { + wallOffsetAttribute[wallOffsetIndex++] = 1; + wallOffsetIndex += 1; + } + } for (i = width - 1; i >= 0; i--) { @@ -500,6 +546,10 @@ define([ wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 1]; wallExtrudeNormals[extrudeNormalIndex++] = topNormals[threeI + 2]; } + if (computeTopOffsets) { + wallOffsetAttribute[wallOffsetIndex++] = 1; + wallOffsetIndex += 1; + } } var geo = calculateAttributesWall(wallPositions, vertexFormat, ellipsoid); @@ -518,6 +568,13 @@ define([ values : wallExtrudeNormals }); } + if (hasOffsets) { + geo.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values : wallOffsetAttribute + }); + } var wallIndices = IndexDatatype.createTypedArray(wallCount, perimeterPositions * 6); @@ -652,6 +709,7 @@ define([ this._extrudedHeight = Math.min(height, extrudedHeight); this._shadowVolume = defaultValue(options.shadowVolume, false); this._workerName = 'createRectangleGeometry'; + this._offsetAttribute = options.offsetAttribute; this._rotatedRectangle = undefined; this._textureCoordinateRotationPoints = undefined; @@ -661,7 +719,7 @@ define([ * The number of elements used to pack the object into an array. * @type {Number} */ - RectangleGeometry.packedLength = Rectangle.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 6; + RectangleGeometry.packedLength = Rectangle.packedLength + Ellipsoid.packedLength + VertexFormat.packedLength + 7; /** * Stores the provided instance into the provided array. @@ -694,7 +752,8 @@ define([ array[startingIndex++] = value._rotation; array[startingIndex++] = value._stRotation; array[startingIndex++] = value._extrudedHeight; - array[startingIndex] = value._shadowVolume ? 1.0 : 0.0; + array[startingIndex++] = value._shadowVolume ? 1.0 : 0.0; + array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; @@ -710,7 +769,8 @@ define([ rotation : undefined, stRotation : undefined, extrudedHeight : undefined, - shadowVolume : undefined + shadowVolume : undefined, + offsetAttribute: undefined }; /** @@ -742,7 +802,8 @@ define([ var rotation = array[startingIndex++]; var stRotation = array[startingIndex++]; var extrudedHeight = array[startingIndex++]; - var shadowVolume = array[startingIndex] === 1.0; + var shadowVolume = array[startingIndex++] === 1.0; + var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions.granularity = granularity; @@ -751,6 +812,8 @@ define([ scratchOptions.stRotation = stRotation; scratchOptions.extrudedHeight = extrudedHeight; scratchOptions.shadowVolume = shadowVolume; + scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; + return new RectangleGeometry(scratchOptions); } @@ -763,6 +826,7 @@ define([ result._stRotation = stRotation; result._extrudedHeight = extrudedHeight; result._shadowVolume = shadowVolume; + result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; @@ -819,6 +883,7 @@ define([ rectangle = rectangleGeometry._rectangle; if (extrude) { options.shadowVolume = rectangleGeometry._shadowVolume; + options.offsetAttribute = rectangleGeometry._offsetAttribute; geometry = constructExtrudedRectangle(options); var topBS = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, surfaceHeight, topBoundingSphere); var bottomBS = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, extrudedHeight, bottomBoundingSphere); @@ -826,6 +891,19 @@ define([ } else { geometry = constructRectangle(options); geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(geometry.attributes.position.values, surfaceHeight, ellipsoid, false); + + if (defined(rectangleGeometry._offsetAttribute)) { + var length = geometry.attributes.position.values.length; + var applyOffset = new Uint8Array(length / 3); + var offsetValue = rectangleGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + arrayFill(applyOffset, offsetValue); + geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } + boundingSphere = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, surfaceHeight); } diff --git a/Source/Core/RectangleOutlineGeometry.js b/Source/Core/RectangleOutlineGeometry.js index 9defa9b21155..7666b3109a6f 100644 --- a/Source/Core/RectangleOutlineGeometry.js +++ b/Source/Core/RectangleOutlineGeometry.js @@ -1,4 +1,5 @@ define([ + './arrayFill', './BoundingSphere', './Cartesian3', './Cartographic', @@ -10,6 +11,7 @@ define([ './Geometry', './GeometryAttribute', './GeometryAttributes', + './GeometryOffsetAttribute', './IndexDatatype', './Math', './PolygonPipeline', @@ -17,6 +19,7 @@ define([ './Rectangle', './RectangleGeometryLibrary' ], function( + arrayFill, BoundingSphere, Cartesian3, Cartographic, @@ -28,6 +31,7 @@ define([ Geometry, GeometryAttribute, GeometryAttributes, + GeometryOffsetAttribute, IndexDatatype, CesiumMath, PolygonPipeline, @@ -213,6 +217,7 @@ define([ this._surfaceHeight = Math.max(height, extrudedHeight); this._rotation = rotation; this._extrudedHeight = Math.min(height, extrudedHeight); + this._offsetAttribute = options.offsetAttribute; this._workerName = 'createRectangleOutlineGeometry'; } @@ -220,7 +225,7 @@ define([ * The number of elements used to pack the object into an array. * @type {Number} */ - RectangleOutlineGeometry.packedLength = Rectangle.packedLength + Ellipsoid.packedLength + 4; + RectangleOutlineGeometry.packedLength = Rectangle.packedLength + Ellipsoid.packedLength + 5; /** * Stores the provided instance into the provided array. @@ -253,7 +258,8 @@ define([ array[startingIndex++] = value._granularity; array[startingIndex++] = value._surfaceHeight; array[startingIndex++] = value._rotation; - array[startingIndex] = value._extrudedHeight; + array[startingIndex++] = value._extrudedHeight; + array[startingIndex] = defaultValue(value._offsetAttribute, -1); return array; }; @@ -266,7 +272,8 @@ define([ granularity : undefined, height : undefined, rotation : undefined, - extrudedHeight : undefined + extrudedHeight : undefined, + offsetAttribute: undefined }; /** @@ -295,13 +302,16 @@ define([ var granularity = array[startingIndex++]; var height = array[startingIndex++]; var rotation = array[startingIndex++]; - var extrudedHeight = array[startingIndex]; + var extrudedHeight = array[startingIndex++]; + var offsetAttribute = array[startingIndex]; if (!defined(result)) { scratchOptions.granularity = granularity; scratchOptions.height = height; scratchOptions.rotation = rotation; scratchOptions.extrudedHeight = extrudedHeight; + scratchOptions.offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; + return new RectangleOutlineGeometry(scratchOptions); } @@ -310,6 +320,7 @@ define([ result._surfaceHeight = height; result._rotation = rotation; result._extrudedHeight = extrudedHeight; + result._offsetAttribute = offsetAttribute === -1 ? undefined : offsetAttribute; return result; }; @@ -341,15 +352,44 @@ define([ var surfaceHeight = rectangleGeometry._surfaceHeight; var extrudedHeight = rectangleGeometry._extrudedHeight; var extrude = !CesiumMath.equalsEpsilon(surfaceHeight, extrudedHeight, 0, CesiumMath.EPSILON2); - + var offsetValue; if (extrude) { geometry = constructExtrudedRectangle(options); + if (defined(rectangleGeometry._offsetAttribute)) { + var size = geometry.attributes.position.values.length / 3; + var offsetAttribute = new Uint8Array(size); + if (rectangleGeometry._offsetAttribute === GeometryOffsetAttribute.TOP) { + offsetAttribute = arrayFill(offsetAttribute, 1, 0, size / 2); + } else { + offsetValue = rectangleGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + offsetAttribute = arrayFill(offsetAttribute, offsetValue); + } + + geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values : offsetAttribute + }); + } var topBS = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, surfaceHeight, topBoundingSphere); var bottomBS = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, extrudedHeight, bottomBoundingSphere); boundingSphere = BoundingSphere.union(topBS, bottomBS); } else { geometry = constructRectangle(options); geometry.attributes.position.values = PolygonPipeline.scaleToGeodeticHeight(geometry.attributes.position.values, surfaceHeight, ellipsoid, false); + + if (defined(rectangleGeometry._offsetAttribute)) { + var length = geometry.attributes.position.values.length; + var applyOffset = new Uint8Array(length / 3); + offsetValue = rectangleGeometry._offsetAttribute === GeometryOffsetAttribute.NONE ? 0 : 1; + arrayFill(applyOffset, offsetValue); + geometry.attributes.applyOffset = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 1, + values: applyOffset + }); + } + boundingSphere = BoundingSphere.fromRectangle3D(rectangle, ellipsoid, surfaceHeight); } diff --git a/Source/Scene/Primitive.js b/Source/Scene/Primitive.js index aa026437b501..dc581b4c4904 100644 --- a/Source/Scene/Primitive.js +++ b/Source/Scene/Primitive.js @@ -18,8 +18,10 @@ define([ '../Core/Geometry', '../Core/GeometryAttribute', '../Core/GeometryAttributes', + '../Core/Intersect', '../Core/isArray', '../Core/Matrix4', + '../Core/Plane', '../Core/RuntimeError', '../Core/subdivideArray', '../Core/TaskProcessor', @@ -59,8 +61,10 @@ define([ Geometry, GeometryAttribute, GeometryAttributes, + Intersect, isArray, Matrix4, + Plane, RuntimeError, subdivideArray, TaskProcessor, @@ -379,8 +383,11 @@ define([ this._batchTable = undefined; this._batchTableAttributeIndices = undefined; + this._offsetInstanceExtend = undefined; this._instanceBoundingSpheres = undefined; this._instanceBoundingSpheresCV = undefined; + this._tempBoundingSpheres = undefined; + this._recomputeBoundingSpheres = false; this._batchTableBoundingSpheresUpdated = false; this._batchTableBoundingSphereAttributeIndices = undefined; } @@ -603,19 +610,19 @@ define([ functionName : 'czm_batchTable_boundingSphereCenter3DHigh', componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 3 - },{ + }, { functionName : 'czm_batchTable_boundingSphereCenter3DLow', componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 3 - },{ + }, { functionName : 'czm_batchTable_boundingSphereCenter2DHigh', componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 3 - },{ + }, { functionName : 'czm_batchTable_boundingSphereCenter2DLow', componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 3 - },{ + }, { functionName : 'czm_batchTable_boundingSphereRadius', componentDatatype : ComponentDatatype.FLOAT, componentsPerAttribute : 1 @@ -720,6 +727,7 @@ define([ function cloneInstance(instance, geometry) { return { geometry : geometry, + attributes: instance.attributes, modelMatrix : Matrix4.clone(instance.modelMatrix), pickPrimitive : instance.pickPrimitive, id : instance.id @@ -867,6 +875,21 @@ define([ return vsPick; }; + Primitive._appendOffsetToShader = function(primitive, vertexShaderSource) { + if (!defined(primitive._batchTableAttributeIndices.offset)) { + return vertexShaderSource; + } + + var attr = 'attribute float batchId;\n'; + attr += 'attribute float applyOffset;'; + var modifiedShader = vertexShaderSource.replace(/attribute\s+float\s+batchId;/g, attr); + + var str = 'vec4 $1 = czm_computePosition();\n'; + str += ' $1 = $1 + vec4(czm_batchTable_offset(batchId) * applyOffset, 0.0);'; + modifiedShader = modifiedShader.replace(/vec4\s+([A-Za-z0-9_]+)\s+=\s+czm_computePosition\(\);/g, str); + return modifiedShader; + }; + Primitive._appendDistanceDisplayConditionToShader = function(primitive, vertexShaderSource, scene3DOnly) { if (!defined(primitive._batchTableAttributeIndices.distanceDisplayCondition)) { return vertexShaderSource; @@ -1049,7 +1072,7 @@ define([ if (shaderAttributes.hasOwnProperty(name)) { if (!defined(attributeLocations[name])) { throw new DeveloperError('Appearance/Geometry mismatch. The appearance requires vertex shader attribute input \'' + name + - '\', which was not computed as part of the Geometry. Use the appearance\'s vertexFormat property when constructing the geometry.'); + '\', which was not computed as part of the Geometry. Use the appearance\'s vertexFormat property when constructing the geometry.'); } } } @@ -1177,10 +1200,12 @@ define([ primitive._attributeLocations = result.attributeLocations; primitive.modelMatrix = Matrix4.clone(result.modelMatrix, primitive.modelMatrix); primitive._pickOffsets = result.pickOffsets; + primitive._offsetInstanceExtend = result.offsetInstanceExtend; primitive._instanceBoundingSpheres = result.boundingSpheres; primitive._instanceBoundingSpheresCV = result.boundingSpheresCV; if (defined(primitive._geometries) && primitive._geometries.length > 0) { + primitive._recomputeBoundingSpheres = true; primitive._state = PrimitiveState.COMBINED; } else { setReady(primitive, frameState, PrimitiveState.FAILED, undefined); @@ -1237,16 +1262,93 @@ define([ primitive._attributeLocations = result.attributeLocations; primitive.modelMatrix = Matrix4.clone(result.modelMatrix, primitive.modelMatrix); primitive._pickOffsets = result.pickOffsets; + primitive._offsetInstanceExtend = result.offsetInstanceExtend; primitive._instanceBoundingSpheres = result.boundingSpheres; primitive._instanceBoundingSpheresCV = result.boundingSpheresCV; if (defined(primitive._geometries) && primitive._geometries.length > 0) { + primitive._recomputeBoundingSpheres = true; primitive._state = PrimitiveState.COMBINED; } else { setReady(primitive, frameState, PrimitiveState.FAILED, undefined); } } + function recomputeBoundingSpheres(primitive, frameState) { + var offsetIndex = primitive._batchTableAttributeIndices.offset; + if (!primitive._recomputeBoundingSpheres || !defined(offsetIndex)) { + primitive._recomputeBoundingSpheres = false; + return; + } + + var i; + var offsetInstanceExtend = primitive._offsetInstanceExtend; + var boundingSpheres = primitive._instanceBoundingSpheres; + var length = boundingSpheres.length; + var newBoundingSpheres = primitive._tempBoundingSpheres; + if (!defined(newBoundingSpheres)) { + newBoundingSpheres = new Array(length); + for (i = 0; i < length; i++) { + newBoundingSpheres[i] = new BoundingSphere(); + } + primitive._tempBoundingSpheres = newBoundingSpheres; + } + for (i = 0; i < length; ++i) { + var newBS = newBoundingSpheres[i]; + var offset = primitive._batchTable.getBatchedAttribute(i, offsetIndex, new Cartesian3()); + newBS = boundingSpheres[i].clone(newBS); + transformBoundingSphere(newBS, offset, offsetInstanceExtend[i]); + } + var combinedBS = []; + var combinedWestBS = []; + var combinedEastBS = []; + + for (i = 0; i < length; ++i) { + var bs = newBoundingSpheres[i]; + + var minX = bs.center.x - bs.radius; + if (minX > 0 || BoundingSphere.intersectPlane(bs, Plane.ORIGIN_ZX_PLANE) !== Intersect.INTERSECTING) { + combinedBS.push(bs); + } else { + combinedWestBS.push(bs); + combinedEastBS.push(bs); + } + } + + var resultBS1 = combinedBS[0]; + var resultBS2 = combinedEastBS[0]; + var resultBS3 = combinedWestBS[0]; + + for (i = 1; i < combinedBS.length; i++) { + resultBS1 = BoundingSphere.union(resultBS1, combinedBS[i]); + } + for (i = 1; i < combinedEastBS.length; i++) { + resultBS2 = BoundingSphere.union(resultBS2, combinedEastBS[i]); + } + for (i = 1; i < combinedWestBS.length; i++) { + resultBS3 = BoundingSphere.union(resultBS3, combinedWestBS[i]); + } + var result = []; + if (defined(resultBS1)) { + result.push(resultBS1); + } + if (defined(resultBS2)) { + result.push(resultBS2); + } + if (defined(resultBS3)) { + result.push(resultBS3); + } + + for (i = 0; i < result.length; i++) { + var boundingSphere = result[i].clone(primitive._boundingSpheres[i]); + primitive._boundingSpheres[i] = boundingSphere; + primitive._boundingSphereCV[i] = BoundingSphere.projectTo2D(boundingSphere, frameState.mapProjection, primitive._boundingSphereCV[i]); + } + + Primitive._updateBoundingVolumes(primitive, frameState, primitive.modelMatrix, true); + primitive._recomputeBoundingSpheres = false; + } + var scratchBoundingSphereCenterEncoded = new EncodedCartesian3(); var scratchBoundingSphereCartographic = new Cartographic(); var scratchBoundingSphereCenter2D = new Cartesian3(); @@ -1403,6 +1505,7 @@ define([ var attributeLocations = primitive._attributeLocations; var vs = primitive._batchTable.getVertexShaderCallback()(appearance.vertexShaderSource); + vs = Primitive._appendOffsetToShader(primitive, vs); vs = Primitive._appendShowToShader(primitive, vs); vs = Primitive._appendDistanceDisplayConditionToShader(primitive, vs, frameState.scene3DOnly); vs = appendPickToVertexShader(vs); @@ -1572,26 +1675,12 @@ define([ } } - Primitive._updateBoundingVolumes = function(primitive, frameState, modelMatrix) { + Primitive._updateBoundingVolumes = function(primitive, frameState, modelMatrix, forceUpdate) { var i; var length; var boundingSphere; - // Update bounding volumes for primitives that are sized in pixels. - // The pixel size in meters varies based on the distance from the camera. - var pixelSize = primitive.appearance.pixelSize; - if (defined(pixelSize)) { - length = primitive._boundingSpheres.length; - for (i = 0; i < length; ++i) { - boundingSphere = primitive._boundingSpheres[i]; - var boundingSphereWC = primitive._boundingSphereWC[i]; - var pixelSizeInMeters = frameState.camera.getPixelSize(boundingSphere, frameState.context.drawingBufferWidth, frameState.context.drawingBufferHeight); - var sizeInMeters = pixelSizeInMeters * pixelSize; - boundingSphereWC.radius = boundingSphere.radius + sizeInMeters; - } - } - - if (!Matrix4.equals(modelMatrix, primitive._modelMatrix)) { + if (forceUpdate || !Matrix4.equals(modelMatrix, primitive._modelMatrix)) { Matrix4.clone(modelMatrix, primitive._modelMatrix); length = primitive._boundingSpheres.length; for (i = 0; i < length; ++i) { @@ -1606,6 +1695,20 @@ define([ } } } + + // Update bounding volumes for primitives that are sized in pixels. + // The pixel size in meters varies based on the distance from the camera. + var pixelSize = primitive.appearance.pixelSize; + if (defined(pixelSize)) { + length = primitive._boundingSpheres.length; + for (i = 0; i < length; ++i) { + boundingSphere = primitive._boundingSpheres[i]; + var boundingSphereWC = primitive._boundingSphereWC[i]; + var pixelSizeInMeters = frameState.camera.getPixelSize(boundingSphere, frameState.context.drawingBufferWidth, frameState.context.drawingBufferHeight); + var sizeInMeters = pixelSizeInMeters * pixelSize; + boundingSphereWC.radius = boundingSphere.radius + sizeInMeters; + } + } }; function updateAndQueueCommands(primitive, frameState, colorCommands, pickCommands, modelMatrix, cull, debugShowBoundingVolume, twoPasses) { @@ -1724,6 +1827,10 @@ define([ return; } + if (this._recomputeBoundingSpheres) { + recomputeBoundingSpheres(this, frameState); + } + // Create or recreate render state and shader program if appearance/material changed var appearance = this.appearance; var material = appearance.material; @@ -1735,7 +1842,7 @@ define([ this._material = material; createRS = true; createSP = true; - } else if (this._material !== material ) { + } else if (this._material !== material) { this._material = material; createSP = true; } @@ -1784,6 +1891,21 @@ define([ updateAndQueueCommandsFunc(this, frameState, this._colorCommands, this._pickCommands, this.modelMatrix, this.cull, this.debugShowBoundingVolume, twoPasses); }; + var offsetBoundingSphereScratch1 = new BoundingSphere(); + var offsetBoundingSphereScratch2 = new BoundingSphere(); + function transformBoundingSphere(boundingSphere, offset, extend) { + if (extend) { + var origBS = BoundingSphere.clone(boundingSphere, offsetBoundingSphereScratch1); + var offsetBS = BoundingSphere.clone(boundingSphere, offsetBoundingSphereScratch2); + offsetBS.center = Cartesian3.add(offsetBS.center, offset, offsetBS.center); + boundingSphere = BoundingSphere.union(origBS, offsetBS, boundingSphere); + } else { + boundingSphere.center = Cartesian3.add(boundingSphere.center, offset, boundingSphere.center); + } + + return boundingSphere; + } + function createGetFunction(batchTable, instanceIndex, attributeIndex) { return function() { var attributeValue = batchTable.getBatchedAttribute(instanceIndex, attributeIndex); @@ -1799,7 +1921,7 @@ define([ }; } - function createSetFunction(batchTable, instanceIndex, attributeIndex) { + function createSetFunction(batchTable, instanceIndex, attributeIndex, primitive, name) { return function(value) { //>>includeStart('debug', pragmas.debug); if (!defined(value) || !defined(value.length) || value.length < 1 || value.length > 4) { @@ -1808,17 +1930,29 @@ define([ //>>includeEnd('debug'); var attributeValue = getAttributeValue(value); batchTable.setBatchedAttribute(instanceIndex, attributeIndex, attributeValue); + if (name === 'offset') { + primitive._recomputeBoundingSpheres = true; + } }; } + var offsetScratch = new Cartesian3(); + function createBoundingSphereProperties(primitive, properties, index) { properties.boundingSphere = { get : function() { var boundingSphere = primitive._instanceBoundingSpheres[index]; - var modelMatrix = primitive.modelMatrix; - if (defined(modelMatrix) && defined(boundingSphere)) { - boundingSphere = BoundingSphere.transform(boundingSphere, modelMatrix); + if (defined(boundingSphere)) { + var modelMatrix = primitive.modelMatrix; + var offset = properties.offset; + if (defined(offset)) { + transformBoundingSphere(boundingSphere.center, Cartesian3.fromArray(offset, 0, offsetScratch), primitive._offsetInstanceExtend[index]); + } + if (defined(modelMatrix)) { + boundingSphere = BoundingSphere.transform(boundingSphere, modelMatrix); + } } + return boundingSphere; } }; @@ -1850,6 +1984,7 @@ define([ * attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.AQUA); * attributes.show = Cesium.ShowGeometryInstanceAttribute.toValue(true); * attributes.distanceDisplayCondition = Cesium.DistanceDisplayConditionGeometryInstanceAttribute.toValue(100.0, 10000.0); + * attributes.offset = Cesium.OffsetGeometryInstanceAttribute.toValue(Cartesian3.IDENTITY); */ Primitive.prototype.getGeometryInstanceAttributes = function(id) { //>>includeStart('debug', pragmas.debug); @@ -1907,7 +2042,7 @@ define([ } if (createSetter) { - properties[name].set = createSetFunction(batchTable, index, attributeIndex); + properties[name].set = createSetFunction(batchTable, index, attributeIndex, this, name); } } } diff --git a/Source/Scene/PrimitivePipeline.js b/Source/Scene/PrimitivePipeline.js index 353a6a2e072a..b4549b046d92 100644 --- a/Source/Scene/PrimitivePipeline.js +++ b/Source/Scene/PrimitivePipeline.js @@ -12,6 +12,7 @@ define([ '../Core/GeometryPipeline', '../Core/IndexDatatype', '../Core/Matrix4', + '../Core/OffsetGeometryInstanceAttribute', '../Core/WebMercatorProjection' ], function( BoundingSphere, @@ -27,6 +28,7 @@ define([ GeometryPipeline, IndexDatatype, Matrix4, + OffsetGeometryInstanceAttribute, WebMercatorProjection) { 'use strict'; @@ -109,7 +111,7 @@ define([ var primitiveType; var length = instances.length; - for (i = 0 ; i < length; ++i) { + for (i = 0; i < length; ++i) { if (defined(instances[i].geometry)) { primitiveType = instances[i].geometry.primitiveType; break; @@ -274,6 +276,8 @@ define([ var length = instances.length; var pickOffsets; + var offsetInstanceExtend; + var hasOffset = false; if (length > 0) { geometries = geometryPipeline(parameters); if (geometries.length > 0) { @@ -282,6 +286,10 @@ define([ pickOffsets = createInstancePickOffsets(instances, geometries); } } + if (defined(instances[0].attributes) && defined(instances[0].attributes.offset)) { + offsetInstanceExtend = new Array(length); + hasOffset = true; + } } var boundingSpheres = new Array(length); @@ -292,6 +300,9 @@ define([ if (defined(geometry)) { boundingSpheres[i] = geometry.boundingSphere; boundingSpheresCV[i] = geometry.boundingSphereCV; + if (hasOffset) { + offsetInstanceExtend[i] = defined(instance.geometry.attributes) && defined(instance.geometry.attributes.applyOffset) && instance.geometry.attributes.applyOffset.values.indexOf(0) !== -1; + } } var eastHemisphereGeometry = instance.eastHemisphereGeometry; @@ -311,6 +322,7 @@ define([ modelMatrix : parameters.modelMatrix, attributeLocations : attributeLocations, pickOffsets : pickOffsets, + offsetInstanceExtend : offsetInstanceExtend, boundingSpheres : boundingSpheres, boundingSpheresCV : boundingSpheresCV }; @@ -318,7 +330,7 @@ define([ function transferGeometry(geometry, transferableObjects) { var attributes = geometry.attributes; - for ( var name in attributes) { + for (var name in attributes) { if (attributes.hasOwnProperty(name)) { var attribute = attributes[name]; @@ -356,7 +368,7 @@ define([ count += 6 + 2 * BoundingSphere.packedLength + (defined(geometry.indices) ? geometry.indices.length : 0); - for ( var property in attributes) { + for (var property in attributes) { if (attributes.hasOwnProperty(property) && defined(attributes[property])) { var attribute = attributes[property]; count += 5 + attribute.values.length; @@ -409,7 +421,7 @@ define([ var attributes = geometry.attributes; var attributesToWrite = []; - for ( var property in attributes) { + for (var property in attributes) { if (attributes.hasOwnProperty(property) && defined(attributes[property])) { attributesToWrite.push(property); if (!defined(stringHash[property])) { @@ -539,14 +551,20 @@ define([ function packInstancesForCombine(instances, transferableObjects) { var length = instances.length; - var packedData = new Float64Array(1 + (length * 16)); + var packedData = new Float64Array(1 + (length * 19)); var count = 0; packedData[count++] = length; for (var i = 0; i < length; i++) { var instance = instances[i]; - Matrix4.pack(instance.modelMatrix, packedData, count); count += Matrix4.packedLength; + if (defined(instance.attributes) && defined(instance.attributes.offset)) { + var values = instance.attributes.offset.value; + packedData[count] = values[0]; + packedData[count + 1] = values[1]; + packedData[count + 2] = values[2]; + } + count += 3; } transferableObjects.push(packedData.buffer); @@ -561,10 +579,18 @@ define([ var i = 1; while (i < packedInstances.length) { var modelMatrix = Matrix4.unpack(packedInstances, i); + var attributes; i += Matrix4.packedLength; + if (defined(packedInstances[i])) { + attributes = { + offset : new OffsetGeometryInstanceAttribute(packedInstances[i], packedInstances[i + 1], packedInstances[i + 2]) + }; + } + i += 3; result[count++] = { - modelMatrix : modelMatrix + modelMatrix : modelMatrix, + attributes : attributes }; } @@ -687,6 +713,7 @@ define([ attributeLocations : results.attributeLocations, modelMatrix : results.modelMatrix, pickOffsets : results.pickOffsets, + offsetInstanceExtend: results.offsetInstanceExtend, boundingSpheres : packedBoundingSpheres, boundingSpheresCV : packedBoundingSpheresCV }; @@ -701,6 +728,7 @@ define([ attributeLocations : packedResult.attributeLocations, modelMatrix : packedResult.modelMatrix, pickOffsets : packedResult.pickOffsets, + offsetInstanceExtend: packedResult.offsetInstanceExtend, boundingSpheres : unpackBoundingSpheres(packedResult.boundingSpheres), boundingSpheresCV : unpackBoundingSpheres(packedResult.boundingSpheresCV) }; diff --git a/Specs/Core/CircleGeometrySpec.js b/Specs/Core/CircleGeometrySpec.js index 1d247c4a7fd0..c31b377543e0 100644 --- a/Specs/Core/CircleGeometrySpec.js +++ b/Specs/Core/CircleGeometrySpec.js @@ -193,6 +193,6 @@ defineSuite([ radius : 1.0, stRotation : CesiumMath.PI_OVER_TWO }); - var packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, CesiumMath.PI_OVER_TWO, 0.0, 0.1, 0.0, 0.0]; + var packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, CesiumMath.PI_OVER_TWO, 0.0, 0.1, 0.0, 0.0, -1]; createPackableSpecs(CircleGeometry, packableInstance, packedInstance); }); diff --git a/Specs/Core/CircleOutlineGeometrySpec.js b/Specs/Core/CircleOutlineGeometrySpec.js index c99c39102320..916e1c203e35 100644 --- a/Specs/Core/CircleOutlineGeometrySpec.js +++ b/Specs/Core/CircleOutlineGeometrySpec.js @@ -104,7 +104,7 @@ defineSuite([ height : 5, extrudedHeight : 7 }); - var packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 2, 2, 0, 7, 1, 5, 4]; + var packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 2, 2, 0, 7, 1, 5, 4, -1]; createPackableSpecs(CircleOutlineGeometry, packableInstance, packedInstance, 'extruded'); //Because extrudedHeight is optional and has to be taken into account when packing, we have a second test without it. @@ -116,6 +116,6 @@ defineSuite([ numberOfVerticalLines : 4, height : 5 }); - packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 2, 2, 0, 5, 1, 5, 4]; + packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 2, 2, 0, 5, 1, 5, 4, -1]; createPackableSpecs(CircleOutlineGeometry, packableInstance, packedInstance, 'at height'); }); diff --git a/Specs/Core/CorridorGeometrySpec.js b/Specs/Core/CorridorGeometrySpec.js index dca5468bf5b2..0fbfe8c00dea 100644 --- a/Specs/Core/CorridorGeometrySpec.js +++ b/Specs/Core/CorridorGeometrySpec.js @@ -1,17 +1,21 @@ defineSuite([ 'Core/CorridorGeometry', + 'Core/arrayFill', 'Core/Cartesian3', 'Core/CornerType', 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Core/Math', 'Core/Rectangle', 'Core/VertexFormat', 'Specs/createPackableSpecs' ], function( CorridorGeometry, + arrayFill, Cartesian3, CornerType, Ellipsoid, + GeometryOffsetAttribute, CesiumMath, Rectangle, VertexFormat, @@ -128,6 +132,76 @@ defineSuite([ expect(m.indices.length).toEqual(numTriangles * 3); }); + it('computes offset attribute', function() { + var m = CorridorGeometry.createGeometry(new CorridorGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + 90.0, -30.0, + 90.0, -35.0 + ]), + cornerType: CornerType.MITERED, + width : 30000, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 12; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for top vertices', function() { + var m = CorridorGeometry.createGeometry(new CorridorGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + 90.0, -30.0, + 90.0, -35.0 + ]), + cornerType: CornerType.MITERED, + width : 30000, + extrudedHeight: 30000, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 72; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 12); + expected = arrayFill(expected, 1, 24, 48); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for all vertices', function() { + var m = CorridorGeometry.createGeometry(new CorridorGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + 90.0, -30.0, + 90.0, -35.0 + ]), + cornerType: CornerType.MITERED, + width : 30000, + extrudedHeight: 30000, + offsetAttribute: GeometryOffsetAttribute.ALL + })); + + var numVertices = 72; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + it('computes right turn', function() { var m = CorridorGeometry.createGeometry(new CorridorGeometry({ vertexFormat : VertexFormat.POSITION_ONLY, @@ -320,6 +394,6 @@ defineSuite([ var packedInstance = [2, positions[0].x, positions[0].y, positions[0].z, positions[1].x, positions[1].y, positions[1].z]; packedInstance.push(Ellipsoid.WGS84.radii.x, Ellipsoid.WGS84.radii.y, Ellipsoid.WGS84.radii.z); packedInstance.push(1.0, 0.0, 0.0, 0.0, 0.0, 0.0); - packedInstance.push(30000.0, 0.0, 0.0, 2.0, 0.1, 0.0); + packedInstance.push(30000.0, 0.0, 0.0, 2.0, 0.1, 0.0, -1); createPackableSpecs(CorridorGeometry, corridor, packedInstance); }); diff --git a/Specs/Core/CorridorOutlineGeometrySpec.js b/Specs/Core/CorridorOutlineGeometrySpec.js index 4392d7b89468..c1b31b935174 100644 --- a/Specs/Core/CorridorOutlineGeometrySpec.js +++ b/Specs/Core/CorridorOutlineGeometrySpec.js @@ -1,14 +1,18 @@ defineSuite([ 'Core/CorridorOutlineGeometry', + 'Core/arrayFill', 'Core/Cartesian3', 'Core/CornerType', 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Specs/createPackableSpecs' ], function( CorridorOutlineGeometry, + arrayFill, Cartesian3, CornerType, Ellipsoid, + GeometryOffsetAttribute, createPackableSpecs) { 'use strict'; @@ -73,6 +77,72 @@ defineSuite([ expect(m.indices.length).toEqual(28 * 2); // 5 segments * 4 lines per segment + 4 lines * 2 ends }); + it('computes offset attribute for top vertices', function() { + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : Cartesian3.fromDegreesArray([ + 90.0, -30.0, + 90.0, -35.0 + ]), + cornerType: CornerType.MITERED, + width : 30000, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 12; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); // 6 left + 6 right + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for top vertices', function() { + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : Cartesian3.fromDegreesArray([ + 90.0, -30.0, + 90.0, -35.0 + ]), + cornerType: CornerType.MITERED, + width : 30000, + extrudedHeight: 30000, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 24; + expect(m.attributes.position.values.length).toEqual(24 * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 12); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for all vertices', function() { + var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ + positions : Cartesian3.fromDegreesArray([ + 90.0, -30.0, + 90.0, -35.0 + ]), + cornerType: CornerType.MITERED, + width : 30000, + extrudedHeight: 30000, + offsetAttribute: GeometryOffsetAttribute.ALL + })); + + var numVertices = 24; + expect(m.attributes.position.values.length).toEqual(24 * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + it('computes right turn', function() { var m = CorridorOutlineGeometry.createGeometry(new CorridorOutlineGeometry({ positions : Cartesian3.fromDegreesArray([ @@ -175,6 +245,6 @@ defineSuite([ }); var packedInstance = [2, positions[0].x, positions[0].y, positions[0].z, positions[1].x, positions[1].y, positions[1].z]; packedInstance.push(Ellipsoid.WGS84.radii.x, Ellipsoid.WGS84.radii.y, Ellipsoid.WGS84.radii.z); - packedInstance.push(30000.0, 0.0, 0.0, 2.0, 0.1); + packedInstance.push(30000.0, 0.0, 0.0, 2.0, 0.1, -1); createPackableSpecs(CorridorOutlineGeometry, corridor, packedInstance); }); diff --git a/Specs/Core/EllipseGeometrySpec.js b/Specs/Core/EllipseGeometrySpec.js index 9f331f0d4841..5003bdcc99fe 100644 --- a/Specs/Core/EllipseGeometrySpec.js +++ b/Specs/Core/EllipseGeometrySpec.js @@ -1,14 +1,18 @@ defineSuite([ 'Core/EllipseGeometry', + 'Core/arrayFill', 'Core/Cartesian3', 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Core/Math', 'Core/VertexFormat', 'Specs/createPackableSpecs' ], function( EllipseGeometry, + arrayFill, Cartesian3, Ellipsoid, + GeometryOffsetAttribute, CesiumMath, VertexFormat, createPackableSpecs) { @@ -139,6 +143,73 @@ defineSuite([ expect(m.indices.length).toEqual(numTriangles * 3); }); + it('computes offset attribute', function() { + var m = EllipseGeometry.createGeometry(new EllipseGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + ellipsoid : Ellipsoid.WGS84, + center : Cartesian3.fromDegrees(0,0), + granularity : 0.1, + semiMajorAxis : 1.0, + semiMinorAxis : 1.0, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 16; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for top vertices', function() { + var m = EllipseGeometry.createGeometry(new EllipseGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + ellipsoid : Ellipsoid.WGS84, + center : Cartesian3.fromDegrees(0,0), + granularity : 0.1, + semiMajorAxis : 1.0, + semiMinorAxis : 1.0, + extrudedHeight : 50000, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 48; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 16); + expected = arrayFill(expected, 1, 32, 40); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for all vertices', function() { + var m = EllipseGeometry.createGeometry(new EllipseGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + ellipsoid : Ellipsoid.WGS84, + center : Cartesian3.fromDegrees(0,0), + granularity : 0.1, + semiMajorAxis : 1.0, + semiMinorAxis : 1.0, + extrudedHeight : 50000, + offsetAttribute: GeometryOffsetAttribute.ALL + })); + + var numVertices = 48; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + it('compute all vertex attributes extruded', function() { var m = EllipseGeometry.createGeometry(new EllipseGeometry({ vertexFormat : VertexFormat.ALL, @@ -311,7 +382,7 @@ defineSuite([ semiMinorAxis : 1.0, stRotation : CesiumMath.PI_OVER_TWO }); - var packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, CesiumMath.PI_OVER_TWO, 0.0, 0.1, 0.0, 0.0]; + var packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, CesiumMath.PI_OVER_TWO, 0.0, 0.1, 0.0, 0.0, -1]; createPackableSpecs(EllipseGeometry, packableInstance, packedInstance); }); diff --git a/Specs/Core/EllipseOutlineGeometrySpec.js b/Specs/Core/EllipseOutlineGeometrySpec.js index 69d38898e1e2..763b9b8dd124 100644 --- a/Specs/Core/EllipseOutlineGeometrySpec.js +++ b/Specs/Core/EllipseOutlineGeometrySpec.js @@ -1,12 +1,16 @@ defineSuite([ 'Core/EllipseOutlineGeometry', + 'Core/arrayFill', 'Core/Cartesian3', 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Specs/createPackableSpecs' ], function( EllipseOutlineGeometry, + arrayFill, Cartesian3, Ellipsoid, + GeometryOffsetAttribute, createPackableSpecs) { 'use strict'; @@ -86,6 +90,69 @@ defineSuite([ expect(m.indices.length).toEqual(24 * 2); // 8 top + 8 bottom + 8 sides }); + it('computes offset attribute', function() { + var m = EllipseOutlineGeometry.createGeometry(new EllipseOutlineGeometry({ + ellipsoid : Ellipsoid.WGS84, + center : Cartesian3.fromDegrees(0,0), + granularity : 0.1, + semiMajorAxis : 1.0, + semiMinorAxis : 1.0, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 8; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for top vertices', function() { + var m = EllipseOutlineGeometry.createGeometry(new EllipseOutlineGeometry({ + ellipsoid : Ellipsoid.WGS84, + center : Cartesian3.fromDegrees(0,0), + granularity : 0.1, + semiMajorAxis : 1.0, + semiMinorAxis : 1.0, + extrudedHeight : 5.0, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 16; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 8); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for all vertices', function() { + var m = EllipseOutlineGeometry.createGeometry(new EllipseOutlineGeometry({ + ellipsoid : Ellipsoid.WGS84, + center : Cartesian3.fromDegrees(0,0), + granularity : 0.1, + semiMajorAxis : 1.0, + semiMinorAxis : 1.0, + extrudedHeight : 5.0, + offsetAttribute: GeometryOffsetAttribute.ALL + })); + + var numVertices = 16; + expect(m.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + it('computes positions extruded, no lines drawn between top and bottom', function() { var m = EllipseOutlineGeometry.createGeometry(new EllipseOutlineGeometry({ ellipsoid : Ellipsoid.WGS84, @@ -147,7 +214,7 @@ defineSuite([ rotation : 6, extrudedHeight : 7 }); - var packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 3, 2, 6, 7, 1, 5, 4]; + var packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 3, 2, 6, 7, 1, 5, 4, -1]; createPackableSpecs(EllipseOutlineGeometry, packableInstance, packedInstance, 'extruded'); //Because extrudedHeight is optional and has to be taken into account when packing, we have a second test without it. @@ -161,6 +228,6 @@ defineSuite([ height : 5, rotation : 6 }); - packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 3, 2, 6, 5, 1, 5, 4]; + packedInstance = [center.x, center.y, center.z, ellipsoid.radii.x, ellipsoid.radii.y, ellipsoid.radii.z, 3, 2, 6, 5, 1, 5, 4, -1]; createPackableSpecs(EllipseOutlineGeometry, packableInstance, packedInstance, 'at height'); }); diff --git a/Specs/Core/PolygonGeometrySpec.js b/Specs/Core/PolygonGeometrySpec.js index 90d519197bb6..20b8f0e8e0b7 100644 --- a/Specs/Core/PolygonGeometrySpec.js +++ b/Specs/Core/PolygonGeometrySpec.js @@ -1,8 +1,10 @@ defineSuite([ 'Core/PolygonGeometry', + 'Core/arrayFill', 'Core/BoundingSphere', 'Core/Cartesian3', 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Core/GeometryPipeline', 'Core/Math', 'Core/Rectangle', @@ -10,9 +12,11 @@ defineSuite([ 'Specs/createPackableSpecs' ], function( PolygonGeometry, + arrayFill, BoundingSphere, Cartesian3, Ellipsoid, + GeometryOffsetAttribute, GeometryPipeline, CesiumMath, Rectangle, @@ -448,6 +452,226 @@ defineSuite([ expect(p.indices.length).toEqual(numTriangles * 3); }); + it('computes offset attribute', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + granularity: CesiumMath.RADIANS_PER_DEGREE, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 13; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for top vertices', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 50; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 13); + expected = arrayFill(expected, 1, 26, 38); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded and not closeTop for top vertices', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + closeTop: false, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 37; // 13 bottom + 8 top edge + 8 bottom edge + 4 top corner + 4 bottom corner + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 13, 25); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded and not closeBottom for top vertcies', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + closeBottom: false, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 37; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 25); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded and not closeBottom or closeTop for top vertices', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + closeTop: false, + closeBottom: false, + offsetAttribute: GeometryOffsetAttribute.TOP + })); + + var numVertices = 24; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 12); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for all vertices', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + offsetAttribute: GeometryOffsetAttribute.ALL + })); + + var numVertices = 50; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded and not closeTop for all vertices', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + closeTop: false, + offsetAttribute: GeometryOffsetAttribute.ALL + })); + + var numVertices = 37; // 13 bottom + 8 top edge + 8 bottom edge + 4 top corner + 4 bottom corner + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded and not closeBottom for all vertcies', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + closeBottom: false, + offsetAttribute: GeometryOffsetAttribute.ALL + })); + + var numVertices = 37; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded and not closeBottom or closeTop for all vertices', function() { + var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ + vertexFormat : VertexFormat.POSITION_ONLY, + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + closeTop: false, + closeBottom: false, + offsetAttribute: GeometryOffsetAttribute.ALL + })); + + var numVertices = 24; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + it('removes duplicates extruded', function() { var p = PolygonGeometry.createGeometry(PolygonGeometry.fromPositions({ vertexFormat : VertexFormat.POSITION_ONLY, @@ -743,6 +967,6 @@ defineSuite([ addPositions(packedInstance, holePositions1); packedInstance.push(Ellipsoid.WGS84.radii.x, Ellipsoid.WGS84.radii.y, Ellipsoid.WGS84.radii.z); packedInstance.push(1.0, 0.0, 0.0, 0.0, 0.0, 0.0); - packedInstance.push(0.0, 0.0, CesiumMath.PI_OVER_THREE, 0.0, 0.0, 1.0, 0, 1, 0, 52); + packedInstance.push(0.0, 0.0, CesiumMath.PI_OVER_THREE, 0.0, 0.0, 1.0, 0, 1, 0, -1, 53); createPackableSpecs(PolygonGeometry, polygon, packedInstance); }); diff --git a/Specs/Core/PolygonOutlineGeometrySpec.js b/Specs/Core/PolygonOutlineGeometrySpec.js index 348d16438730..907a21fb31bb 100644 --- a/Specs/Core/PolygonOutlineGeometrySpec.js +++ b/Specs/Core/PolygonOutlineGeometrySpec.js @@ -1,15 +1,19 @@ defineSuite([ 'Core/PolygonOutlineGeometry', + 'Core/arrayFill', 'Core/BoundingSphere', 'Core/Cartesian3', 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Core/Math', 'Specs/createPackableSpecs' ], function( PolygonOutlineGeometry, + arrayFill, BoundingSphere, Cartesian3, Ellipsoid, + GeometryOffsetAttribute, CesiumMath, createPackableSpecs) { 'use strict'; @@ -360,6 +364,71 @@ defineSuite([ expect(p.indices.length).toEqual(36 * 2); // 12 top + 12 bottom + 12 edges }); + it('computes offset attribute', function() { + var p = PolygonOutlineGeometry.createGeometry(PolygonOutlineGeometry.fromPositions({ + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + offsetAttribute : GeometryOffsetAttribute.TOP + })); + + var numVertices = 8; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for top vertices', function() { + var p = PolygonOutlineGeometry.createGeometry(PolygonOutlineGeometry.fromPositions({ + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + offsetAttribute : GeometryOffsetAttribute.TOP + })); + + var numVertices = 16; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 8); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for all vertices', function() { + var p = PolygonOutlineGeometry.createGeometry(PolygonOutlineGeometry.fromPositions({ + positions : Cartesian3.fromDegreesArray([ + -1.0, -1.0, + 1.0, -1.0, + 1.0, 1.0, + -1.0, 1.0 + ]), + extrudedHeight: 30000, + offsetAttribute : GeometryOffsetAttribute.ALL + })); + + var numVertices = 16; + expect(p.attributes.position.values.length).toEqual(numVertices * 3); + + var offset = p.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + it('undefined is returned if there are less than 3 positions', function() { var polygonOutline = PolygonOutlineGeometry.fromPositions({ positions : Cartesian3.fromDegreesArray([ @@ -415,7 +484,6 @@ defineSuite([ packedInstance.push(3.0, 0.0); addPositions(packedInstance, holePositions1); packedInstance.push(Ellipsoid.WGS84.radii.x, Ellipsoid.WGS84.radii.y, Ellipsoid.WGS84.radii.z); - packedInstance.push(0.0, 0.0, CesiumMath.PI_OVER_THREE, 0.0, 1.0, 42); + packedInstance.push(0.0, 0.0, CesiumMath.PI_OVER_THREE, 0.0, 1.0, -1, 43); createPackableSpecs(PolygonOutlineGeometry, polygon, packedInstance); - }); diff --git a/Specs/Core/RectangleGeometrySpec.js b/Specs/Core/RectangleGeometrySpec.js index 4a6b2a65df44..7e2323c21e91 100644 --- a/Specs/Core/RectangleGeometrySpec.js +++ b/Specs/Core/RectangleGeometrySpec.js @@ -1,9 +1,11 @@ defineSuite([ 'Core/RectangleGeometry', + 'Core/arrayFill', 'Core/Cartesian2', 'Core/Cartesian3', 'Core/Ellipsoid', 'Core/GeographicProjection', + 'Core/GeometryOffsetAttribute', 'Core/Math', 'Core/Matrix2', 'Core/Rectangle', @@ -11,10 +13,12 @@ defineSuite([ 'Specs/createPackableSpecs' ], function( RectangleGeometry, + arrayFill, Cartesian2, Cartesian3, Ellipsoid, GeographicProjection, + GeometryOffsetAttribute, CesiumMath, Matrix2, Rectangle, @@ -262,6 +266,75 @@ defineSuite([ expect(m.indices.length).toEqual(numTriangles * 3); }); + it('computes offset attribute', function() { + var rectangle = new Rectangle(-2.0, -1.0, 0.0, 1.0); + var m = RectangleGeometry.createGeometry(new RectangleGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + rectangle : rectangle, + granularity : 1.0, + offsetAttribute : GeometryOffsetAttribute.TOP + })); + var positions = m.attributes.position.values; + + var numVertices = 9; + expect(positions.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for top vertices', function() { + var rectangle = new Rectangle(-2.0, -1.0, 0.0, 1.0); + var m = RectangleGeometry.createGeometry(new RectangleGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + rectangle : rectangle, + granularity : 1.0, + extrudedHeight : 2, + offsetAttribute : GeometryOffsetAttribute.TOP + })); + var positions = m.attributes.position.values; + + var numVertices = 42; // (9 fill + 8 edge + 4 corners) * 2 to duplicate for bottom + expect(positions.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 9); + for (var i = 18; i < offset.length; i+=2) { + expected[i] = 1; + } + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for all vertices', function() { + var rectangle = new Rectangle(-2.0, -1.0, 0.0, 1.0); + var m = RectangleGeometry.createGeometry(new RectangleGeometry({ + vertexFormat : VertexFormat.POSITION_ONLY, + rectangle : rectangle, + granularity : 1.0, + extrudedHeight : 2, + offsetAttribute : GeometryOffsetAttribute.ALL + })); + var positions = m.attributes.position.values; + + var numVertices = 42; // (9 fill + 8 edge + 4 corners) * 2 to duplicate for bottom + expect(positions.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + for (var i = 18; i < offset.length; i+=2) { + expected[i] = 1; + } + expect(offset).toEqual(expected); + }); + it('undefined is returned if any side are of length zero', function() { var rectangle0 = new RectangleGeometry({ rectangle : Rectangle.fromDegrees(-80.0, 39.0, -80.0, 42.0) @@ -384,6 +457,6 @@ defineSuite([ granularity : 1.0, ellipsoid : Ellipsoid.UNIT_SPHERE }); - var packedInstance = [-2.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]; + var packedInstance = [-2.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1]; createPackableSpecs(RectangleGeometry, rectangle, packedInstance); }); diff --git a/Specs/Core/RectangleOutlineGeometrySpec.js b/Specs/Core/RectangleOutlineGeometrySpec.js index aaf57809d56a..7ed6583e5520 100644 --- a/Specs/Core/RectangleOutlineGeometrySpec.js +++ b/Specs/Core/RectangleOutlineGeometrySpec.js @@ -1,8 +1,10 @@ defineSuite([ 'Core/RectangleOutlineGeometry', + 'Core/arrayFill', 'Core/Cartesian2', 'Core/Cartesian3', 'Core/Ellipsoid', + 'Core/GeometryOffsetAttribute', 'Core/GeographicProjection', 'Core/Math', 'Core/Matrix2', @@ -10,9 +12,11 @@ defineSuite([ 'Specs/createPackableSpecs' ], function( RectangleOutlineGeometry, + arrayFill, Cartesian2, Cartesian3, Ellipsoid, + GeometryOffsetAttribute, GeographicProjection, CesiumMath, Matrix2, @@ -166,6 +170,66 @@ defineSuite([ expect(geometry2).toBeUndefined(); }); + it('computes offset attribute', function() { + var rectangle = new Rectangle(-2.0, -1.0, 0.0, 1.0); + var m = RectangleOutlineGeometry.createGeometry(new RectangleOutlineGeometry({ + rectangle : rectangle, + granularity : 1.0, + offsetAttribute : GeometryOffsetAttribute.TOP + })); + var positions = m.attributes.position.values; + + var numVertices = 8; + expect(positions.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for top vertices', function() { + var rectangle = new Rectangle(-2.0, -1.0, 0.0, 1.0); + var m = RectangleOutlineGeometry.createGeometry(new RectangleOutlineGeometry({ + rectangle : rectangle, + granularity : 1.0, + extrudedHeight : 2, + offsetAttribute : GeometryOffsetAttribute.TOP + })); + var positions = m.attributes.position.values; + + var numVertices = 16; + expect(positions.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 0); + expected = arrayFill(expected, 1, 0, 8); + expect(offset).toEqual(expected); + }); + + it('computes offset attribute extruded for all vertices', function() { + var rectangle = new Rectangle(-2.0, -1.0, 0.0, 1.0); + var m = RectangleOutlineGeometry.createGeometry(new RectangleOutlineGeometry({ + rectangle : rectangle, + granularity : 1.0, + extrudedHeight : 2, + offsetAttribute : GeometryOffsetAttribute.ALL + })); + var positions = m.attributes.position.values; + + var numVertices = 16; + expect(positions.length).toEqual(numVertices * 3); + + var offset = m.attributes.applyOffset.values; + expect(offset.length).toEqual(numVertices); + var expected = new Array(offset.length); + expected = arrayFill(expected, 1); + expect(offset).toEqual(expected); + }); + var rectangle = new RectangleOutlineGeometry({ rectangle : new Rectangle(0.1, 0.2, 0.3, 0.4), ellipsoid : new Ellipsoid(5, 6, 7), @@ -174,7 +238,7 @@ defineSuite([ rotation : 10, extrudedHeight : 11 }); - var packedInstance = [0.1, 0.2, 0.3, 0.4, 5, 6, 7, 8, 11, 10, 9]; + var packedInstance = [0.1, 0.2, 0.3, 0.4, 5, 6, 7, 8, 11, 10, 9, -1]; createPackableSpecs(RectangleOutlineGeometry, rectangle, packedInstance, 'extruded'); rectangle = new RectangleOutlineGeometry({ @@ -184,7 +248,7 @@ defineSuite([ height : 9, rotation : 10 }); - packedInstance = [0.1, 0.2, 0.3, 0.4, 5, 6, 7, 8, 9, 10, 9]; + packedInstance = [0.1, 0.2, 0.3, 0.4, 5, 6, 7, 8, 9, 10, 9, -1]; createPackableSpecs(RectangleOutlineGeometry, rectangle, packedInstance, 'at height'); });