UNPKG

cesium

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

1,260 lines (1,091 loc) 53.7 kB
define([ '../Core/Cartesian2', '../Core/Cartesian3', '../Core/Cartesian4', '../Core/Color', '../Core/combine', '../Core/ComponentDatatype', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', '../Core/destroyObject', '../Core/DeveloperError', '../Core/FeatureDetection', '../Core/getStringFromTypedArray', '../Core/Math', '../Core/Matrix3', '../Core/Matrix4', '../Core/oneTimeWarning', '../Core/OrthographicFrustum', '../Core/Plane', '../Core/PrimitiveType', '../Core/RuntimeError', '../Core/Transforms', '../Renderer/Buffer', '../Renderer/BufferUsage', '../Renderer/DrawCommand', '../Renderer/Pass', '../Renderer/RenderState', '../Renderer/ShaderProgram', '../Renderer/ShaderSource', '../Renderer/VertexArray', '../ThirdParty/when', './BlendingState', './Cesium3DTileBatchTable', './Cesium3DTileFeature', './Cesium3DTileFeatureTable', './ClippingPlaneCollection', './getClipAndStyleCode', './getClippingFunction', './SceneMode', './ShadowMode' ], function( Cartesian2, Cartesian3, Cartesian4, Color, combine, ComponentDatatype, defaultValue, defined, defineProperties, destroyObject, DeveloperError, FeatureDetection, getStringFromTypedArray, CesiumMath, Matrix3, Matrix4, oneTimeWarning, OrthographicFrustum, Plane, PrimitiveType, RuntimeError, Transforms, Buffer, BufferUsage, DrawCommand, Pass, RenderState, ShaderProgram, ShaderSource, VertexArray, when, BlendingState, Cesium3DTileBatchTable, Cesium3DTileFeature, Cesium3DTileFeatureTable, ClippingPlaneCollection, getClipAndStyleCode, getClippingFunction, SceneMode, ShadowMode) { 'use strict'; // Bail out if the browser doesn't support typed arrays, to prevent the setup function // from failing, since we won't be able to create a WebGL context anyway. if (!FeatureDetection.supportsTypedArrays()) { return {}; } /** * Represents the contents of a * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/PointCloud/README.md|Points} * tile in a {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/README.md|3D Tiles} tileset. * <p> * Implements the {@link Cesium3DTileContent} interface. * </p> * * @alias PointCloud3DTileContent * @constructor * * @private */ function PointCloud3DTileContent(tileset, tile, resource, arrayBuffer, byteOffset) { this._tileset = tileset; this._tile = tile; this._resource = resource; // Hold onto the payload until the render resources are created this._parsedContent = undefined; this._drawCommand = undefined; this._pickCommand = undefined; this._pickId = undefined; // Only defined when batchTable is undefined this._isTranslucent = false; this._styleTranslucent = false; this._constantColor = Color.clone(Color.WHITE); this._rtcCenter = undefined; this._batchTable = undefined; // Used when feature table contains BATCH_ID semantic // These values are used to regenerate the shader when the style changes this._styleableShaderAttributes = undefined; this._isQuantized = false; this._isOctEncoded16P = false; this._isRGB565 = false; this._hasColors = false; this._hasNormals = false; this._hasBatchIds = false; // Use per-point normals to hide back-facing points. this.backFaceCulling = false; this._backFaceCulling = false; this._opaqueRenderState = undefined; this._translucentRenderState = undefined; this._highlightColor = Color.clone(Color.WHITE); this._pointSize = 1.0; this._quantizedVolumeScale = undefined; this._quantizedVolumeOffset = undefined; this._modelMatrix = Matrix4.clone(Matrix4.IDENTITY); this._mode = undefined; this._readyPromise = when.defer(); this._pointsLength = 0; this._geometryByteLength = 0; this._features = undefined; this._modelViewMatrix = Matrix4.clone(Matrix4.IDENTITY); this.featurePropertiesDirty = false; // Options for geometric error based attenuation this._attenuation = false; this._geometricErrorScale = undefined; this._maximumAttenuation = undefined; this._baseResolution = undefined; this._baseResolutionApproximation = undefined; initialize(this, arrayBuffer, byteOffset); } defineProperties(PointCloud3DTileContent.prototype, { featuresLength : { get : function() { if (defined(this._batchTable)) { return this._batchTable.featuresLength; } return 0; } }, pointsLength : { get : function() { return this._pointsLength; } }, trianglesLength : { get : function() { return 0; } }, geometryByteLength : { get : function() { return this._geometryByteLength; } }, texturesByteLength : { get : function() { return 0; } }, batchTableByteLength : { get : function() { if (defined(this._batchTable)) { return this._batchTable.memorySizeInBytes; } return 0; } }, innerContents : { get : function() { return undefined; } }, readyPromise : { get : function() { return this._readyPromise.promise; } }, tileset : { get : function() { return this._tileset; } }, tile : { get : function() { return this._tile; } }, url : { get : function() { return this._resource.getUrlComponent(true); } }, batchTable : { get : function() { return this._batchTable; } } }); var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT; function initialize(content, arrayBuffer, byteOffset) { byteOffset = defaultValue(byteOffset, 0); var uint8Array = new Uint8Array(arrayBuffer); var view = new DataView(arrayBuffer); byteOffset += sizeOfUint32; // Skip magic var version = view.getUint32(byteOffset, true); if (version !== 1) { throw new RuntimeError('Only Point Cloud tile version 1 is supported. Version ' + version + ' is not.'); } byteOffset += sizeOfUint32; // Skip byteLength byteOffset += sizeOfUint32; var featureTableJsonByteLength = view.getUint32(byteOffset, true); if (featureTableJsonByteLength === 0) { throw new RuntimeError('Feature table must have a byte length greater than zero'); } byteOffset += sizeOfUint32; var featureTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var batchTableJsonByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var batchTableBinaryByteLength = view.getUint32(byteOffset, true); byteOffset += sizeOfUint32; var featureTableString = getStringFromTypedArray(uint8Array, byteOffset, featureTableJsonByteLength); var featureTableJson = JSON.parse(featureTableString); byteOffset += featureTableJsonByteLength; var featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength); byteOffset += featureTableBinaryByteLength; // Get the batch table JSON and binary var batchTableJson; var batchTableBinary; if (batchTableJsonByteLength > 0) { // Has a batch table JSON var batchTableString = getStringFromTypedArray(uint8Array, byteOffset, batchTableJsonByteLength); batchTableJson = JSON.parse(batchTableString); byteOffset += batchTableJsonByteLength; if (batchTableBinaryByteLength > 0) { // Has a batch table binary batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength); byteOffset += batchTableBinaryByteLength; } } var featureTable = new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary); var pointsLength = featureTable.getGlobalProperty('POINTS_LENGTH'); featureTable.featuresLength = pointsLength; if (!defined(pointsLength)) { throw new RuntimeError('Feature table global property: POINTS_LENGTH must be defined'); } // Get the positions var positions; var isQuantized = false; if (defined(featureTableJson.POSITION)) { positions = featureTable.getPropertyArray('POSITION', ComponentDatatype.FLOAT, 3); var rtcCenter = featureTable.getGlobalProperty('RTC_CENTER', ComponentDatatype.FLOAT, 3); if (defined(rtcCenter)) { content._rtcCenter = Cartesian3.unpack(rtcCenter); } } else if (defined(featureTableJson.POSITION_QUANTIZED)) { positions = featureTable.getPropertyArray('POSITION_QUANTIZED', ComponentDatatype.UNSIGNED_SHORT, 3); isQuantized = true; var quantizedVolumeScale = featureTable.getGlobalProperty('QUANTIZED_VOLUME_SCALE', ComponentDatatype.FLOAT, 3); if (!defined(quantizedVolumeScale)) { throw new RuntimeError('Global property: QUANTIZED_VOLUME_SCALE must be defined for quantized positions.'); } content._quantizedVolumeScale = Cartesian3.unpack(quantizedVolumeScale); var quantizedVolumeOffset = featureTable.getGlobalProperty('QUANTIZED_VOLUME_OFFSET', ComponentDatatype.FLOAT, 3); if (!defined(quantizedVolumeOffset)) { throw new RuntimeError('Global property: QUANTIZED_VOLUME_OFFSET must be defined for quantized positions.'); } content._quantizedVolumeOffset = Cartesian3.unpack(quantizedVolumeOffset); } if (!defined(positions)) { throw new RuntimeError('Either POSITION or POSITION_QUANTIZED must be defined.'); } // Get the colors var colors; var isTranslucent = false; var isRGB565 = false; if (defined(featureTableJson.RGBA)) { colors = featureTable.getPropertyArray('RGBA', ComponentDatatype.UNSIGNED_BYTE, 4); isTranslucent = true; } else if (defined(featureTableJson.RGB)) { colors = featureTable.getPropertyArray('RGB', ComponentDatatype.UNSIGNED_BYTE, 3); } else if (defined(featureTableJson.RGB565)) { colors = featureTable.getPropertyArray('RGB565', ComponentDatatype.UNSIGNED_SHORT, 1); isRGB565 = true; } else if (defined(featureTableJson.CONSTANT_RGBA)) { var constantRGBA = featureTable.getGlobalProperty('CONSTANT_RGBA', ComponentDatatype.UNSIGNED_BYTE, 4); content._constantColor = Color.fromBytes(constantRGBA[0], constantRGBA[1], constantRGBA[2], constantRGBA[3], content._constantColor); } else { // Use a default constant color content._constantColor = Color.clone(Color.DARKGRAY, content._constantColor); } content._isTranslucent = isTranslucent; // Get the normals var normals; var isOctEncoded16P = false; if (defined(featureTableJson.NORMAL)) { normals = featureTable.getPropertyArray('NORMAL', ComponentDatatype.FLOAT, 3); } else if (defined(featureTableJson.NORMAL_OCT16P)) { normals = featureTable.getPropertyArray('NORMAL_OCT16P', ComponentDatatype.UNSIGNED_BYTE, 2); isOctEncoded16P = true; } // Get the batchIds and batch table. BATCH_ID does not need to be defined when the point cloud has per-point properties. var batchIds; if (defined(featureTableJson.BATCH_ID)) { batchIds = featureTable.getPropertyArray('BATCH_ID', ComponentDatatype.UNSIGNED_SHORT, 1); var batchLength = featureTable.getGlobalProperty('BATCH_LENGTH'); if (!defined(batchLength)) { throw new RuntimeError('Global property: BATCH_LENGTH must be defined when BATCH_ID is defined.'); } if (defined(batchTableBinary)) { // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed batchTableBinary = new Uint8Array(batchTableBinary); } content._batchTable = new Cesium3DTileBatchTable(content, batchLength, batchTableJson, batchTableBinary); } // If points are not batched and there are per-point properties, use these properties for styling purposes var styleableProperties; if (!defined(batchIds) && defined(batchTableBinary)) { styleableProperties = Cesium3DTileBatchTable.getBinaryProperties(pointsLength, batchTableJson, batchTableBinary); // WebGL does not support UNSIGNED_INT, INT, or DOUBLE vertex attributes. Convert these to FLOAT. for (var name in styleableProperties) { if (styleableProperties.hasOwnProperty(name)) { var property = styleableProperties[name]; var typedArray = property.typedArray; var componentDatatype = ComponentDatatype.fromTypedArray(typedArray); if (componentDatatype === ComponentDatatype.INT || componentDatatype === ComponentDatatype.UNSIGNED_INT || componentDatatype === ComponentDatatype.DOUBLE) { oneTimeWarning('Cast pnts property to floats', 'Point cloud property "' + name + '" will be casted to a float array because INT, UNSIGNED_INT, and DOUBLE are not valid WebGL vertex attribute types. Some precision may be lost.'); property.typedArray = new Float32Array(typedArray); } } } } content._parsedContent = { positions : positions, colors : colors, normals : normals, batchIds : batchIds, styleableProperties : styleableProperties }; content._pointsLength = pointsLength; content._isQuantized = isQuantized; content._isOctEncoded16P = isOctEncoded16P; content._isRGB565 = isRGB565; content._hasColors = defined(colors); content._hasNormals = defined(normals); content._hasBatchIds = defined(batchIds); // Compute an approximation for base resolution in case it isn't given. // Assume a uniform distribution of points in cubical cells throughout the // bounding sphere around the tile. // Typical use case is leaves, where lower estimates of interpoint distance might // lead to underattenuation. var sphereVolume = content._tile.contentBoundingVolume.boundingSphere.volume(); content._baseResolutionApproximation = CesiumMath.cbrt(sphereVolume / pointsLength); } var scratchPointSizeAndTilesetTimeAndGeometricErrorAndDepthMultiplier = new Cartesian4(); var positionLocation = 0; var colorLocation = 1; var normalLocation = 2; var batchIdLocation = 3; var numberOfAttributes = 4; var scratchClippingPlaneMatrix = new Matrix4(); function createResources(content, frameState) { var context = frameState.context; var parsedContent = content._parsedContent; var pointsLength = content._pointsLength; var positions = parsedContent.positions; var colors = parsedContent.colors; var normals = parsedContent.normals; var batchIds = parsedContent.batchIds; var styleableProperties = parsedContent.styleableProperties; var hasStyleableProperties = defined(styleableProperties); var isQuantized = content._isQuantized; var isOctEncoded16P = content._isOctEncoded16P; var isRGB565 = content._isRGB565; var isTranslucent = content._isTranslucent; var hasColors = content._hasColors; var hasNormals = content._hasNormals; var hasBatchIds = content._hasBatchIds; var batchTable = content._batchTable; var hasBatchTable = defined(batchTable); var styleableVertexAttributes = []; var styleableShaderAttributes = {}; content._styleableShaderAttributes = styleableShaderAttributes; if (hasStyleableProperties) { var attributeLocation = numberOfAttributes; for (var name in styleableProperties) { if (styleableProperties.hasOwnProperty(name)) { var property = styleableProperties[name]; var typedArray = property.typedArray; var componentCount = property.componentCount; var componentDatatype = ComponentDatatype.fromTypedArray(typedArray); var vertexBuffer = Buffer.createVertexBuffer({ context : context, typedArray : property.typedArray, usage : BufferUsage.STATIC_DRAW }); content._geometryByteLength += vertexBuffer.sizeInBytes; var vertexAttribute = { index : attributeLocation, vertexBuffer : vertexBuffer, componentsPerAttribute : componentCount, componentDatatype : componentDatatype, normalize : false, offsetInBytes : 0, strideInBytes : 0 }; styleableVertexAttributes.push(vertexAttribute); styleableShaderAttributes[name] = { location : attributeLocation, componentCount : componentCount }; ++attributeLocation; } } } var uniformMap = { u_pointSizeAndTilesetTimeAndGeometricErrorAndDepthMultiplier : function() { var scratch = scratchPointSizeAndTilesetTimeAndGeometricErrorAndDepthMultiplier; scratch.x = content._attenuation ? content._maximumAttenuation : content._pointSize; scratch.y = content._tileset.timeSinceLoad; if (content._attenuation) { var geometricError = content.tile.geometricError; if (geometricError === 0) { geometricError = defined(content._baseResolution) ? content._baseResolution : content._baseResolutionApproximation; } var frustum = frameState.camera.frustum; var depthMultiplier; // Attenuation is maximumAttenuation in 2D/ortho if (frameState.mode === SceneMode.SCENE2D || frustum instanceof OrthographicFrustum) { depthMultiplier = Number.POSITIVE_INFINITY; } else { depthMultiplier = context.drawingBufferHeight / frameState.camera.frustum.sseDenominator; } scratch.z = geometricError * content._geometricErrorScale; scratch.w = depthMultiplier; } return scratch; }, u_highlightColor : function() { return content._highlightColor; }, u_constantColor : function() { return content._constantColor; }, u_clippingPlanes : function() { var clippingPlanes = content._tileset.clippingPlanes; return (!defined(clippingPlanes) || !clippingPlanes.enabled) ? context.defaultTexture : clippingPlanes.texture; }, u_clippingPlanesEdgeStyle : function() { var clippingPlanes = content._tileset.clippingPlanes; if (!defined(clippingPlanes)) { return Color.WHITE.withAlpha(0.0); } var style = Color.clone(clippingPlanes.edgeColor); style.alpha = clippingPlanes.edgeWidth; return style; }, u_clippingPlanesMatrix : function() { var clippingPlanes = content._tileset.clippingPlanes; if (!defined(clippingPlanes)) { return Matrix4.IDENTITY; } return Matrix4.multiply(content._modelViewMatrix, clippingPlanes.modelMatrix, scratchClippingPlaneMatrix); } }; if (isQuantized) { uniformMap = combine(uniformMap, { u_quantizedVolumeScale : function() { return content._quantizedVolumeScale; } }); } var positionsVertexBuffer = Buffer.createVertexBuffer({ context : context, typedArray : positions, usage : BufferUsage.STATIC_DRAW }); content._geometryByteLength += positionsVertexBuffer.sizeInBytes; var colorsVertexBuffer; if (hasColors) { colorsVertexBuffer = Buffer.createVertexBuffer({ context : context, typedArray : colors, usage : BufferUsage.STATIC_DRAW }); content._geometryByteLength += colorsVertexBuffer.sizeInBytes; } var normalsVertexBuffer; if (hasNormals) { normalsVertexBuffer = Buffer.createVertexBuffer({ context : context, typedArray : normals, usage : BufferUsage.STATIC_DRAW }); content._geometryByteLength += normalsVertexBuffer.sizeInBytes; } var batchIdsVertexBuffer; if (hasBatchIds) { batchIdsVertexBuffer = Buffer.createVertexBuffer({ context : context, typedArray : batchIds, usage : BufferUsage.STATIC_DRAW }); content._geometryByteLength += batchIdsVertexBuffer.sizeInBytes; } var attributes = []; if (isQuantized) { attributes.push({ index : positionLocation, vertexBuffer : positionsVertexBuffer, componentsPerAttribute : 3, componentDatatype : ComponentDatatype.UNSIGNED_SHORT, normalize : true, // Convert position to 0 to 1 before entering the shader offsetInBytes : 0, strideInBytes : 0 }); } else { attributes.push({ index : positionLocation, vertexBuffer : positionsVertexBuffer, componentsPerAttribute : 3, componentDatatype : ComponentDatatype.FLOAT, normalize : false, offsetInBytes : 0, strideInBytes : 0 }); } if (hasColors) { if (isRGB565) { attributes.push({ index : colorLocation, vertexBuffer : colorsVertexBuffer, componentsPerAttribute : 1, componentDatatype : ComponentDatatype.UNSIGNED_SHORT, normalize : false, offsetInBytes : 0, strideInBytes : 0 }); } else { var colorComponentsPerAttribute = isTranslucent ? 4 : 3; attributes.push({ index : colorLocation, vertexBuffer : colorsVertexBuffer, componentsPerAttribute : colorComponentsPerAttribute, componentDatatype : ComponentDatatype.UNSIGNED_BYTE, normalize : true, offsetInBytes : 0, strideInBytes : 0 }); } } if (hasNormals) { if (isOctEncoded16P) { attributes.push({ index : normalLocation, vertexBuffer : normalsVertexBuffer, componentsPerAttribute : 2, componentDatatype : ComponentDatatype.UNSIGNED_BYTE, normalize : false, offsetInBytes : 0, strideInBytes : 0 }); } else { attributes.push({ index : normalLocation, vertexBuffer : normalsVertexBuffer, componentsPerAttribute : 3, componentDatatype : ComponentDatatype.FLOAT, normalize : false, offsetInBytes : 0, strideInBytes : 0 }); } } if (hasBatchIds) { attributes.push({ index : batchIdLocation, vertexBuffer : batchIdsVertexBuffer, componentsPerAttribute : 1, componentDatatype : ComponentDatatype.fromTypedArray(batchIds), normalize : false, offsetInBytes : 0, strideInBytes : 0 }); } if (hasStyleableProperties) { attributes = attributes.concat(styleableVertexAttributes); } var vertexArray = new VertexArray({ context : context, attributes : attributes }); var drawUniformMap = uniformMap; if (hasBatchTable) { drawUniformMap = batchTable.getUniformMapCallback()(uniformMap); } var pickUniformMap; if (hasBatchTable) { pickUniformMap = batchTable.getPickUniformMapCallback()(uniformMap); } else { content._pickId = context.createPickId({ primitive : content._tileset, content : content }); pickUniformMap = combine(uniformMap, { czm_pickColor : function() { return content._pickId.color; } }); } content._opaqueRenderState = RenderState.fromCache({ depthTest : { enabled : true } }); content._translucentRenderState = RenderState.fromCache({ depthTest : { enabled : true }, depthMask : false, blending : BlendingState.ALPHA_BLEND }); content._drawCommand = new DrawCommand({ boundingVolume : undefined, // Updated in update cull : false, // Already culled by 3D Tiles modelMatrix : new Matrix4(), primitiveType : PrimitiveType.POINTS, vertexArray : vertexArray, count : pointsLength, shaderProgram : undefined, // Updated in createShaders uniformMap : drawUniformMap, renderState : isTranslucent ? content._translucentRenderState : content._opaqueRenderState, pass : isTranslucent ? Pass.TRANSLUCENT : Pass.CESIUM_3D_TILE, owner : content, castShadows : false, receiveShadows : false }); content._pickCommand = new DrawCommand({ boundingVolume : undefined, // Updated in update cull : false, // Already culled by 3D Tiles modelMatrix : new Matrix4(), primitiveType : PrimitiveType.POINTS, vertexArray : vertexArray, count : pointsLength, shaderProgram : undefined, // Updated in createShaders uniformMap : pickUniformMap, renderState : isTranslucent ? content._translucentRenderState : content._opaqueRenderState, pass : isTranslucent ? Pass.TRANSLUCENT : Pass.CESIUM_3D_TILE, owner : content }); } var defaultProperties = ['POSITION', 'COLOR', 'NORMAL', 'POSITION_ABSOLUTE']; function getStyleableProperties(source, properties) { // Get all the properties used by this style var regex = /czm_tiles3d_style_(\w+)/g; var matches = regex.exec(source); while (matches !== null) { var name = matches[1]; if (properties.indexOf(name) === -1) { properties.push(name); } matches = regex.exec(source); } } function getVertexAttribute(vertexArray, index) { var numberOfAttributes = vertexArray.numberOfAttributes; for (var i = 0; i < numberOfAttributes; ++i) { var attribute = vertexArray.getAttribute(i); if (attribute.index === index) { return attribute; } } } function modifyStyleFunction(source) { // Replace occurrences of czm_tiles3d_style_DEFAULTPROPERTY var length = defaultProperties.length; for (var i = 0; i < length; ++i) { var property = defaultProperties[i]; var styleName = 'czm_tiles3d_style_' + property; var replaceName = property.toLowerCase(); source = source.replace(new RegExp(styleName + '(\\W)', 'g'), replaceName + '$1'); } // Edit the function header to accept the point position, color, and normal return source.replace('()', '(vec3 position, vec3 position_absolute, vec4 color, vec3 normal)'); } function createShaders(content, frameState, style) { var i; var name; var attribute; var context = frameState.context; var batchTable = content._batchTable; var hasBatchTable = defined(batchTable); var hasStyle = defined(style); var isQuantized = content._isQuantized; var isOctEncoded16P = content._isOctEncoded16P; var isRGB565 = content._isRGB565; var isTranslucent = content._isTranslucent; var hasColors = content._hasColors; var hasNormals = content._hasNormals; var hasBatchIds = content._hasBatchIds; var backFaceCulling = content._backFaceCulling; var vertexArray = content._drawCommand.vertexArray; var clippingPlanes = content._tileset.clippingPlanes; var attenuation = content._attenuation; var colorStyleFunction; var showStyleFunction; var pointSizeStyleFunction; var styleTranslucent = isTranslucent; if (hasBatchTable) { // Styling is handled in the batch table hasStyle = false; } if (hasStyle) { var shaderState = { translucent : false }; colorStyleFunction = style.getColorShaderFunction('getColorFromStyle', 'czm_tiles3d_style_', shaderState); showStyleFunction = style.getShowShaderFunction('getShowFromStyle', 'czm_tiles3d_style_', shaderState); pointSizeStyleFunction = style.getPointSizeShaderFunction('getPointSizeFromStyle', 'czm_tiles3d_style_', shaderState); if (defined(colorStyleFunction) && shaderState.translucent) { styleTranslucent = true; } } content._styleTranslucent = styleTranslucent; var hasColorStyle = defined(colorStyleFunction); var hasShowStyle = defined(showStyleFunction); var hasPointSizeStyle = defined(pointSizeStyleFunction); var hasClippedContent = defined(clippingPlanes) && clippingPlanes.enabled && content._tile._isClipped; // Get the properties in use by the style var styleableProperties = []; if (hasColorStyle) { getStyleableProperties(colorStyleFunction, styleableProperties); colorStyleFunction = modifyStyleFunction(colorStyleFunction); } if (hasShowStyle) { getStyleableProperties(showStyleFunction, styleableProperties); showStyleFunction = modifyStyleFunction(showStyleFunction); } if (hasPointSizeStyle) { getStyleableProperties(pointSizeStyleFunction, styleableProperties); pointSizeStyleFunction = modifyStyleFunction(pointSizeStyleFunction); } var usesColorSemantic = styleableProperties.indexOf('COLOR') >= 0; var usesNormalSemantic = styleableProperties.indexOf('NORMAL') >= 0; // Split default properties from user properties var userProperties = styleableProperties.filter(function(property) { return defaultProperties.indexOf(property) === -1; }); if (usesNormalSemantic && !hasNormals) { throw new RuntimeError('Style references the NORMAL semantic but the point cloud does not have normals'); } // Disable vertex attributes that aren't used in the style, enable attributes that are var styleableShaderAttributes = content._styleableShaderAttributes; for (name in styleableShaderAttributes) { if (styleableShaderAttributes.hasOwnProperty(name)) { attribute = styleableShaderAttributes[name]; var enabled = (userProperties.indexOf(name) >= 0); var vertexAttribute = getVertexAttribute(vertexArray, attribute.location); vertexAttribute.enabled = enabled; } } var usesColors = hasColors && (!hasColorStyle || usesColorSemantic); if (hasColors) { // Disable the color vertex attribute if the color style does not reference the color semantic var colorVertexAttribute = getVertexAttribute(vertexArray, colorLocation); colorVertexAttribute.enabled = usesColors; } var attributeLocations = { a_position : positionLocation }; if (usesColors) { attributeLocations.a_color = colorLocation; } if (hasNormals) { attributeLocations.a_normal = normalLocation; } if (hasBatchIds) { attributeLocations.a_batchId = batchIdLocation; } var attributeDeclarations = ''; var length = userProperties.length; for (i = 0; i < length; ++i) { name = userProperties[i]; attribute = styleableShaderAttributes[name]; if (!defined(attribute)) { throw new RuntimeError('Style references a property "' + name + '" that does not exist or is not styleable.'); } var componentCount = attribute.componentCount; var attributeName = 'czm_tiles3d_style_' + name; var attributeType; if (componentCount === 1) { attributeType = 'float'; } else { attributeType = 'vec' + componentCount; } attributeDeclarations += 'attribute ' + attributeType + ' ' + attributeName + '; \n'; attributeLocations[attributeName] = attribute.location; } var vs = 'attribute vec3 a_position; \n' + 'varying vec4 v_color; \n' + 'uniform vec4 u_pointSizeAndTilesetTimeAndGeometricErrorAndDepthMultiplier; \n' + 'uniform vec4 u_constantColor; \n' + 'uniform vec4 u_highlightColor; \n'; vs += 'float u_pointSize; \n' + 'float u_tilesetTime; \n'; if (attenuation) { vs += 'float u_geometricError; \n' + 'float u_depthMultiplier; \n'; } vs += attributeDeclarations; if (usesColors) { if (isTranslucent) { vs += 'attribute vec4 a_color; \n'; } else if (isRGB565) { vs += 'attribute float a_color; \n' + 'const float SHIFT_RIGHT_11 = 1.0 / 2048.0; \n' + 'const float SHIFT_RIGHT_5 = 1.0 / 32.0; \n' + 'const float SHIFT_LEFT_11 = 2048.0; \n' + 'const float SHIFT_LEFT_5 = 32.0; \n' + 'const float NORMALIZE_6 = 1.0 / 64.0; \n' + 'const float NORMALIZE_5 = 1.0 / 32.0; \n'; } else { vs += 'attribute vec3 a_color; \n'; } } if (hasNormals) { if (isOctEncoded16P) { vs += 'attribute vec2 a_normal; \n'; } else { vs += 'attribute vec3 a_normal; \n'; } } if (hasBatchIds) { vs += 'attribute float a_batchId; \n'; } if (isQuantized) { vs += 'uniform vec3 u_quantizedVolumeScale; \n'; } if (hasColorStyle) { vs += colorStyleFunction; } if (hasShowStyle) { vs += showStyleFunction; } if (hasPointSizeStyle) { vs += pointSizeStyleFunction; } vs += 'void main() \n' + '{ \n' + ' u_pointSize = u_pointSizeAndTilesetTimeAndGeometricErrorAndDepthMultiplier.x; \n' + ' u_tilesetTime = u_pointSizeAndTilesetTimeAndGeometricErrorAndDepthMultiplier.y; \n'; if (attenuation) { vs += ' u_geometricError = u_pointSizeAndTilesetTimeAndGeometricErrorAndDepthMultiplier.z; \n' + ' u_depthMultiplier = u_pointSizeAndTilesetTimeAndGeometricErrorAndDepthMultiplier.w; \n'; } if (usesColors) { if (isTranslucent) { vs += ' vec4 color = a_color; \n'; } else if (isRGB565) { vs += ' float compressed = a_color; \n' + ' float r = floor(compressed * SHIFT_RIGHT_11); \n' + ' compressed -= r * SHIFT_LEFT_11; \n' + ' float g = floor(compressed * SHIFT_RIGHT_5); \n' + ' compressed -= g * SHIFT_LEFT_5; \n' + ' float b = compressed; \n' + ' vec3 rgb = vec3(r * NORMALIZE_5, g * NORMALIZE_6, b * NORMALIZE_5); \n' + ' vec4 color = vec4(rgb, 1.0); \n'; } else { vs += ' vec4 color = vec4(a_color, 1.0); \n'; } } else { vs += ' vec4 color = u_constantColor; \n'; } if (isQuantized) { vs += ' vec3 position = a_position * u_quantizedVolumeScale; \n'; } else { vs += ' vec3 position = a_position; \n'; } vs += ' vec3 position_absolute = vec3(czm_model * vec4(position, 1.0)); \n'; if (hasNormals) { if (isOctEncoded16P) { vs += ' vec3 normal = czm_octDecode(a_normal); \n'; } else { vs += ' vec3 normal = a_normal; \n'; } } else { vs += ' vec3 normal = vec3(1.0); \n'; } if (hasColorStyle) { vs += ' color = getColorFromStyle(position, position_absolute, color, normal); \n'; } if (hasShowStyle) { vs += ' float show = float(getShowFromStyle(position, position_absolute, color, normal)); \n'; } if (hasPointSizeStyle) { vs += ' gl_PointSize = getPointSizeFromStyle(position, position_absolute, color, normal); \n'; } else if (attenuation) { vs += ' vec4 positionEC = czm_modelView * vec4(position, 1.0); \n' + ' float depth = -positionEC.z; \n' + // compute SSE for this point ' gl_PointSize = min((u_geometricError / depth) * u_depthMultiplier, u_pointSize); \n'; } else { vs += ' gl_PointSize = u_pointSize; \n'; } vs += ' color = color * u_highlightColor; \n'; if (hasNormals) { vs += ' normal = czm_normal * normal; \n' + ' float diffuseStrength = czm_getLambertDiffuse(czm_sunDirectionEC, normal); \n' + ' diffuseStrength = max(diffuseStrength, 0.4); \n' + // Apply some ambient lighting ' color.xyz *= diffuseStrength; \n'; } vs += ' v_color = color; \n' + ' gl_Position = czm_modelViewProjection * vec4(position, 1.0); \n'; if (hasNormals && backFaceCulling) { vs += ' float visible = step(-normal.z, 0.0); \n' + ' gl_Position *= visible; \n' + ' gl_PointSize *= visible; \n'; } if (hasShowStyle) { vs += ' gl_Position *= show; \n' + ' gl_PointSize *= show; \n'; } vs += '} \n'; var fs = 'varying vec4 v_color; \n'; if (hasClippedContent) { fs += 'uniform sampler2D u_clippingPlanes; \n' + 'uniform mat4 u_clippingPlanesMatrix; \n' + 'uniform vec4 u_clippingPlanesEdgeStyle; \n'; fs += '\n'; fs += getClippingFunction(clippingPlanes, context); fs += '\n'; } fs += 'void main() \n' + '{ \n' + ' gl_FragColor = v_color; \n'; if (hasClippedContent) { fs += getClipAndStyleCode('u_clippingPlanes', 'u_clippingPlanesMatrix', 'u_clippingPlanesEdgeStyle'); } fs += '} \n'; var drawVS = vs; var drawFS = fs; if (hasBatchTable) { // Batched points always use the HIGHLIGHT color blend mode drawVS = batchTable.getVertexShaderCallback(false, 'a_batchId', undefined)(drawVS); drawFS = batchTable.getFragmentShaderCallback(false, undefined)(drawFS); } var pickVS = vs; var pickFS = fs; if (hasBatchTable) { pickVS = batchTable.getPickVertexShaderCallback('a_batchId')(pickVS); pickFS = batchTable.getPickFragmentShaderCallback()(pickFS); } else { pickFS = ShaderSource.createPickFragmentShaderSource(pickFS, 'uniform'); } var drawCommand = content._drawCommand; if (defined(drawCommand.shaderProgram)) { // Destroy the old shader drawCommand.shaderProgram.destroy(); } drawCommand.shaderProgram = ShaderProgram.fromCache({ context : context, vertexShaderSource : drawVS, fragmentShaderSource : drawFS, attributeLocations : attributeLocations }); var pickCommand = content._pickCommand; if (defined(pickCommand.shaderProgram)) { // Destroy the old shader pickCommand.shaderProgram.destroy(); } pickCommand.shaderProgram = ShaderProgram.fromCache({ context : context, vertexShaderSource : pickVS, fragmentShaderSource : pickFS, attributeLocations : attributeLocations }); try { // Check if the shader compiles correctly. If not there is likely a syntax error with the style. drawCommand.shaderProgram._bind(); } catch (error) { // Rephrase the error. throw new RuntimeError('Error generating style shader: this may be caused by a type mismatch, index out-of-bounds, or other syntax error.'); } } function createFeatures(content) { var featuresLength = content.featuresLength; if (!defined(content._features) && (featuresLength > 0)) { var features = new Array(featuresLength); for (var i = 0; i < featuresLength; ++i) { features[i] = new Cesium3DTileFeature(content, i); } content._features = features; } } PointCloud3DTileContent.prototype.hasProperty = function(batchId, name) { if (defined(this._batchTable)) { return this._batchTable.hasProperty(batchId, name); } return false; }; /** * Part of the {@link Cesium3DTileContent} interface. * * In this context a feature refers to a group of points that share the same BATCH_ID. * For example all the points that represent a door in a house point cloud would be a feature. * * Features are backed by a batch table and can be colored, shown/hidden, picked, etc like features * in b3dm and i3dm. * * When the BATCH_ID semantic is omitted and the point cloud stores per-point properties, they * are not accessible by getFeature. They are only used for dynamic styling. */ PointCloud3DTileContent.prototype.getFeature = function(batchId) { if (!defined(this._batchTable)) { return undefined; } var featuresLength = this.featuresLength; //>>includeStart('debug', pragmas.debug); if (!defined(batchId) || (batchId < 0) || (batchId >= featuresLength)) { throw new DeveloperError('batchId is required and between zero and featuresLength - 1 (' + (featuresLength - 1) + ').'); } //>>includeEnd('debug'); createFeatures(this); return this._features[batchId]; }; PointCloud3DTileContent.prototype.applyDebugSettings = function(enabled, color) { this._highlightColor = enabled ? color : Color.WHITE; }; PointCloud3DTileContent.prototype.applyStyle = function(frameState, style) { if (defined(this._batchTable)) { this._batchTable.applyStyle(frameState, style); } else { createShaders(this, frameState, style); } }; var scratchComputedTranslation = new Cartesian4(); var scratchComputedMatrixIn2D = new Matrix4(); PointCloud3DTileContent.prototype.update = function(tileset, frameState) { var modelMatrix = this._tile.computedTransform; var modelMatrixChanged = !Matrix4.equals(this._modelMatrix, modelMatrix); var updateModelMatrix = modelMatrixChanged || this._mode !== frameState.mode; this._mode = frameState.mode; var context = frameState.context; if (!defined(this._drawCommand)) { createResources(this, frameState); createShaders(this, frameState, tileset.style); updateModelMatrix = true; this._readyPromise.resolve(this); this._parsedContent = undefined; // Unload } // update for clipping planes if (this._tile.clippingPlanesDirty) { createShaders(this, frameState, tileset.style); } var clippingPlanes = this._tileset.clippingPlanes; var clippingEnabled = defined(clippingPlanes) && clippingPlanes.enabled && this._tile._isClipped; if (clippingEnabled) { Matrix4.multiply(context.uniformState.view3D, modelMatrix, this._modelViewMatrix); } // Update attenuation var pointCloudShading = this._tileset.pointCloudShading; if (defined(pointCloudShading)) { var formerAttenuation = this._attenuation; this._attenuation = pointCloudShading.attenuation; this._geometricErrorScale = pointCloudShading.geometricErrorScale; this._maximumAttenuation = defined(pointCloudShading.maximumAttenuation) ? pointCloudShading.maximumAttenuation : tileset.maximumScreenSpaceError; this._baseResolution = pointCloudShading.baseResolution; if (this._attenuation !== formerAttenuation) { createShaders(this, frameState, tileset.style); } } if (updateModelMatrix) { Matrix4.clone(modelMatrix, this._modelMatrix); if (defined(this._rtcCenter)) {