| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 | import BoundingRectangle from "../Core/BoundingRectangle.js";import Cartesian3 from "../Core/Cartesian3.js";import CullingVolume from "../Core/CullingVolume.js";import defined from "../Core/defined.js";import getTimestamp from "../Core/getTimestamp.js";import Interval from "../Core/Interval.js";import CesiumMath from "../Core/Math.js";import Matrix4 from "../Core/Matrix4.js";import ClearCommand from "../Renderer/ClearCommand.js";import Pass from "../Renderer/Pass.js";import PassState from "../Renderer/PassState.js";import Camera from "./Camera.js";import FrustumCommands from "./FrustumCommands.js";import GlobeDepth from "./GlobeDepth.js";import GlobeTranslucencyFramebuffer from "./GlobeTranslucencyFramebuffer.js";import OIT from "./OIT.js";import PickDepthFramebuffer from "./PickDepthFramebuffer.js";import PickFramebuffer from "./PickFramebuffer.js";import SceneFramebuffer from "./SceneFramebuffer.js";import SceneMode from "./SceneMode.js";import ShadowMap from "./ShadowMap.js";import TranslucentTileClassification from "./TranslucentTileClassification.js";function CommandExtent() {  this.command = undefined;  this.near = undefined;  this.far = undefined;}/** * @private */function View(scene, camera, viewport) {  const context = scene.context;  let globeDepth;  if (context.depthTexture) {    globeDepth = new GlobeDepth();  }  let oit;  if (scene._useOIT && context.depthTexture) {    oit = new OIT(context);  }  const passState = new PassState(context);  passState.viewport = BoundingRectangle.clone(viewport);  this.camera = camera;  this._cameraClone = Camera.clone(camera);  this._cameraStartFired = false;  this._cameraMovedTime = undefined;  this.viewport = viewport;  this.passState = passState;  this.pickFramebuffer = new PickFramebuffer(context);  this.pickDepthFramebuffer = new PickDepthFramebuffer();  this.sceneFramebuffer = new SceneFramebuffer();  this.globeDepth = globeDepth;  this.globeTranslucencyFramebuffer = new GlobeTranslucencyFramebuffer();  this.oit = oit;  this.translucentTileClassification = new TranslucentTileClassification(    context  );  this.pickDepths = [];  this.frustumCommandsList = [];  this.debugFrustumStatistics = undefined;  // Array of all commands that get rendered into frustums along with their near / far values.  // Acts similar to a ManagedArray.  this._commandExtents = [];}const scratchPosition0 = new Cartesian3();const scratchPosition1 = new Cartesian3();function maxComponent(a, b) {  const x = Math.max(Math.abs(a.x), Math.abs(b.x));  const y = Math.max(Math.abs(a.y), Math.abs(b.y));  const z = Math.max(Math.abs(a.z), Math.abs(b.z));  return Math.max(Math.max(x, y), z);}function cameraEqual(camera0, camera1, epsilon) {  const scalar =    1 / Math.max(1, maxComponent(camera0.position, camera1.position));  Cartesian3.multiplyByScalar(camera0.position, scalar, scratchPosition0);  Cartesian3.multiplyByScalar(camera1.position, scalar, scratchPosition1);  return (    Cartesian3.equalsEpsilon(scratchPosition0, scratchPosition1, epsilon) &&    Cartesian3.equalsEpsilon(camera0.direction, camera1.direction, epsilon) &&    Cartesian3.equalsEpsilon(camera0.up, camera1.up, epsilon) &&    Cartesian3.equalsEpsilon(camera0.right, camera1.right, epsilon) &&    Matrix4.equalsEpsilon(camera0.transform, camera1.transform, epsilon) &&    camera0.frustum.equalsEpsilon(camera1.frustum, epsilon)  );}View.prototype.checkForCameraUpdates = function (scene) {  const camera = this.camera;  const cameraClone = this._cameraClone;  if (!cameraEqual(camera, cameraClone, CesiumMath.EPSILON15)) {    if (!this._cameraStartFired) {      camera.moveStart.raiseEvent();      this._cameraStartFired = true;    }    this._cameraMovedTime = getTimestamp();    Camera.clone(camera, cameraClone);    return true;  }  if (    this._cameraStartFired &&    getTimestamp() - this._cameraMovedTime > scene.cameraEventWaitTime  ) {    camera.moveEnd.raiseEvent();    this._cameraStartFired = false;  }  return false;};function updateFrustums(view, scene, near, far) {  const frameState = scene.frameState;  const camera = frameState.camera;  const farToNearRatio = frameState.useLogDepth    ? scene.logarithmicDepthFarToNearRatio    : scene.farToNearRatio;  const is2D = scene.mode === SceneMode.SCENE2D;  const nearToFarDistance2D = scene.nearToFarDistance2D;  // Extend the far plane slightly further to prevent geometry clipping against the far plane.  far *= 1.0 + CesiumMath.EPSILON2;  // The computed near plane must be between the user defined near and far planes.  // The computed far plane must between the user defined far and computed near.  // This will handle the case where the computed near plane is further than the user defined far plane.  near = Math.min(Math.max(near, camera.frustum.near), camera.frustum.far);  far = Math.max(Math.min(far, camera.frustum.far), near);  let numFrustums;  if (is2D) {    // The multifrustum for 2D is uniformly distributed. To avoid z-fighting in 2D,    // the camera is moved to just before the frustum and the frustum depth is scaled    // to be in [1.0, nearToFarDistance2D].    far = Math.min(far, camera.position.z + scene.nearToFarDistance2D);    near = Math.min(near, far);    numFrustums = Math.ceil(      Math.max(1.0, far - near) / scene.nearToFarDistance2D    );  } else {    // The multifrustum for 3D/CV is non-uniformly distributed.    numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio));  }  const frustumCommandsList = view.frustumCommandsList;  frustumCommandsList.length = numFrustums;  for (let m = 0; m < numFrustums; ++m) {    let curNear;    let curFar;    if (is2D) {      curNear = Math.min(        far - nearToFarDistance2D,        near + m * nearToFarDistance2D      );      curFar = Math.min(far, curNear + nearToFarDistance2D);    } else {      curNear = Math.max(near, Math.pow(farToNearRatio, m) * near);      curFar = Math.min(far, farToNearRatio * curNear);    }    let frustumCommands = frustumCommandsList[m];    if (!defined(frustumCommands)) {      frustumCommands = frustumCommandsList[m] = new FrustumCommands(        curNear,        curFar      );    } else {      frustumCommands.near = curNear;      frustumCommands.far = curFar;    }  }}function insertIntoBin(view, scene, command, commandNear, commandFar) {  if (scene.debugShowFrustums) {    command.debugOverlappingFrustums = 0;  }  const frustumCommandsList = view.frustumCommandsList;  const length = frustumCommandsList.length;  for (let i = 0; i < length; ++i) {    const frustumCommands = frustumCommandsList[i];    const curNear = frustumCommands.near;    const curFar = frustumCommands.far;    if (commandNear > curFar) {      continue;    }    if (commandFar < curNear) {      break;    }    const pass = command.pass;    const index = frustumCommands.indices[pass]++;    frustumCommands.commands[pass][index] = command;    if (scene.debugShowFrustums) {      command.debugOverlappingFrustums |= 1 << i;    }    if (command.executeInClosestFrustum) {      break;    }  }  if (scene.debugShowFrustums) {    const cf = view.debugFrustumStatistics.commandsInFrustums;    cf[command.debugOverlappingFrustums] = defined(      cf[command.debugOverlappingFrustums]    )      ? cf[command.debugOverlappingFrustums] + 1      : 1;    ++view.debugFrustumStatistics.totalCommands;  }  scene.updateDerivedCommands(command);}const scratchCullingVolume = new CullingVolume();const scratchNearFarInterval = new Interval();View.prototype.createPotentiallyVisibleSet = function (scene) {  const frameState = scene.frameState;  const camera = frameState.camera;  const direction = camera.directionWC;  const position = camera.positionWC;  const computeList = scene._computeCommandList;  const overlayList = scene._overlayCommandList;  const commandList = frameState.commandList;  if (scene.debugShowFrustums) {    this.debugFrustumStatistics = {      totalCommands: 0,      commandsInFrustums: {},    };  }  const frustumCommandsList = this.frustumCommandsList;  const numberOfFrustums = frustumCommandsList.length;  const numberOfPasses = Pass.NUMBER_OF_PASSES;  for (let n = 0; n < numberOfFrustums; ++n) {    for (let p = 0; p < numberOfPasses; ++p) {      frustumCommandsList[n].indices[p] = 0;    }  }  computeList.length = 0;  overlayList.length = 0;  const commandExtents = this._commandExtents;  const commandExtentCapacity = commandExtents.length;  let commandExtentCount = 0;  let near = +Number.MAX_VALUE;  let far = -Number.MAX_VALUE;  const shadowsEnabled = frameState.shadowState.shadowsEnabled;  let shadowNear = +Number.MAX_VALUE;  let shadowFar = -Number.MAX_VALUE;  let shadowClosestObjectSize = Number.MAX_VALUE;  const occluder =    frameState.mode === SceneMode.SCENE3D ? frameState.occluder : undefined;  let cullingVolume = frameState.cullingVolume;  // get user culling volume minus the far plane.  const planes = scratchCullingVolume.planes;  for (let k = 0; k < 5; ++k) {    planes[k] = cullingVolume.planes[k];  }  cullingVolume = scratchCullingVolume;  const length = commandList.length;  for (let i = 0; i < length; ++i) {    const command = commandList[i];    const pass = command.pass;    if (pass === Pass.COMPUTE) {      computeList.push(command);    } else if (pass === Pass.OVERLAY) {      overlayList.push(command);    } else {      let commandNear;      let commandFar;      const boundingVolume = command.boundingVolume;      if (defined(boundingVolume)) {        if (!scene.isVisible(command, cullingVolume, occluder)) {          continue;        }        const nearFarInterval = boundingVolume.computePlaneDistances(          position,          direction,          scratchNearFarInterval        );        commandNear = nearFarInterval.start;        commandFar = nearFarInterval.stop;        near = Math.min(near, commandNear);        far = Math.max(far, commandFar);        // Compute a tight near and far plane for commands that receive shadows. This helps compute        // good splits for cascaded shadow maps. Ignore commands that exceed the maximum distance.        // When moving the camera low LOD globe tiles begin to load, whose bounding volumes        // throw off the near/far fitting for the shadow map. Only update for globe tiles that the        // camera isn't inside.        if (          shadowsEnabled &&          command.receiveShadows &&          commandNear < ShadowMap.MAXIMUM_DISTANCE &&          !(pass === Pass.GLOBE && commandNear < -100.0 && commandFar > 100.0)        ) {          // Get the smallest bounding volume the camera is near. This is used to place more shadow detail near the object.          const size = commandFar - commandNear;          if (pass !== Pass.GLOBE && commandNear < 100.0) {            shadowClosestObjectSize = Math.min(shadowClosestObjectSize, size);          }          shadowNear = Math.min(shadowNear, commandNear);          shadowFar = Math.max(shadowFar, commandFar);        }      } else if (command instanceof ClearCommand) {        // Clear commands don't need a bounding volume - just add the clear to all frustums.        commandNear = camera.frustum.near;        commandFar = camera.frustum.far;      } else {        // If command has no bounding volume we need to use the camera's        // worst-case near and far planes to avoid clipping something important.        commandNear = camera.frustum.near;        commandFar = camera.frustum.far;        near = Math.min(near, commandNear);        far = Math.max(far, commandFar);      }      let extent = commandExtents[commandExtentCount];      if (!defined(extent)) {        extent = commandExtents[commandExtentCount] = new CommandExtent();      }      extent.command = command;      extent.near = commandNear;      extent.far = commandFar;      commandExtentCount++;    }  }  if (shadowsEnabled) {    shadowNear = Math.min(      Math.max(shadowNear, camera.frustum.near),      camera.frustum.far    );    shadowFar = Math.max(Math.min(shadowFar, camera.frustum.far), shadowNear);  }  // Use the computed near and far for shadows  if (shadowsEnabled) {    frameState.shadowState.nearPlane = shadowNear;    frameState.shadowState.farPlane = shadowFar;    frameState.shadowState.closestObjectSize = shadowClosestObjectSize;  }  updateFrustums(this, scene, near, far);  let c;  let ce;  for (c = 0; c < commandExtentCount; c++) {    ce = commandExtents[c];    insertIntoBin(this, scene, ce.command, ce.near, ce.far);  }  // Dereference old commands  if (commandExtentCount < commandExtentCapacity) {    for (c = commandExtentCount; c < commandExtentCapacity; c++) {      ce = commandExtents[c];      if (!defined(ce.command)) {        // If the command is undefined, it's assumed that all        // subsequent commmands were set to undefined as well,        // so no need to loop over them all        break;      }      ce.command = undefined;    }  }  const numFrustums = frustumCommandsList.length;  const frustumSplits = frameState.frustumSplits;  frustumSplits.length = numFrustums + 1;  for (let j = 0; j < numFrustums; ++j) {    frustumSplits[j] = frustumCommandsList[j].near;    if (j === numFrustums - 1) {      frustumSplits[j + 1] = frustumCommandsList[j].far;    }  }};View.prototype.destroy = function () {  this.pickFramebuffer = this.pickFramebuffer && this.pickFramebuffer.destroy();  this.pickDepthFramebuffer =    this.pickDepthFramebuffer && this.pickDepthFramebuffer.destroy();  this.sceneFramebuffer =    this.sceneFramebuffer && this.sceneFramebuffer.destroy();  this.globeDepth = this.globeDepth && this.globeDepth.destroy();  this.oit = this.oit && this.oit.destroy();  this.translucentTileClassification =    this.translucentTileClassification &&    this.translucentTileClassification.destroy();  this.globeTranslucencyFramebuffer =    this.globeTranslucencyFramebuffer &&    this.globeTranslucencyFramebuffer.destroy();  let i;  const pickDepths = this.pickDepths;  const length = pickDepths.length;  for (i = 0; i < length; ++i) {    pickDepths[i].destroy();  }};export default View;
 |