123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- import AttributeCompression from '../Core/AttributeCompression.js';
- import Cartesian3 from '../Core/Cartesian3.js';
- import Color from '../Core/Color.js';
- import ComponentDatatype from '../Core/ComponentDatatype.js';
- import defaultValue from '../Core/defaultValue.js';
- import defined from '../Core/defined.js';
- import defineProperties from '../Core/defineProperties.js';
- import deprecationWarning from '../Core/deprecationWarning.js';
- import destroyObject from '../Core/destroyObject.js';
- import DeveloperError from '../Core/DeveloperError.js';
- import Ellipsoid from '../Core/Ellipsoid.js';
- import getStringFromTypedArray from '../Core/getStringFromTypedArray.js';
- import Matrix3 from '../Core/Matrix3.js';
- import Matrix4 from '../Core/Matrix4.js';
- import Quaternion from '../Core/Quaternion.js';
- import RequestType from '../Core/RequestType.js';
- import RuntimeError from '../Core/RuntimeError.js';
- import Transforms from '../Core/Transforms.js';
- import TranslationRotationScale from '../Core/TranslationRotationScale.js';
- import Pass from '../Renderer/Pass.js';
- import Axis from './Axis.js';
- import Cesium3DTileBatchTable from './Cesium3DTileBatchTable.js';
- import Cesium3DTileFeature from './Cesium3DTileFeature.js';
- import Cesium3DTileFeatureTable from './Cesium3DTileFeatureTable.js';
- import ModelInstanceCollection from './ModelInstanceCollection.js';
- /**
- * Represents the contents of a
- * {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification/TileFormats/Instanced3DModel|Instanced 3D Model}
- * tile in a {@link https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification|3D Tiles} tileset.
- * <p>
- * Implements the {@link Cesium3DTileContent} interface.
- * </p>
- *
- * @alias Instanced3DModel3DTileContent
- * @constructor
- *
- * @private
- */
- function Instanced3DModel3DTileContent(tileset, tile, resource, arrayBuffer, byteOffset) {
- this._tileset = tileset;
- this._tile = tile;
- this._resource = resource;
- this._modelInstanceCollection = undefined;
- this._batchTable = undefined;
- this._features = undefined;
- this.featurePropertiesDirty = false;
- initialize(this, arrayBuffer, byteOffset);
- }
- // This can be overridden for testing purposes
- Instanced3DModel3DTileContent._deprecationWarning = deprecationWarning;
- defineProperties(Instanced3DModel3DTileContent.prototype, {
- featuresLength : {
- get : function() {
- return this._batchTable.featuresLength;
- }
- },
- pointsLength : {
- get : function() {
- return 0;
- }
- },
- trianglesLength : {
- get : function() {
- var model = this._modelInstanceCollection._model;
- if (defined(model)) {
- return model.trianglesLength;
- }
- return 0;
- }
- },
- geometryByteLength : {
- get : function() {
- var model = this._modelInstanceCollection._model;
- if (defined(model)) {
- return model.geometryByteLength;
- }
- return 0;
- }
- },
- texturesByteLength : {
- get : function() {
- var model = this._modelInstanceCollection._model;
- if (defined(model)) {
- return model.texturesByteLength;
- }
- return 0;
- }
- },
- batchTableByteLength : {
- get : function() {
- return this._batchTable.memorySizeInBytes;
- }
- },
- innerContents : {
- get : function() {
- return undefined;
- }
- },
- readyPromise : {
- get : function() {
- return this._modelInstanceCollection.readyPromise;
- }
- },
- 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;
- }
- }
- });
- function getPickIdCallback(content) {
- return function() {
- return content._batchTable.getPickId();
- };
- }
- var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
- var propertyScratch1 = new Array(4);
- var propertyScratch2 = new Array(4);
- function initialize(content, arrayBuffer, byteOffset) {
- var byteStart = defaultValue(byteOffset, 0);
- byteOffset = byteStart;
- 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 Instanced 3D Model version 1 is supported. Version ' + version + ' is not.');
- }
- byteOffset += sizeOfUint32;
- var byteLength = view.getUint32(byteOffset, true);
- byteOffset += sizeOfUint32;
- var featureTableJsonByteLength = view.getUint32(byteOffset, true);
- if (featureTableJsonByteLength === 0) {
- throw new RuntimeError('featureTableJsonByteLength is zero, the feature table must be defined.');
- }
- 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 gltfFormat = view.getUint32(byteOffset, true);
- if (gltfFormat !== 1 && gltfFormat !== 0) {
- throw new RuntimeError('Only glTF format 0 (uri) or 1 (embedded) are supported. Format ' + gltfFormat + ' is not.');
- }
- byteOffset += sizeOfUint32;
- var featureTableString = getStringFromTypedArray(uint8Array, byteOffset, featureTableJsonByteLength);
- var featureTableJson = JSON.parse(featureTableString);
- byteOffset += featureTableJsonByteLength;
- var featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength);
- byteOffset += featureTableBinaryByteLength;
- var featureTable = new Cesium3DTileFeatureTable(featureTableJson, featureTableBinary);
- var instancesLength = featureTable.getGlobalProperty('INSTANCES_LENGTH');
- featureTable.featuresLength = instancesLength;
- if (!defined(instancesLength)) {
- throw new RuntimeError('Feature table global property: INSTANCES_LENGTH must be defined');
- }
- var batchTableJson;
- var batchTableBinary;
- if (batchTableJsonByteLength > 0) {
- 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);
- // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed
- batchTableBinary = new Uint8Array(batchTableBinary);
- byteOffset += batchTableBinaryByteLength;
- }
- }
- content._batchTable = new Cesium3DTileBatchTable(content, instancesLength, batchTableJson, batchTableBinary);
- var gltfByteLength = byteStart + byteLength - byteOffset;
- if (gltfByteLength === 0) {
- throw new RuntimeError('glTF byte length is zero, i3dm must have a glTF to instance.');
- }
- var gltfView;
- if (byteOffset % 4 === 0) {
- gltfView = new Uint8Array(arrayBuffer, byteOffset, gltfByteLength);
- } else {
- // Create a copy of the glb so that it is 4-byte aligned
- Instanced3DModel3DTileContent._deprecationWarning('i3dm-glb-unaligned', 'The embedded glb is not aligned to a 4-byte boundary.');
- gltfView = new Uint8Array(uint8Array.subarray(byteOffset, byteOffset + gltfByteLength));
- }
- var tileset = content._tileset;
- // Create model instance collection
- var collectionOptions = {
- instances : new Array(instancesLength),
- batchTable : content._batchTable,
- cull : false, // Already culled by 3D Tiles
- url : undefined,
- requestType : RequestType.TILES3D,
- gltf : undefined,
- basePath : undefined,
- incrementallyLoadTextures : false,
- upAxis : tileset._gltfUpAxis,
- forwardAxis : Axis.X,
- opaquePass : Pass.CESIUM_3D_TILE, // Draw opaque portions during the 3D Tiles pass
- pickIdLoaded : getPickIdCallback(content),
- imageBasedLightingFactor : tileset.imageBasedLightingFactor,
- lightColor : tileset.lightColor,
- luminanceAtZenith : tileset.luminanceAtZenith,
- sphericalHarmonicCoefficients : tileset.sphericalHarmonicCoefficients,
- specularEnvironmentMaps : tileset.specularEnvironmentMaps
- };
- if (gltfFormat === 0) {
- var gltfUrl = getStringFromTypedArray(gltfView);
- // We need to remove padding from the end of the model URL in case this tile was part of a composite tile.
- // This removes all white space and null characters from the end of the string.
- gltfUrl = gltfUrl.replace(/[\s\0]+$/, '');
- collectionOptions.url = content._resource.getDerivedResource({
- url: gltfUrl
- });
- } else {
- collectionOptions.gltf = gltfView;
- collectionOptions.basePath = content._resource.clone();
- }
- var eastNorthUp = featureTable.getGlobalProperty('EAST_NORTH_UP');
- var rtcCenter;
- var rtcCenterArray = featureTable.getGlobalProperty('RTC_CENTER', ComponentDatatype.FLOAT, 3);
- if (defined(rtcCenterArray)) {
- rtcCenter = Cartesian3.unpack(rtcCenterArray);
- }
- var instances = collectionOptions.instances;
- var instancePosition = new Cartesian3();
- var instancePositionArray = new Array(3);
- var instanceNormalRight = new Cartesian3();
- var instanceNormalUp = new Cartesian3();
- var instanceNormalForward = new Cartesian3();
- var instanceRotation = new Matrix3();
- var instanceQuaternion = new Quaternion();
- var instanceScale = new Cartesian3();
- var instanceTranslationRotationScale = new TranslationRotationScale();
- var instanceTransform = new Matrix4();
- for (var i = 0; i < instancesLength; i++) {
- // Get the instance position
- var position = featureTable.getProperty('POSITION', ComponentDatatype.FLOAT, 3, i, propertyScratch1);
- if (!defined(position)) {
- position = instancePositionArray;
- var positionQuantized = featureTable.getProperty('POSITION_QUANTIZED', ComponentDatatype.UNSIGNED_SHORT, 3, i, propertyScratch1);
- if (!defined(positionQuantized)) {
- throw new RuntimeError('Either POSITION or POSITION_QUANTIZED must be defined for each instance.');
- }
- 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.');
- }
- 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.');
- }
- for (var j = 0; j < 3; j++) {
- position[j] = (positionQuantized[j] / 65535.0 * quantizedVolumeScale[j]) + quantizedVolumeOffset[j];
- }
- }
- Cartesian3.unpack(position, 0, instancePosition);
- if (defined(rtcCenter)) {
- Cartesian3.add(instancePosition, rtcCenter, instancePosition);
- }
- instanceTranslationRotationScale.translation = instancePosition;
- // Get the instance rotation
- var normalUp = featureTable.getProperty('NORMAL_UP', ComponentDatatype.FLOAT, 3, i, propertyScratch1);
- var normalRight = featureTable.getProperty('NORMAL_RIGHT', ComponentDatatype.FLOAT, 3, i, propertyScratch2);
- var hasCustomOrientation = false;
- if (defined(normalUp)) {
- if (!defined(normalRight)) {
- throw new RuntimeError('To define a custom orientation, both NORMAL_UP and NORMAL_RIGHT must be defined.');
- }
- Cartesian3.unpack(normalUp, 0, instanceNormalUp);
- Cartesian3.unpack(normalRight, 0, instanceNormalRight);
- hasCustomOrientation = true;
- } else {
- var octNormalUp = featureTable.getProperty('NORMAL_UP_OCT32P', ComponentDatatype.UNSIGNED_SHORT, 2, i, propertyScratch1);
- var octNormalRight = featureTable.getProperty('NORMAL_RIGHT_OCT32P', ComponentDatatype.UNSIGNED_SHORT, 2, i, propertyScratch2);
- if (defined(octNormalUp)) {
- if (!defined(octNormalRight)) {
- throw new RuntimeError('To define a custom orientation with oct-encoded vectors, both NORMAL_UP_OCT32P and NORMAL_RIGHT_OCT32P must be defined.');
- }
- AttributeCompression.octDecodeInRange(octNormalUp[0], octNormalUp[1], 65535, instanceNormalUp);
- AttributeCompression.octDecodeInRange(octNormalRight[0], octNormalRight[1], 65535, instanceNormalRight);
- hasCustomOrientation = true;
- } else if (eastNorthUp) {
- Transforms.eastNorthUpToFixedFrame(instancePosition, Ellipsoid.WGS84, instanceTransform);
- Matrix4.getMatrix3(instanceTransform, instanceRotation);
- } else {
- Matrix3.clone(Matrix3.IDENTITY, instanceRotation);
- }
- }
- if (hasCustomOrientation) {
- Cartesian3.cross(instanceNormalRight, instanceNormalUp, instanceNormalForward);
- Cartesian3.normalize(instanceNormalForward, instanceNormalForward);
- Matrix3.setColumn(instanceRotation, 0, instanceNormalRight, instanceRotation);
- Matrix3.setColumn(instanceRotation, 1, instanceNormalUp, instanceRotation);
- Matrix3.setColumn(instanceRotation, 2, instanceNormalForward, instanceRotation);
- }
- Quaternion.fromRotationMatrix(instanceRotation, instanceQuaternion);
- instanceTranslationRotationScale.rotation = instanceQuaternion;
- // Get the instance scale
- instanceScale = Cartesian3.fromElements(1.0, 1.0, 1.0, instanceScale);
- var scale = featureTable.getProperty('SCALE', ComponentDatatype.FLOAT, 1, i);
- if (defined(scale)) {
- Cartesian3.multiplyByScalar(instanceScale, scale, instanceScale);
- }
- var nonUniformScale = featureTable.getProperty('SCALE_NON_UNIFORM', ComponentDatatype.FLOAT, 3, i, propertyScratch1);
- if (defined(nonUniformScale)) {
- instanceScale.x *= nonUniformScale[0];
- instanceScale.y *= nonUniformScale[1];
- instanceScale.z *= nonUniformScale[2];
- }
- instanceTranslationRotationScale.scale = instanceScale;
- // Get the batchId
- var batchId = featureTable.getProperty('BATCH_ID', ComponentDatatype.UNSIGNED_SHORT, 1, i);
- if (!defined(batchId)) {
- // If BATCH_ID semantic is undefined, batchId is just the instance number
- batchId = i;
- }
- // Create the model matrix and the instance
- Matrix4.fromTranslationRotationScale(instanceTranslationRotationScale, instanceTransform);
- var modelMatrix = instanceTransform.clone();
- instances[i] = {
- modelMatrix : modelMatrix,
- batchId : batchId
- };
- }
- content._modelInstanceCollection = new ModelInstanceCollection(collectionOptions);
- }
- 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;
- }
- }
- Instanced3DModel3DTileContent.prototype.hasProperty = function(batchId, name) {
- return this._batchTable.hasProperty(batchId, name);
- };
- Instanced3DModel3DTileContent.prototype.getFeature = function(batchId) {
- 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];
- };
- Instanced3DModel3DTileContent.prototype.applyDebugSettings = function(enabled, color) {
- color = enabled ? color : Color.WHITE;
- this._batchTable.setAllColor(color);
- };
- Instanced3DModel3DTileContent.prototype.applyStyle = function(style) {
- this._batchTable.applyStyle(style);
- };
- Instanced3DModel3DTileContent.prototype.update = function(tileset, frameState) {
- var commandStart = frameState.commandList.length;
- // In the PROCESSING state we may be calling update() to move forward
- // the content's resource loading. In the READY state, it will
- // actually generate commands.
- this._batchTable.update(tileset, frameState);
- this._modelInstanceCollection.modelMatrix = this._tile.computedTransform;
- this._modelInstanceCollection.shadows = this._tileset.shadows;
- this._modelInstanceCollection.lightColor = this._tileset.lightColor;
- this._modelInstanceCollection.luminanceAtZenith = this._tileset.luminanceAtZenith;
- this._modelInstanceCollection.sphericalHarmonicCoefficients = this._tileset.sphericalHarmonicCoefficients;
- this._modelInstanceCollection.specularEnvironmentMaps = this._tileset.specularEnvironmentMaps;
- this._modelInstanceCollection.debugWireframe = this._tileset.debugWireframe;
- var model = this._modelInstanceCollection._model;
- if (defined(model)) {
- // Update for clipping planes
- var tilesetClippingPlanes = this._tileset.clippingPlanes;
- model.clippingPlanesOriginMatrix = this._tileset.clippingPlanesOriginMatrix;
- if (defined(tilesetClippingPlanes) && this._tile.clippingPlanesDirty) {
- // Dereference the clipping planes from the model if they are irrelevant - saves on shading
- // Link/Dereference directly to avoid ownership checks.
- model._clippingPlanes = (tilesetClippingPlanes.enabled && this._tile._isClipped) ? tilesetClippingPlanes : undefined;
- }
- // If the model references a different ClippingPlaneCollection due to the tileset's collection being replaced with a
- // ClippingPlaneCollection that gives this tile the same clipping status, update the model to use the new ClippingPlaneCollection.
- if (defined(tilesetClippingPlanes) && defined(model._clippingPlanes) && model._clippingPlanes !== tilesetClippingPlanes) {
- model._clippingPlanes = tilesetClippingPlanes;
- }
- }
- this._modelInstanceCollection.update(frameState);
- // If any commands were pushed, add derived commands
- var commandEnd = frameState.commandList.length;
- if ((commandStart < commandEnd) && (frameState.passes.render || frameState.passes.pick)) {
- this._batchTable.addDerivedCommands(frameState, commandStart, false);
- }
- };
- Instanced3DModel3DTileContent.prototype.isDestroyed = function() {
- return false;
- };
- Instanced3DModel3DTileContent.prototype.destroy = function() {
- this._modelInstanceCollection = this._modelInstanceCollection && this._modelInstanceCollection.destroy();
- this._batchTable = this._batchTable && this._batchTable.destroy();
- return destroyObject(this);
- };
- export default Instanced3DModel3DTileContent;
|