123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- 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 defined from "../Core/defined.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 I3dmParser from "./I3dmParser.js";
- import ModelInstanceCollection from "./ModelInstanceCollection.js";
- import ModelAnimationLoop from "./ModelAnimationLoop.js";
- /**
- * Represents the contents of a
- * {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification/TileFormats/Instanced3DModel|Instanced 3D Model}
- * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/main/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._metadata = undefined;
- this._batchTable = undefined;
- this._features = undefined;
- this.featurePropertiesDirty = false;
- this._group = undefined;
- initialize(this, arrayBuffer, byteOffset);
- }
- // This can be overridden for testing purposes
- Instanced3DModel3DTileContent._deprecationWarning = deprecationWarning;
- Object.defineProperties(Instanced3DModel3DTileContent.prototype, {
- featuresLength: {
- get: function () {
- return this._batchTable.featuresLength;
- },
- },
- pointsLength: {
- get: function () {
- return 0;
- },
- },
- trianglesLength: {
- get: function () {
- const model = this._modelInstanceCollection._model;
- if (defined(model)) {
- return model.trianglesLength;
- }
- return 0;
- },
- },
- geometryByteLength: {
- get: function () {
- const model = this._modelInstanceCollection._model;
- if (defined(model)) {
- return model.geometryByteLength;
- }
- return 0;
- },
- },
- texturesByteLength: {
- get: function () {
- const 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);
- },
- },
- metadata: {
- get: function () {
- return this._metadata;
- },
- set: function (value) {
- this._metadata = value;
- },
- },
- batchTable: {
- get: function () {
- return this._batchTable;
- },
- },
- group: {
- get: function () {
- return this._group;
- },
- set: function (value) {
- this._group = value;
- },
- },
- });
- function getPickIdCallback(content) {
- return function () {
- return content._batchTable.getPickId();
- };
- }
- const propertyScratch1 = new Array(4);
- const propertyScratch2 = new Array(4);
- function initialize(content, arrayBuffer, byteOffset) {
- const i3dm = I3dmParser.parse(arrayBuffer, byteOffset);
- const gltfFormat = i3dm.gltfFormat;
- const gltfView = i3dm.gltf;
- const featureTableJson = i3dm.featureTableJson;
- const featureTableBinary = i3dm.featureTableBinary;
- const batchTableJson = i3dm.batchTableJson;
- const batchTableBinary = i3dm.batchTableBinary;
- const featureTable = new Cesium3DTileFeatureTable(
- featureTableJson,
- featureTableBinary
- );
- const instancesLength = featureTable.getGlobalProperty("INSTANCES_LENGTH");
- featureTable.featuresLength = instancesLength;
- if (!defined(instancesLength)) {
- throw new RuntimeError(
- "Feature table global property: INSTANCES_LENGTH must be defined"
- );
- }
- content._batchTable = new Cesium3DTileBatchTable(
- content,
- instancesLength,
- batchTableJson,
- batchTableBinary
- );
- const tileset = content._tileset;
- // Create model instance collection
- const 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),
- imageBasedLighting: tileset.imageBasedLighting,
- specularEnvironmentMaps: tileset.specularEnvironmentMaps,
- backFaceCulling: tileset.backFaceCulling,
- showOutline: tileset.showOutline,
- showCreditsOnScreen: tileset.showCreditsOnScreen,
- };
- if (gltfFormat === 0) {
- let 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();
- }
- const eastNorthUp = featureTable.getGlobalProperty("EAST_NORTH_UP");
- let rtcCenter;
- const rtcCenterArray = featureTable.getGlobalProperty(
- "RTC_CENTER",
- ComponentDatatype.FLOAT,
- 3
- );
- if (defined(rtcCenterArray)) {
- rtcCenter = Cartesian3.unpack(rtcCenterArray);
- }
- const instances = collectionOptions.instances;
- const instancePosition = new Cartesian3();
- const instancePositionArray = new Array(3);
- const instanceNormalRight = new Cartesian3();
- const instanceNormalUp = new Cartesian3();
- const instanceNormalForward = new Cartesian3();
- const instanceRotation = new Matrix3();
- const instanceQuaternion = new Quaternion();
- let instanceScale = new Cartesian3();
- const instanceTranslationRotationScale = new TranslationRotationScale();
- const instanceTransform = new Matrix4();
- for (let i = 0; i < instancesLength; i++) {
- // Get the instance position
- let position = featureTable.getProperty(
- "POSITION",
- ComponentDatatype.FLOAT,
- 3,
- i,
- propertyScratch1
- );
- if (!defined(position)) {
- position = instancePositionArray;
- const 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."
- );
- }
- const 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."
- );
- }
- const 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 (let 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
- const normalUp = featureTable.getProperty(
- "NORMAL_UP",
- ComponentDatatype.FLOAT,
- 3,
- i,
- propertyScratch1
- );
- const normalRight = featureTable.getProperty(
- "NORMAL_RIGHT",
- ComponentDatatype.FLOAT,
- 3,
- i,
- propertyScratch2
- );
- let 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 {
- const octNormalUp = featureTable.getProperty(
- "NORMAL_UP_OCT32P",
- ComponentDatatype.UNSIGNED_SHORT,
- 2,
- i,
- propertyScratch1
- );
- const 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);
- const scale = featureTable.getProperty(
- "SCALE",
- ComponentDatatype.FLOAT,
- 1,
- i
- );
- if (defined(scale)) {
- Cartesian3.multiplyByScalar(instanceScale, scale, instanceScale);
- }
- const 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
- let 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
- );
- const modelMatrix = instanceTransform.clone();
- instances[i] = {
- modelMatrix: modelMatrix,
- batchId: batchId,
- };
- }
- content._modelInstanceCollection = new ModelInstanceCollection(
- collectionOptions
- );
- content._modelInstanceCollection.readyPromise
- .catch(function () {
- // Any readyPromise failure is handled in modelInstanceCollection
- })
- .then(function (collection) {
- if (content._modelInstanceCollection.ready) {
- collection.activeAnimations.addAll({
- loop: ModelAnimationLoop.REPEAT,
- });
- }
- });
- }
- function createFeatures(content) {
- const featuresLength = content.featuresLength;
- if (!defined(content._features) && featuresLength > 0) {
- const features = new Array(featuresLength);
- for (let 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) {
- const 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
- ) {
- const 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.imageBasedLighting = this._tileset.imageBasedLighting;
- this._modelInstanceCollection.backFaceCulling = this._tileset.backFaceCulling;
- this._modelInstanceCollection.debugWireframe = this._tileset.debugWireframe;
- this._modelInstanceCollection.showCreditsOnScreen = this._tileset.showCreditsOnScreen;
- this._modelInstanceCollection.splitDirection = this._tileset.splitDirection;
- const model = this._modelInstanceCollection._model;
- if (defined(model)) {
- // Update for clipping planes
- const tilesetClippingPlanes = this._tileset.clippingPlanes;
- model.referenceMatrix = 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
- const 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;
|