智慧水务管理系统 - 精河县供水工程综合管理平台

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. import BoundingRectangle from "../Core/BoundingRectangle.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import CullingVolume from "../Core/CullingVolume.js";
  4. import defined from "../Core/defined.js";
  5. import getTimestamp from "../Core/getTimestamp.js";
  6. import Interval from "../Core/Interval.js";
  7. import CesiumMath from "../Core/Math.js";
  8. import Matrix4 from "../Core/Matrix4.js";
  9. import ClearCommand from "../Renderer/ClearCommand.js";
  10. import Pass from "../Renderer/Pass.js";
  11. import PassState from "../Renderer/PassState.js";
  12. import Camera from "./Camera.js";
  13. import EdgeFramebuffer from "./EdgeFramebuffer.js";
  14. import FrustumCommands from "./FrustumCommands.js";
  15. import GlobeDepth from "./GlobeDepth.js";
  16. import GlobeTranslucencyFramebuffer from "./GlobeTranslucencyFramebuffer.js";
  17. import OIT from "./OIT.js";
  18. import PickDepthFramebuffer from "./PickDepthFramebuffer.js";
  19. import PickFramebuffer from "./PickFramebuffer.js";
  20. import SceneFramebuffer from "./SceneFramebuffer.js";
  21. import SceneMode from "./SceneMode.js";
  22. import ShadowMap from "./ShadowMap.js";
  23. import TranslucentTileClassification from "./TranslucentTileClassification.js";
  24. function CommandExtent() {
  25. this.command = undefined;
  26. this.near = undefined;
  27. this.far = undefined;
  28. }
  29. /**
  30. * @alias View
  31. * @constructor
  32. *
  33. * @param {Scene} scene
  34. * @param {Camera} camera
  35. * @param {BoundingRectangle} viewport
  36. *
  37. * @private
  38. */
  39. function View(scene, camera, viewport) {
  40. const context = scene.context;
  41. let globeDepth;
  42. if (context.depthTexture) {
  43. globeDepth = new GlobeDepth();
  44. }
  45. let oit;
  46. if (scene._useOIT && context.depthTexture) {
  47. oit = new OIT(context);
  48. }
  49. const passState = new PassState(context);
  50. passState.viewport = BoundingRectangle.clone(viewport);
  51. this.camera = camera;
  52. this._cameraClone = Camera.clone(camera);
  53. this._cameraStartFired = false;
  54. this._cameraMovedTime = undefined;
  55. this.viewport = viewport;
  56. this.passState = passState;
  57. this.pickFramebuffer = new PickFramebuffer(context);
  58. this.pickDepthFramebuffer = new PickDepthFramebuffer();
  59. this.sceneFramebuffer = new SceneFramebuffer();
  60. this.edgeFramebuffer = new EdgeFramebuffer();
  61. this.globeDepth = globeDepth;
  62. this.globeTranslucencyFramebuffer = new GlobeTranslucencyFramebuffer();
  63. this.oit = oit;
  64. this.translucentTileClassification = new TranslucentTileClassification(
  65. context,
  66. );
  67. /**
  68. * @type {PickDepth[]}
  69. */
  70. this.pickDepths = [];
  71. this.frustumCommandsList = [];
  72. this.debugFrustumStatistics = undefined;
  73. // Array of all commands that get rendered into frustums along with their near / far values.
  74. // Acts similar to a ManagedArray.
  75. this._commandExtents = [];
  76. }
  77. const scratchPosition0 = new Cartesian3();
  78. const scratchPosition1 = new Cartesian3();
  79. /**
  80. * Check if two cameras have the same view.
  81. *
  82. * @param {Camera} camera0 The first camera for comparison.
  83. * @param {Camera} camera1 The second camera for comparison.
  84. * @param {number} epsilon The epsilon tolerance to use for equality testing.
  85. * @returns {boolean} <code>true</code> if the cameras are equal.
  86. *
  87. * @private
  88. */
  89. function cameraEqual(camera0, camera1, epsilon) {
  90. const maximumPositionComponent = Math.max(
  91. Cartesian3.maximumComponent(
  92. Cartesian3.abs(camera0.position, scratchPosition0),
  93. ),
  94. Cartesian3.maximumComponent(
  95. Cartesian3.abs(camera1.position, scratchPosition1),
  96. ),
  97. );
  98. const scalar = 1 / Math.max(1, maximumPositionComponent);
  99. Cartesian3.multiplyByScalar(camera0.position, scalar, scratchPosition0);
  100. Cartesian3.multiplyByScalar(camera1.position, scalar, scratchPosition1);
  101. return (
  102. Cartesian3.equalsEpsilon(scratchPosition0, scratchPosition1, epsilon) &&
  103. Cartesian3.equalsEpsilon(camera0.direction, camera1.direction, epsilon) &&
  104. Cartesian3.equalsEpsilon(camera0.up, camera1.up, epsilon) &&
  105. Cartesian3.equalsEpsilon(camera0.right, camera1.right, epsilon) &&
  106. Matrix4.equalsEpsilon(camera0.transform, camera1.transform, epsilon) &&
  107. camera0.frustum.equalsEpsilon(camera1.frustum, epsilon)
  108. );
  109. }
  110. /**
  111. * Check if the camera position or direction has changed.
  112. *
  113. * @param {Scene} scene
  114. * @returns {boolean} <code>true</code> if the camera has been updated
  115. *
  116. * @private
  117. */
  118. View.prototype.checkForCameraUpdates = function (scene) {
  119. const camera = this.camera;
  120. const cameraClone = this._cameraClone;
  121. if (!cameraEqual(camera, cameraClone, CesiumMath.EPSILON15)) {
  122. if (!this._cameraStartFired) {
  123. camera.moveStart.raiseEvent();
  124. this._cameraStartFired = true;
  125. }
  126. this._cameraMovedTime = getTimestamp();
  127. Camera.clone(camera, cameraClone);
  128. return true;
  129. }
  130. if (
  131. this._cameraStartFired &&
  132. getTimestamp() - this._cameraMovedTime > scene.cameraEventWaitTime
  133. ) {
  134. camera.moveEnd.raiseEvent();
  135. this._cameraStartFired = false;
  136. }
  137. return false;
  138. };
  139. /**
  140. * Split the depth range of the scene into multiple frustums, and initialize
  141. * a list of {@link FrustumCommands} with the distances to the near and far
  142. * planes for each frustum.
  143. *
  144. * @param {View} view The view to which the frustum commands list is attached.
  145. * @param {Scene} scene The scene to be rendered.
  146. * @param {number} near The distance to the nearest object in the scene.
  147. * @param {number} far The distance to the farthest object in the scene.
  148. *
  149. * @private
  150. */
  151. function updateFrustums(view, scene, near, far) {
  152. const { frameState } = scene;
  153. const { camera, useLogDepth } = frameState;
  154. const farToNearRatio = useLogDepth
  155. ? scene.logarithmicDepthFarToNearRatio
  156. : scene.farToNearRatio;
  157. const is2D = scene.mode === SceneMode.SCENE2D;
  158. const nearToFarDistance2D = scene.nearToFarDistance2D;
  159. // Extend the far plane slightly further to prevent geometry clipping against the far plane.
  160. far *= 1.0 + CesiumMath.EPSILON2;
  161. // The computed near plane must be between the user defined near and far planes.
  162. // The computed far plane must between the user defined far and computed near.
  163. // This will handle the case where the computed near plane is further than the user defined far plane.
  164. near = Math.min(Math.max(near, camera.frustum.near), camera.frustum.far);
  165. far = Math.max(Math.min(far, camera.frustum.far), near);
  166. let numFrustums;
  167. if (is2D) {
  168. // The multifrustum for 2D is uniformly distributed. To avoid z-fighting in 2D,
  169. // the camera is moved to just before the frustum and the frustum depth is scaled
  170. // to be in [1.0, nearToFarDistance2D].
  171. far = Math.min(far, camera.position.z + scene.nearToFarDistance2D);
  172. near = Math.min(near, far);
  173. numFrustums = Math.ceil(
  174. Math.max(1.0, far - near) / scene.nearToFarDistance2D,
  175. );
  176. } else {
  177. // The multifrustum for 3D/CV is non-uniformly distributed.
  178. numFrustums = Math.ceil(Math.log(far / near) / Math.log(farToNearRatio));
  179. }
  180. const { frustumCommandsList } = view;
  181. frustumCommandsList.length = numFrustums;
  182. for (let m = 0; m < numFrustums; ++m) {
  183. let curNear;
  184. let curFar;
  185. if (is2D) {
  186. curNear = Math.min(
  187. far - nearToFarDistance2D,
  188. near + m * nearToFarDistance2D,
  189. );
  190. curFar = Math.min(far, curNear + nearToFarDistance2D);
  191. } else {
  192. curNear = Math.max(near, Math.pow(farToNearRatio, m) * near);
  193. curFar = Math.min(far, farToNearRatio * curNear);
  194. }
  195. let frustumCommands = frustumCommandsList[m];
  196. if (!defined(frustumCommands)) {
  197. frustumCommands = frustumCommandsList[m] = new FrustumCommands(
  198. curNear,
  199. curFar,
  200. );
  201. } else {
  202. frustumCommands.near = curNear;
  203. frustumCommands.far = curFar;
  204. }
  205. }
  206. }
  207. /**
  208. * Insert a command into the appropriate {@link FrustumCommands} based on the
  209. * range of depths covered by its bounding volume.
  210. *
  211. * @param {View} view
  212. * @param {Scene} scene
  213. * @param {CommandExtent} commandExtent
  214. *
  215. * @private
  216. */
  217. function insertIntoBin(view, scene, commandExtent) {
  218. const { command, near, far } = commandExtent;
  219. if (scene.debugShowFrustums) {
  220. command.debugOverlappingFrustums = 0;
  221. }
  222. const { frustumCommandsList } = view;
  223. for (let i = 0; i < frustumCommandsList.length; ++i) {
  224. const frustumCommands = frustumCommandsList[i];
  225. if (near > frustumCommands.far) {
  226. continue;
  227. }
  228. if (far < frustumCommands.near) {
  229. break;
  230. }
  231. const pass = command.pass;
  232. const index = frustumCommands.indices[pass]++;
  233. frustumCommands.commands[pass][index] = command;
  234. if (scene.debugShowFrustums) {
  235. command.debugOverlappingFrustums |= 1 << i;
  236. }
  237. if (command.executeInClosestFrustum) {
  238. break;
  239. }
  240. }
  241. if (scene.debugShowFrustums) {
  242. const { debugFrustumStatistics } = view;
  243. const { debugOverlappingFrustums } = command;
  244. const cf = debugFrustumStatistics.commandsInFrustums;
  245. cf[debugOverlappingFrustums] = defined(cf[debugOverlappingFrustums])
  246. ? cf[debugOverlappingFrustums] + 1
  247. : 1;
  248. ++debugFrustumStatistics.totalCommands;
  249. }
  250. scene.updateDerivedCommands(command);
  251. }
  252. const scratchCullingVolume = new CullingVolume();
  253. const scratchNearFarInterval = new Interval();
  254. View.prototype.createPotentiallyVisibleSet = function (scene) {
  255. const { frameState } = scene;
  256. const { camera, commandList, shadowState } = frameState;
  257. const { positionWC, directionWC, frustum } = camera;
  258. const computeList = scene._computeCommandList;
  259. const overlayList = scene._overlayCommandList;
  260. if (scene.debugShowFrustums) {
  261. this.debugFrustumStatistics = {
  262. totalCommands: 0,
  263. commandsInFrustums: {},
  264. };
  265. }
  266. const frustumCommandsList = this.frustumCommandsList;
  267. for (let n = 0; n < frustumCommandsList.length; ++n) {
  268. for (let p = 0; p < Pass.NUMBER_OF_PASSES; ++p) {
  269. frustumCommandsList[n].indices[p] = 0;
  270. }
  271. }
  272. computeList.length = 0;
  273. overlayList.length = 0;
  274. const commandExtents = this._commandExtents;
  275. const commandExtentCapacity = commandExtents.length;
  276. let commandExtentCount = 0;
  277. let near = +Number.MAX_VALUE;
  278. let far = -Number.MAX_VALUE;
  279. const { shadowsEnabled } = shadowState;
  280. let shadowNear = +Number.MAX_VALUE;
  281. let shadowFar = -Number.MAX_VALUE;
  282. let shadowClosestObjectSize = Number.MAX_VALUE;
  283. const occluder =
  284. frameState.mode === SceneMode.SCENE3D ? frameState.occluder : undefined;
  285. // get user culling volume minus the far plane.
  286. let { cullingVolume } = frameState;
  287. const planes = scratchCullingVolume.planes;
  288. for (let k = 0; k < 5; ++k) {
  289. planes[k] = cullingVolume.planes[k];
  290. }
  291. cullingVolume = scratchCullingVolume;
  292. for (let i = 0; i < commandList.length; ++i) {
  293. const command = commandList[i];
  294. const { pass, boundingVolume } = command;
  295. if (pass === Pass.COMPUTE) {
  296. computeList.push(command);
  297. } else if (pass === Pass.OVERLAY) {
  298. overlayList.push(command);
  299. } else {
  300. let commandNear;
  301. let commandFar;
  302. if (defined(boundingVolume)) {
  303. if (!scene.isVisible(cullingVolume, command, occluder)) {
  304. continue;
  305. }
  306. const nearFarInterval = boundingVolume.computePlaneDistances(
  307. positionWC,
  308. directionWC,
  309. scratchNearFarInterval,
  310. );
  311. commandNear = nearFarInterval.start;
  312. commandFar = nearFarInterval.stop;
  313. near = Math.min(near, commandNear);
  314. far = Math.max(far, commandFar);
  315. // Compute a tight near and far plane for commands that receive shadows. This helps compute
  316. // good splits for cascaded shadow maps. Ignore commands that exceed the maximum distance.
  317. // When moving the camera low LOD globe tiles begin to load, whose bounding volumes
  318. // throw off the near/far fitting for the shadow map. Only update for globe tiles that the
  319. // camera isn't inside.
  320. if (
  321. shadowsEnabled &&
  322. command.receiveShadows &&
  323. commandNear < ShadowMap.MAXIMUM_DISTANCE &&
  324. !(pass === Pass.GLOBE && commandNear < -100.0 && commandFar > 100.0)
  325. ) {
  326. // Get the smallest bounding volume the camera is near. This is used to place more shadow detail near the object.
  327. const size = commandFar - commandNear;
  328. if (pass !== Pass.GLOBE && commandNear < 100.0) {
  329. shadowClosestObjectSize = Math.min(shadowClosestObjectSize, size);
  330. }
  331. shadowNear = Math.min(shadowNear, commandNear);
  332. shadowFar = Math.max(shadowFar, commandFar);
  333. }
  334. } else if (command instanceof ClearCommand) {
  335. // Clear commands don't need a bounding volume - just add the clear to all frustums.
  336. commandNear = frustum.near;
  337. commandFar = frustum.far;
  338. } else {
  339. // If command has no bounding volume we need to use the camera's
  340. // worst-case near and far planes to avoid clipping something important.
  341. commandNear = frustum.near;
  342. commandFar = frustum.far;
  343. near = Math.min(near, commandNear);
  344. far = Math.max(far, commandFar);
  345. }
  346. let extent = commandExtents[commandExtentCount];
  347. if (!defined(extent)) {
  348. extent = commandExtents[commandExtentCount] = new CommandExtent();
  349. }
  350. extent.command = command;
  351. extent.near = commandNear;
  352. extent.far = commandFar;
  353. commandExtentCount++;
  354. }
  355. }
  356. if (shadowsEnabled) {
  357. shadowNear = Math.min(Math.max(shadowNear, frustum.near), frustum.far);
  358. shadowFar = Math.max(Math.min(shadowFar, frustum.far), shadowNear);
  359. // Use the computed near and far for shadows
  360. shadowState.nearPlane = shadowNear;
  361. shadowState.farPlane = shadowFar;
  362. shadowState.closestObjectSize = shadowClosestObjectSize;
  363. }
  364. updateFrustums(this, scene, near, far);
  365. for (let c = 0; c < commandExtentCount; c++) {
  366. insertIntoBin(this, scene, commandExtents[c]);
  367. }
  368. // Dereference old commands
  369. if (commandExtentCount < commandExtentCapacity) {
  370. for (let c = commandExtentCount; c < commandExtentCapacity; c++) {
  371. const commandExtent = commandExtents[c];
  372. if (!defined(commandExtent.command)) {
  373. // If the command is undefined, it's assumed that all
  374. // subsequent commmands were set to undefined as well,
  375. // so no need to loop over them all
  376. break;
  377. }
  378. commandExtent.command = undefined;
  379. }
  380. }
  381. const numFrustums = frustumCommandsList.length;
  382. const { frustumSplits } = frameState;
  383. frustumSplits.length = numFrustums + 1;
  384. for (let j = 0; j < numFrustums; ++j) {
  385. frustumSplits[j] = frustumCommandsList[j].near;
  386. if (j === numFrustums - 1) {
  387. frustumSplits[j + 1] = frustumCommandsList[j].far;
  388. }
  389. }
  390. };
  391. View.prototype.destroy = function () {
  392. this.pickFramebuffer = this.pickFramebuffer && this.pickFramebuffer.destroy();
  393. this.pickDepthFramebuffer =
  394. this.pickDepthFramebuffer && this.pickDepthFramebuffer.destroy();
  395. this.sceneFramebuffer =
  396. this.sceneFramebuffer && this.sceneFramebuffer.destroy();
  397. this.edgeFramebuffer = this.edgeFramebuffer && this.edgeFramebuffer.destroy();
  398. this.globeDepth = this.globeDepth && this.globeDepth.destroy();
  399. this.oit = this.oit && this.oit.destroy();
  400. this.translucentTileClassification =
  401. this.translucentTileClassification &&
  402. this.translucentTileClassification.destroy();
  403. this.globeTranslucencyFramebuffer =
  404. this.globeTranslucencyFramebuffer &&
  405. this.globeTranslucencyFramebuffer.destroy();
  406. const pickDepths = this.pickDepths;
  407. for (let i = 0; i < pickDepths.length; ++i) {
  408. pickDepths[i].destroy();
  409. }
  410. };
  411. export default View;