123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167 |
- import BoundingSphere from "../Core/BoundingSphere.js";
- import Cartesian2 from "../Core/Cartesian2.js";
- import Cartesian3 from "../Core/Cartesian3.js";
- import Check from "../Core/Check.js";
- import clone from "../Core/clone.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 destroyObject from "../Core/destroyObject.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import Matrix4 from "../Core/Matrix4.js";
- import PrimitiveType from "../Core/PrimitiveType.js";
- import Resource from "../Core/Resource.js";
- import RuntimeError from "../Core/RuntimeError.js";
- import Transforms from "../Core/Transforms.js";
- import Buffer from "../Renderer/Buffer.js";
- import BufferUsage from "../Renderer/BufferUsage.js";
- import DrawCommand from "../Renderer/DrawCommand.js";
- import Pass from "../Renderer/Pass.js";
- import RenderState from "../Renderer/RenderState.js";
- import ShaderSource from "../Renderer/ShaderSource.js";
- import ForEach from "../ThirdParty/GltfPipeline/ForEach.js";
- import when from "../ThirdParty/when.js";
- import Model from "./Model.js";
- import ModelInstance from "./ModelInstance.js";
- import ModelUtility from "./ModelUtility.js";
- import SceneMode from "./SceneMode.js";
- import ShadowMode from "./ShadowMode.js";
- var LoadState = {
- NEEDS_LOAD: 0,
- LOADING: 1,
- LOADED: 2,
- FAILED: 3,
- };
- /**
- * A 3D model instance collection. All instances reference the same underlying model, but have unique
- * per-instance properties like model matrix, pick id, etc.
- *
- * Instances are rendered relative-to-center and for best results instances should be positioned close to one another.
- * Otherwise there may be precision issues if, for example, instances are placed on opposite sides of the globe.
- *
- * @alias ModelInstanceCollection
- * @constructor
- *
- * @param {Object} options Object with the following properties:
- * @param {Object[]} [options.instances] An array of instances, where each instance contains a modelMatrix and optional batchId when options.batchTable is defined.
- * @param {Cesium3DTileBatchTable} [options.batchTable] The batch table of the instanced 3D Tile.
- * @param {Resource|String} [options.url] The url to the .gltf file.
- * @param {Object} [options.requestType] The request type, used for request prioritization
- * @param {Object|ArrayBuffer|Uint8Array} [options.gltf] A glTF JSON object, or a binary glTF buffer.
- * @param {Resource|String} [options.basePath=''] The base path that paths in the glTF JSON are relative to.
- * @param {Boolean} [options.dynamic=false] Hint if instance model matrices will be updated frequently.
- * @param {Boolean} [options.show=true] Determines if the collection will be shown.
- * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each instance is pickable with {@link Scene#pick}.
- * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
- * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
- * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the collection casts or receives shadows from light sources.
- * @param {Cartesian2} [options.imageBasedLightingFactor=new Cartesian2(1.0, 1.0)] Scales the diffuse and specular image-based lighting from the earth, sky, atmosphere and star skybox.
- * @param {Cartesian3} [options.lightColor] The light color when shading models. When <code>undefined</code> the scene's light color is used instead.
- * @param {Number} [options.luminanceAtZenith=0.2] The sun's luminance at the zenith in kilo candela per meter squared to use for this model's procedural environment map.
- * @param {Cartesian3[]} [options.sphericalHarmonicCoefficients] The third order spherical harmonic coefficients used for the diffuse color of image-based lighting.
- * @param {String} [options.specularEnvironmentMaps] A URL to a KTX file that contains a cube map of the specular lighting and the convoluted specular mipmaps.
- * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the glTF material's doubleSided property; when false, back face culling is disabled.
- * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for the collection.
- * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the instances in wireframe.
- *
- * @exception {DeveloperError} Must specify either <options.gltf> or <options.url>, but not both.
- * @exception {DeveloperError} Shader program cannot be optimized for instancing. Parameters cannot have any of the following semantics: MODEL, MODELINVERSE, MODELVIEWINVERSE, MODELVIEWPROJECTIONINVERSE, MODELINVERSETRANSPOSE.
- *
- * @private
- */
- function ModelInstanceCollection(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- //>>includeStart('debug', pragmas.debug);
- if (!defined(options.gltf) && !defined(options.url)) {
- throw new DeveloperError("Either options.gltf or options.url is required.");
- }
- if (defined(options.gltf) && defined(options.url)) {
- throw new DeveloperError(
- "Cannot pass in both options.gltf and options.url."
- );
- }
- //>>includeEnd('debug');
- this.show = defaultValue(options.show, true);
- this._instancingSupported = false;
- this._dynamic = defaultValue(options.dynamic, false);
- this._allowPicking = defaultValue(options.allowPicking, true);
- this._ready = false;
- this._readyPromise = when.defer();
- this._state = LoadState.NEEDS_LOAD;
- this._dirty = false;
- // Undocumented options
- this._cull = defaultValue(options.cull, true);
- this._opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);
- this._instances = createInstances(this, options.instances);
- // When the model instance collection is backed by an i3dm tile,
- // use its batch table resources to modify the shaders, attributes, and uniform maps.
- this._batchTable = options.batchTable;
- this._model = undefined;
- this._vertexBufferTypedArray = undefined; // Hold onto the vertex buffer contents when dynamic is true
- this._vertexBuffer = undefined;
- this._batchIdBuffer = undefined;
- this._instancedUniformsByProgram = undefined;
- this._drawCommands = [];
- this._modelCommands = undefined;
- this._renderStates = undefined;
- this._disableCullingRenderStates = undefined;
- this._boundingSphere = createBoundingSphere(this);
- this._center = Cartesian3.clone(this._boundingSphere.center);
- this._rtcTransform = new Matrix4();
- this._rtcModelView = new Matrix4(); // Holds onto uniform
- this._mode = undefined;
- this.modelMatrix = Matrix4.clone(Matrix4.IDENTITY);
- this._modelMatrix = Matrix4.clone(this.modelMatrix);
- // Passed on to Model
- this._url = Resource.createIfNeeded(options.url);
- this._requestType = options.requestType;
- this._gltf = options.gltf;
- this._basePath = Resource.createIfNeeded(options.basePath);
- this._asynchronous = options.asynchronous;
- this._incrementallyLoadTextures = options.incrementallyLoadTextures;
- this._upAxis = options.upAxis; // Undocumented option
- this._forwardAxis = options.forwardAxis; // Undocumented option
- this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED);
- this._shadows = this.shadows;
- this._pickIdLoaded = options.pickIdLoaded;
- this.debugShowBoundingVolume = defaultValue(
- options.debugShowBoundingVolume,
- false
- );
- this._debugShowBoundingVolume = false;
- this.debugWireframe = defaultValue(options.debugWireframe, false);
- this._debugWireframe = false;
- this._imageBasedLightingFactor = new Cartesian2(1.0, 1.0);
- Cartesian2.clone(
- options.imageBasedLightingFactor,
- this._imageBasedLightingFactor
- );
- this.lightColor = options.lightColor;
- this.luminanceAtZenith = options.luminanceAtZenith;
- this.sphericalHarmonicCoefficients = options.sphericalHarmonicCoefficients;
- this.specularEnvironmentMaps = options.specularEnvironmentMaps;
- this.backFaceCulling = defaultValue(options.backFaceCulling, true);
- this._backFaceCulling = this.backFaceCulling;
- }
- Object.defineProperties(ModelInstanceCollection.prototype, {
- allowPicking: {
- get: function () {
- return this._allowPicking;
- },
- },
- length: {
- get: function () {
- return this._instances.length;
- },
- },
- activeAnimations: {
- get: function () {
- return this._model.activeAnimations;
- },
- },
- ready: {
- get: function () {
- return this._ready;
- },
- },
- readyPromise: {
- get: function () {
- return this._readyPromise.promise;
- },
- },
- imageBasedLightingFactor: {
- get: function () {
- return this._imageBasedLightingFactor;
- },
- set: function (value) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("imageBasedLightingFactor", value);
- Check.typeOf.number.greaterThanOrEquals(
- "imageBasedLightingFactor.x",
- value.x,
- 0.0
- );
- Check.typeOf.number.lessThanOrEquals(
- "imageBasedLightingFactor.x",
- value.x,
- 1.0
- );
- Check.typeOf.number.greaterThanOrEquals(
- "imageBasedLightingFactor.y",
- value.y,
- 0.0
- );
- Check.typeOf.number.lessThanOrEquals(
- "imageBasedLightingFactor.y",
- value.y,
- 1.0
- );
- //>>includeEnd('debug');
- Cartesian2.clone(value, this._imageBasedLightingFactor);
- },
- },
- });
- function createInstances(collection, instancesOptions) {
- instancesOptions = defaultValue(instancesOptions, []);
- var length = instancesOptions.length;
- var instances = new Array(length);
- for (var i = 0; i < length; ++i) {
- var instanceOptions = instancesOptions[i];
- var modelMatrix = instanceOptions.modelMatrix;
- var instanceId = defaultValue(instanceOptions.batchId, i);
- instances[i] = new ModelInstance(collection, modelMatrix, instanceId);
- }
- return instances;
- }
- function createBoundingSphere(collection) {
- var instancesLength = collection.length;
- var points = new Array(instancesLength);
- for (var i = 0; i < instancesLength; ++i) {
- points[i] = Matrix4.getTranslation(
- collection._instances[i]._modelMatrix,
- new Cartesian3()
- );
- }
- return BoundingSphere.fromPoints(points);
- }
- var scratchCartesian = new Cartesian3();
- var scratchMatrix = new Matrix4();
- ModelInstanceCollection.prototype.expandBoundingSphere = function (
- instanceModelMatrix
- ) {
- var translation = Matrix4.getTranslation(
- instanceModelMatrix,
- scratchCartesian
- );
- BoundingSphere.expand(
- this._boundingSphere,
- translation,
- this._boundingSphere
- );
- };
- function getCheckUniformSemanticFunction(
- modelSemantics,
- supportedSemantics,
- programId,
- uniformMap
- ) {
- return function (uniform, uniformName) {
- var semantic = uniform.semantic;
- if (defined(semantic) && modelSemantics.indexOf(semantic) > -1) {
- if (supportedSemantics.indexOf(semantic) > -1) {
- uniformMap[uniformName] = semantic;
- } else {
- throw new RuntimeError(
- "Shader program cannot be optimized for instancing. " +
- 'Uniform "' +
- uniformName +
- '" in program "' +
- programId +
- '" uses unsupported semantic "' +
- semantic +
- '"'
- );
- }
- }
- };
- }
- function getInstancedUniforms(collection, programId) {
- if (defined(collection._instancedUniformsByProgram)) {
- return collection._instancedUniformsByProgram[programId];
- }
- var instancedUniformsByProgram = {};
- collection._instancedUniformsByProgram = instancedUniformsByProgram;
- // When using CESIUM_RTC_MODELVIEW the CESIUM_RTC center is ignored. Instances are always rendered relative-to-center.
- var modelSemantics = [
- "MODEL",
- "MODELVIEW",
- "CESIUM_RTC_MODELVIEW",
- "MODELVIEWPROJECTION",
- "MODELINVERSE",
- "MODELVIEWINVERSE",
- "MODELVIEWPROJECTIONINVERSE",
- "MODELINVERSETRANSPOSE",
- "MODELVIEWINVERSETRANSPOSE",
- ];
- var supportedSemantics = [
- "MODELVIEW",
- "CESIUM_RTC_MODELVIEW",
- "MODELVIEWPROJECTION",
- "MODELVIEWINVERSETRANSPOSE",
- ];
- var techniques = collection._model._sourceTechniques;
- for (var techniqueId in techniques) {
- if (techniques.hasOwnProperty(techniqueId)) {
- var technique = techniques[techniqueId];
- var program = technique.program;
- // Different techniques may share the same program, skip if already processed.
- // This assumes techniques that share a program do not declare different semantics for the same uniforms.
- if (!defined(instancedUniformsByProgram[program])) {
- var uniformMap = {};
- instancedUniformsByProgram[program] = uniformMap;
- ForEach.techniqueUniform(
- technique,
- getCheckUniformSemanticFunction(
- modelSemantics,
- supportedSemantics,
- programId,
- uniformMap
- )
- );
- }
- }
- }
- return instancedUniformsByProgram[programId];
- }
- function getVertexShaderCallback(collection) {
- return function (vs, programId) {
- var instancedUniforms = getInstancedUniforms(collection, programId);
- var usesBatchTable = defined(collection._batchTable);
- var renamedSource = ShaderSource.replaceMain(vs, "czm_instancing_main");
- var globalVarsHeader = "";
- var globalVarsMain = "";
- for (var uniform in instancedUniforms) {
- if (instancedUniforms.hasOwnProperty(uniform)) {
- var semantic = instancedUniforms[uniform];
- var varName;
- if (semantic === "MODELVIEW" || semantic === "CESIUM_RTC_MODELVIEW") {
- varName = "czm_instanced_modelView";
- } else if (semantic === "MODELVIEWPROJECTION") {
- varName = "czm_instanced_modelViewProjection";
- globalVarsHeader += "mat4 czm_instanced_modelViewProjection;\n";
- globalVarsMain +=
- "czm_instanced_modelViewProjection = czm_projection * czm_instanced_modelView;\n";
- } else if (semantic === "MODELVIEWINVERSETRANSPOSE") {
- varName = "czm_instanced_modelViewInverseTranspose";
- globalVarsHeader += "mat3 czm_instanced_modelViewInverseTranspose;\n";
- globalVarsMain +=
- "czm_instanced_modelViewInverseTranspose = mat3(czm_instanced_modelView);\n";
- }
- // Remove the uniform declaration
- var regex = new RegExp("uniform.*" + uniform + ".*");
- renamedSource = renamedSource.replace(regex, "");
- // Replace all occurrences of the uniform with the global variable
- regex = new RegExp(uniform + "\\b", "g");
- renamedSource = renamedSource.replace(regex, varName);
- }
- }
- // czm_instanced_model is the model matrix of the instance relative to center
- // czm_instanced_modifiedModelView is the transform from the center to view
- // czm_instanced_nodeTransform is the local offset of the node within the model
- var uniforms =
- "uniform mat4 czm_instanced_modifiedModelView;\n" +
- "uniform mat4 czm_instanced_nodeTransform;\n";
- var batchIdAttribute;
- var pickAttribute;
- var pickVarying;
- if (usesBatchTable) {
- batchIdAttribute = "attribute float a_batchId;\n";
- pickAttribute = "";
- pickVarying = "";
- } else {
- batchIdAttribute = "";
- pickAttribute =
- "attribute vec4 pickColor;\n" + "varying vec4 v_pickColor;\n";
- pickVarying = " v_pickColor = pickColor;\n";
- }
- var instancedSource =
- uniforms +
- globalVarsHeader +
- "mat4 czm_instanced_modelView;\n" +
- "attribute vec4 czm_modelMatrixRow0;\n" +
- "attribute vec4 czm_modelMatrixRow1;\n" +
- "attribute vec4 czm_modelMatrixRow2;\n" +
- batchIdAttribute +
- pickAttribute +
- renamedSource +
- "void main()\n" +
- "{\n" +
- " mat4 czm_instanced_model = mat4(czm_modelMatrixRow0.x, czm_modelMatrixRow1.x, czm_modelMatrixRow2.x, 0.0, czm_modelMatrixRow0.y, czm_modelMatrixRow1.y, czm_modelMatrixRow2.y, 0.0, czm_modelMatrixRow0.z, czm_modelMatrixRow1.z, czm_modelMatrixRow2.z, 0.0, czm_modelMatrixRow0.w, czm_modelMatrixRow1.w, czm_modelMatrixRow2.w, 1.0);\n" +
- " czm_instanced_modelView = czm_instanced_modifiedModelView * czm_instanced_model * czm_instanced_nodeTransform;\n" +
- globalVarsMain +
- " czm_instancing_main();\n" +
- pickVarying +
- "}\n";
- if (usesBatchTable) {
- var gltf = collection._model.gltf;
- var diffuseAttributeOrUniformName = ModelUtility.getDiffuseAttributeOrUniform(
- gltf,
- programId
- );
- instancedSource = collection._batchTable.getVertexShaderCallback(
- true,
- "a_batchId",
- diffuseAttributeOrUniformName
- )(instancedSource);
- }
- return instancedSource;
- };
- }
- function getFragmentShaderCallback(collection) {
- return function (fs, programId) {
- var batchTable = collection._batchTable;
- if (defined(batchTable)) {
- var gltf = collection._model.gltf;
- var diffuseAttributeOrUniformName = ModelUtility.getDiffuseAttributeOrUniform(
- gltf,
- programId
- );
- fs = batchTable.getFragmentShaderCallback(
- true,
- diffuseAttributeOrUniformName
- )(fs);
- } else {
- fs = "varying vec4 v_pickColor;\n" + fs;
- }
- return fs;
- };
- }
- function createModifiedModelView(collection, context) {
- return function () {
- return Matrix4.multiply(
- context.uniformState.view,
- collection._rtcTransform,
- collection._rtcModelView
- );
- };
- }
- function createNodeTransformFunction(node) {
- return function () {
- return node.computedMatrix;
- };
- }
- function getUniformMapCallback(collection, context) {
- return function (uniformMap, programId, node) {
- uniformMap = clone(uniformMap);
- uniformMap.czm_instanced_modifiedModelView = createModifiedModelView(
- collection,
- context
- );
- uniformMap.czm_instanced_nodeTransform = createNodeTransformFunction(node);
- // Remove instanced uniforms from the uniform map
- var instancedUniforms = getInstancedUniforms(collection, programId);
- for (var uniform in instancedUniforms) {
- if (instancedUniforms.hasOwnProperty(uniform)) {
- delete uniformMap[uniform];
- }
- }
- if (defined(collection._batchTable)) {
- uniformMap = collection._batchTable.getUniformMapCallback()(uniformMap);
- }
- return uniformMap;
- };
- }
- function getVertexShaderNonInstancedCallback(collection) {
- return function (vs, programId) {
- if (defined(collection._batchTable)) {
- var gltf = collection._model.gltf;
- var diffuseAttributeOrUniformName = ModelUtility.getDiffuseAttributeOrUniform(
- gltf,
- programId
- );
- vs = collection._batchTable.getVertexShaderCallback(
- true,
- "a_batchId",
- diffuseAttributeOrUniformName
- )(vs);
- // Treat a_batchId as a uniform rather than a vertex attribute
- vs = "uniform float a_batchId\n;" + vs;
- }
- return vs;
- };
- }
- function getFragmentShaderNonInstancedCallback(collection) {
- return function (fs, programId) {
- var batchTable = collection._batchTable;
- if (defined(batchTable)) {
- var gltf = collection._model.gltf;
- var diffuseAttributeOrUniformName = ModelUtility.getDiffuseAttributeOrUniform(
- gltf,
- programId
- );
- fs = batchTable.getFragmentShaderCallback(
- true,
- diffuseAttributeOrUniformName
- )(fs);
- } else {
- fs = "uniform vec4 czm_pickColor;\n" + fs;
- }
- return fs;
- };
- }
- function getUniformMapNonInstancedCallback(collection) {
- return function (uniformMap) {
- if (defined(collection._batchTable)) {
- uniformMap = collection._batchTable.getUniformMapCallback()(uniformMap);
- }
- return uniformMap;
- };
- }
- function getVertexBufferTypedArray(collection) {
- var instances = collection._instances;
- var instancesLength = collection.length;
- var collectionCenter = collection._center;
- var vertexSizeInFloats = 12;
- var bufferData = collection._vertexBufferTypedArray;
- if (!defined(bufferData)) {
- bufferData = new Float32Array(instancesLength * vertexSizeInFloats);
- }
- if (collection._dynamic) {
- // Hold onto the buffer data so we don't have to allocate new memory every frame.
- collection._vertexBufferTypedArray = bufferData;
- }
- for (var i = 0; i < instancesLength; ++i) {
- var modelMatrix = instances[i]._modelMatrix;
- // Instance matrix is relative to center
- var instanceMatrix = Matrix4.clone(modelMatrix, scratchMatrix);
- instanceMatrix[12] -= collectionCenter.x;
- instanceMatrix[13] -= collectionCenter.y;
- instanceMatrix[14] -= collectionCenter.z;
- var offset = i * vertexSizeInFloats;
- // First three rows of the model matrix
- bufferData[offset + 0] = instanceMatrix[0];
- bufferData[offset + 1] = instanceMatrix[4];
- bufferData[offset + 2] = instanceMatrix[8];
- bufferData[offset + 3] = instanceMatrix[12];
- bufferData[offset + 4] = instanceMatrix[1];
- bufferData[offset + 5] = instanceMatrix[5];
- bufferData[offset + 6] = instanceMatrix[9];
- bufferData[offset + 7] = instanceMatrix[13];
- bufferData[offset + 8] = instanceMatrix[2];
- bufferData[offset + 9] = instanceMatrix[6];
- bufferData[offset + 10] = instanceMatrix[10];
- bufferData[offset + 11] = instanceMatrix[14];
- }
- return bufferData;
- }
- function createVertexBuffer(collection, context) {
- var i;
- var instances = collection._instances;
- var instancesLength = collection.length;
- var dynamic = collection._dynamic;
- var usesBatchTable = defined(collection._batchTable);
- if (usesBatchTable) {
- var batchIdBufferData = new Uint16Array(instancesLength);
- for (i = 0; i < instancesLength; ++i) {
- batchIdBufferData[i] = instances[i]._instanceId;
- }
- collection._batchIdBuffer = Buffer.createVertexBuffer({
- context: context,
- typedArray: batchIdBufferData,
- usage: BufferUsage.STATIC_DRAW,
- });
- }
- if (!usesBatchTable) {
- var pickIdBuffer = new Uint8Array(instancesLength * 4);
- for (i = 0; i < instancesLength; ++i) {
- var pickId = collection._pickIds[i];
- var pickColor = pickId.color;
- var offset = i * 4;
- pickIdBuffer[offset] = Color.floatToByte(pickColor.red);
- pickIdBuffer[offset + 1] = Color.floatToByte(pickColor.green);
- pickIdBuffer[offset + 2] = Color.floatToByte(pickColor.blue);
- pickIdBuffer[offset + 3] = Color.floatToByte(pickColor.alpha);
- }
- collection._pickIdBuffer = Buffer.createVertexBuffer({
- context: context,
- typedArray: pickIdBuffer,
- usage: BufferUsage.STATIC_DRAW,
- });
- }
- var vertexBufferTypedArray = getVertexBufferTypedArray(collection);
- collection._vertexBuffer = Buffer.createVertexBuffer({
- context: context,
- typedArray: vertexBufferTypedArray,
- usage: dynamic ? BufferUsage.STREAM_DRAW : BufferUsage.STATIC_DRAW,
- });
- }
- function updateVertexBuffer(collection) {
- var vertexBufferTypedArray = getVertexBufferTypedArray(collection);
- collection._vertexBuffer.copyFromArrayView(vertexBufferTypedArray);
- }
- function createPickIds(collection, context) {
- // PERFORMANCE_IDEA: we could skip the pick buffer completely by allocating
- // a continuous range of pickIds and then converting the base pickId + batchId
- // to RGBA in the shader. The only consider is precision issues, which might
- // not be an issue in WebGL 2.
- var instances = collection._instances;
- var instancesLength = instances.length;
- var pickIds = new Array(instancesLength);
- for (var i = 0; i < instancesLength; ++i) {
- pickIds[i] = context.createPickId(instances[i]);
- }
- return pickIds;
- }
- function createModel(collection, context) {
- var instancingSupported = collection._instancingSupported;
- var usesBatchTable = defined(collection._batchTable);
- var allowPicking = collection._allowPicking;
- var modelOptions = {
- url: collection._url,
- requestType: collection._requestType,
- gltf: collection._gltf,
- basePath: collection._basePath,
- shadows: collection._shadows,
- cacheKey: undefined,
- asynchronous: collection._asynchronous,
- allowPicking: allowPicking,
- incrementallyLoadTextures: collection._incrementallyLoadTextures,
- upAxis: collection._upAxis,
- forwardAxis: collection._forwardAxis,
- precreatedAttributes: undefined,
- vertexShaderLoaded: undefined,
- fragmentShaderLoaded: undefined,
- uniformMapLoaded: undefined,
- pickIdLoaded: collection._pickIdLoaded,
- ignoreCommands: true,
- opaquePass: collection._opaquePass,
- imageBasedLightingFactor: collection.imageBasedLightingFactor,
- lightColor: collection.lightColor,
- luminanceAtZenith: collection.luminanceAtZenith,
- sphericalHarmonicCoefficients: collection.sphericalHarmonicCoefficients,
- specularEnvironmentMaps: collection.specularEnvironmentMaps,
- };
- if (!usesBatchTable) {
- collection._pickIds = createPickIds(collection, context);
- }
- if (instancingSupported) {
- createVertexBuffer(collection, context);
- var vertexSizeInFloats = 12;
- var componentSizeInBytes = ComponentDatatype.getSizeInBytes(
- ComponentDatatype.FLOAT
- );
- var instancedAttributes = {
- czm_modelMatrixRow0: {
- index: 0, // updated in Model
- vertexBuffer: collection._vertexBuffer,
- componentsPerAttribute: 4,
- componentDatatype: ComponentDatatype.FLOAT,
- normalize: false,
- offsetInBytes: 0,
- strideInBytes: componentSizeInBytes * vertexSizeInFloats,
- instanceDivisor: 1,
- },
- czm_modelMatrixRow1: {
- index: 0, // updated in Model
- vertexBuffer: collection._vertexBuffer,
- componentsPerAttribute: 4,
- componentDatatype: ComponentDatatype.FLOAT,
- normalize: false,
- offsetInBytes: componentSizeInBytes * 4,
- strideInBytes: componentSizeInBytes * vertexSizeInFloats,
- instanceDivisor: 1,
- },
- czm_modelMatrixRow2: {
- index: 0, // updated in Model
- vertexBuffer: collection._vertexBuffer,
- componentsPerAttribute: 4,
- componentDatatype: ComponentDatatype.FLOAT,
- normalize: false,
- offsetInBytes: componentSizeInBytes * 8,
- strideInBytes: componentSizeInBytes * vertexSizeInFloats,
- instanceDivisor: 1,
- },
- };
- // When using a batch table, add a batch id attribute
- if (usesBatchTable) {
- instancedAttributes.a_batchId = {
- index: 0, // updated in Model
- vertexBuffer: collection._batchIdBuffer,
- componentsPerAttribute: 1,
- componentDatatype: ComponentDatatype.UNSIGNED_SHORT,
- normalize: false,
- offsetInBytes: 0,
- strideInBytes: 0,
- instanceDivisor: 1,
- };
- }
- if (!usesBatchTable) {
- instancedAttributes.pickColor = {
- index: 0, // updated in Model
- vertexBuffer: collection._pickIdBuffer,
- componentsPerAttribute: 4,
- componentDatatype: ComponentDatatype.UNSIGNED_BYTE,
- normalize: true,
- offsetInBytes: 0,
- strideInBytes: 0,
- instanceDivisor: 1,
- };
- }
- modelOptions.precreatedAttributes = instancedAttributes;
- modelOptions.vertexShaderLoaded = getVertexShaderCallback(collection);
- modelOptions.fragmentShaderLoaded = getFragmentShaderCallback(collection);
- modelOptions.uniformMapLoaded = getUniformMapCallback(collection, context);
- if (defined(collection._url)) {
- modelOptions.cacheKey = collection._url.getUrlComponent() + "#instanced";
- }
- } else {
- modelOptions.vertexShaderLoaded = getVertexShaderNonInstancedCallback(
- collection
- );
- modelOptions.fragmentShaderLoaded = getFragmentShaderNonInstancedCallback(
- collection
- );
- modelOptions.uniformMapLoaded = getUniformMapNonInstancedCallback(
- collection,
- context
- );
- }
- if (defined(collection._url)) {
- collection._model = Model.fromGltf(modelOptions);
- } else {
- collection._model = new Model(modelOptions);
- }
- }
- function updateWireframe(collection, force) {
- if (collection._debugWireframe !== collection.debugWireframe || force) {
- collection._debugWireframe = collection.debugWireframe;
- // This assumes the original primitive was TRIANGLES and that the triangles
- // are connected for the wireframe to look perfect.
- var primitiveType = collection.debugWireframe
- ? PrimitiveType.LINES
- : PrimitiveType.TRIANGLES;
- var commands = collection._drawCommands;
- var length = commands.length;
- for (var i = 0; i < length; ++i) {
- commands[i].primitiveType = primitiveType;
- }
- }
- }
- function getDisableCullingRenderState(renderState) {
- var rs = clone(renderState, true);
- rs.cull.enabled = false;
- return RenderState.fromCache(rs);
- }
- function updateBackFaceCulling(collection, force) {
- if (collection._backFaceCulling !== collection.backFaceCulling || force) {
- collection._backFaceCulling = collection.backFaceCulling;
- var commands = collection._drawCommands;
- var length = commands.length;
- var i;
- if (!defined(collection._disableCullingRenderStates)) {
- collection._disableCullingRenderStates = new Array(length);
- collection._renderStates = new Array(length);
- for (i = 0; i < length; ++i) {
- var renderState = commands[i].renderState;
- var derivedRenderState = getDisableCullingRenderState(renderState);
- collection._disableCullingRenderStates[i] = derivedRenderState;
- collection._renderStates[i] = renderState;
- }
- }
- for (i = 0; i < length; ++i) {
- commands[i].renderState = collection._backFaceCulling
- ? collection._renderStates[i]
- : collection._disableCullingRenderStates[i];
- }
- }
- }
- function updateShowBoundingVolume(collection, force) {
- if (
- collection.debugShowBoundingVolume !==
- collection._debugShowBoundingVolume ||
- force
- ) {
- collection._debugShowBoundingVolume = collection.debugShowBoundingVolume;
- var commands = collection._drawCommands;
- var length = commands.length;
- for (var i = 0; i < length; ++i) {
- commands[i].debugShowBoundingVolume = collection.debugShowBoundingVolume;
- }
- }
- }
- function createCommands(collection, drawCommands) {
- var commandsLength = drawCommands.length;
- var instancesLength = collection.length;
- var boundingSphere = collection._boundingSphere;
- var cull = collection._cull;
- for (var i = 0; i < commandsLength; ++i) {
- var drawCommand = DrawCommand.shallowClone(drawCommands[i]);
- drawCommand.instanceCount = instancesLength;
- drawCommand.boundingVolume = boundingSphere;
- drawCommand.cull = cull;
- if (defined(collection._batchTable)) {
- drawCommand.pickId = collection._batchTable.getPickId();
- } else {
- drawCommand.pickId = "v_pickColor";
- }
- collection._drawCommands.push(drawCommand);
- }
- }
- function createBatchIdFunction(batchId) {
- return function () {
- return batchId;
- };
- }
- function createPickColorFunction(color) {
- return function () {
- return color;
- };
- }
- function createCommandsNonInstanced(collection, drawCommands) {
- // When instancing is disabled, create commands for every instance.
- var instances = collection._instances;
- var commandsLength = drawCommands.length;
- var instancesLength = collection.length;
- var batchTable = collection._batchTable;
- var usesBatchTable = defined(batchTable);
- var cull = collection._cull;
- for (var i = 0; i < commandsLength; ++i) {
- for (var j = 0; j < instancesLength; ++j) {
- var drawCommand = DrawCommand.shallowClone(drawCommands[i]);
- drawCommand.modelMatrix = new Matrix4(); // Updated in updateCommandsNonInstanced
- drawCommand.boundingVolume = new BoundingSphere(); // Updated in updateCommandsNonInstanced
- drawCommand.cull = cull;
- drawCommand.uniformMap = clone(drawCommand.uniformMap);
- if (usesBatchTable) {
- drawCommand.uniformMap.a_batchId = createBatchIdFunction(
- instances[j]._instanceId
- );
- } else {
- var pickId = collection._pickIds[j];
- drawCommand.uniformMap.czm_pickColor = createPickColorFunction(
- pickId.color
- );
- }
- collection._drawCommands.push(drawCommand);
- }
- }
- }
- function updateCommandsNonInstanced(collection) {
- var modelCommands = collection._modelCommands;
- var commandsLength = modelCommands.length;
- var instancesLength = collection.length;
- var collectionTransform = collection._rtcTransform;
- var collectionCenter = collection._center;
- for (var i = 0; i < commandsLength; ++i) {
- var modelCommand = modelCommands[i];
- for (var j = 0; j < instancesLength; ++j) {
- var commandIndex = i * instancesLength + j;
- var drawCommand = collection._drawCommands[commandIndex];
- var instanceMatrix = Matrix4.clone(
- collection._instances[j]._modelMatrix,
- scratchMatrix
- );
- instanceMatrix[12] -= collectionCenter.x;
- instanceMatrix[13] -= collectionCenter.y;
- instanceMatrix[14] -= collectionCenter.z;
- instanceMatrix = Matrix4.multiply(
- collectionTransform,
- instanceMatrix,
- scratchMatrix
- );
- var nodeMatrix = modelCommand.modelMatrix;
- var modelMatrix = drawCommand.modelMatrix;
- Matrix4.multiply(instanceMatrix, nodeMatrix, modelMatrix);
- var nodeBoundingSphere = modelCommand.boundingVolume;
- var boundingSphere = drawCommand.boundingVolume;
- BoundingSphere.transform(
- nodeBoundingSphere,
- instanceMatrix,
- boundingSphere
- );
- }
- }
- }
- function getModelCommands(model) {
- var nodeCommands = model._nodeCommands;
- var length = nodeCommands.length;
- var drawCommands = [];
- for (var i = 0; i < length; ++i) {
- var nc = nodeCommands[i];
- if (nc.show) {
- drawCommands.push(nc.command);
- }
- }
- return drawCommands;
- }
- function commandsDirty(model) {
- var nodeCommands = model._nodeCommands;
- var length = nodeCommands.length;
- var commandsDirty = false;
- for (var i = 0; i < length; i++) {
- var nc = nodeCommands[i];
- if (nc.command.dirty) {
- nc.command.dirty = false;
- commandsDirty = true;
- }
- }
- return commandsDirty;
- }
- function generateModelCommands(modelInstanceCollection, instancingSupported) {
- modelInstanceCollection._drawCommands = [];
- var modelCommands = getModelCommands(modelInstanceCollection._model);
- if (instancingSupported) {
- createCommands(modelInstanceCollection, modelCommands);
- } else {
- createCommandsNonInstanced(modelInstanceCollection, modelCommands);
- updateCommandsNonInstanced(modelInstanceCollection);
- }
- }
- function updateShadows(collection, force) {
- if (collection.shadows !== collection._shadows || force) {
- collection._shadows = collection.shadows;
- var castShadows = ShadowMode.castShadows(collection.shadows);
- var receiveShadows = ShadowMode.receiveShadows(collection.shadows);
- var drawCommands = collection._drawCommands;
- var length = drawCommands.length;
- for (var i = 0; i < length; ++i) {
- var drawCommand = drawCommands[i];
- drawCommand.castShadows = castShadows;
- drawCommand.receiveShadows = receiveShadows;
- }
- }
- }
- ModelInstanceCollection.prototype.update = function (frameState) {
- if (frameState.mode === SceneMode.MORPHING) {
- return;
- }
- if (!this.show) {
- return;
- }
- if (this.length === 0) {
- return;
- }
- var context = frameState.context;
- if (this._state === LoadState.NEEDS_LOAD) {
- this._state = LoadState.LOADING;
- this._instancingSupported = context.instancedArrays;
- createModel(this, context);
- var that = this;
- this._model.readyPromise.otherwise(function (error) {
- that._state = LoadState.FAILED;
- that._readyPromise.reject(error);
- });
- }
- var instancingSupported = this._instancingSupported;
- var model = this._model;
- model.imageBasedLightingFactor = this.imageBasedLightingFactor;
- model.lightColor = this.lightColor;
- model.luminanceAtZenith = this.luminanceAtZenith;
- model.sphericalHarmonicCoefficients = this.sphericalHarmonicCoefficients;
- model.specularEnvironmentMaps = this.specularEnvironmentMaps;
- model.update(frameState);
- if (model.ready && this._state === LoadState.LOADING) {
- this._state = LoadState.LOADED;
- this._ready = true;
- // Expand bounding volume to fit the radius of the loaded model including the model's offset from the center
- var modelRadius =
- model.boundingSphere.radius +
- Cartesian3.magnitude(model.boundingSphere.center);
- this._boundingSphere.radius += modelRadius;
- this._modelCommands = getModelCommands(model);
- generateModelCommands(this, instancingSupported);
- this._readyPromise.resolve(this);
- return;
- }
- if (this._state !== LoadState.LOADED) {
- return;
- }
- var modeChanged = frameState.mode !== this._mode;
- var modelMatrix = this.modelMatrix;
- var modelMatrixChanged = !Matrix4.equals(this._modelMatrix, modelMatrix);
- if (modeChanged || modelMatrixChanged) {
- this._mode = frameState.mode;
- Matrix4.clone(modelMatrix, this._modelMatrix);
- var rtcTransform = Matrix4.multiplyByTranslation(
- this._modelMatrix,
- this._center,
- this._rtcTransform
- );
- if (this._mode !== SceneMode.SCENE3D) {
- rtcTransform = Transforms.basisTo2D(
- frameState.mapProjection,
- rtcTransform,
- rtcTransform
- );
- }
- Matrix4.getTranslation(rtcTransform, this._boundingSphere.center);
- }
- if (instancingSupported && this._dirty) {
- // If at least one instance has moved assume the collection is now dynamic
- this._dynamic = true;
- this._dirty = false;
- // PERFORMANCE_IDEA: only update dirty sub-sections instead of the whole collection
- updateVertexBuffer(this);
- }
- // If the model was set to rebuild shaders during update, rebuild instanced commands.
- var modelCommandsDirty = commandsDirty(model);
- if (modelCommandsDirty) {
- generateModelCommands(this, instancingSupported);
- }
- // If any node changes due to an animation, update the commands. This could be inefficient if the model is
- // composed of many nodes and only one changes, however it is probably fine in the general use case.
- // Only applies when instancing is disabled. The instanced shader automatically handles node transformations.
- if (
- !instancingSupported &&
- (model.dirty || this._dirty || modeChanged || modelMatrixChanged)
- ) {
- updateCommandsNonInstanced(this);
- }
- updateShadows(this, modelCommandsDirty);
- updateWireframe(this, modelCommandsDirty);
- updateBackFaceCulling(this, modelCommandsDirty);
- updateShowBoundingVolume(this, modelCommandsDirty);
- var passes = frameState.passes;
- if (!passes.render && !passes.pick) {
- return;
- }
- var commandList = frameState.commandList;
- var commands = this._drawCommands;
- var commandsLength = commands.length;
- for (var i = 0; i < commandsLength; ++i) {
- commandList.push(commands[i]);
- }
- };
- ModelInstanceCollection.prototype.isDestroyed = function () {
- return false;
- };
- ModelInstanceCollection.prototype.destroy = function () {
- this._model = this._model && this._model.destroy();
- var pickIds = this._pickIds;
- if (defined(pickIds)) {
- var length = pickIds.length;
- for (var i = 0; i < length; ++i) {
- pickIds[i].destroy();
- }
- }
- return destroyObject(this);
- };
- export default ModelInstanceCollection;
|