You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

S3MTile.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. import ContentState from './Enum/ContentState.js'
  2. import S3ModelParser from '../S3MParser/S3ModelParser.js'
  3. import S3MContentParser from './S3MContentParser.js'
  4. import RangeMode from './Enum/RangeMode.js'
  5. const { Cesium } = DC.Namespace
  6. function S3MTile(
  7. layer,
  8. parent,
  9. boundingVolume,
  10. fileName,
  11. rangeData,
  12. rangeMode
  13. ) {
  14. this.layer = layer
  15. this.parent = parent
  16. let path = fileName.replace(/\\/g, '/')
  17. this.fileExtension = Cesium.getExtensionFromUri(fileName)
  18. this.relativePath = getUrl(path, layer)
  19. this.fileName = fileName
  20. this.isLeafTile = rangeData === 0
  21. this.isRootTile = false
  22. this.boundingVolume = this.createBoundingVolume(
  23. boundingVolume,
  24. layer.modelMatrix
  25. )
  26. let baseResource = Cesium.Resource.createIfNeeded(layer._baseResource)
  27. if (Cesium.defined(parent)) {
  28. this.baseUri = parent.baseUri
  29. } else {
  30. let resource = new Cesium.Resource(path)
  31. this.baseUri = resource.getBaseUri()
  32. }
  33. this.contentResource = baseResource.getDerivedResource({
  34. url: this.relativePath
  35. })
  36. this.serverKey = Cesium.RequestScheduler.getServerKey(
  37. this.contentResource.getUrlComponent()
  38. )
  39. this.request = undefined
  40. this.cacheNode = undefined
  41. this.distanceToCamera = 0
  42. this.centerZDepth = 0
  43. this.pixel = 0
  44. this.depth = parent ? parent.depth + 1 : 0
  45. this.visibilityPlaneMask = 0
  46. this.visible = false
  47. this.children = []
  48. this.renderEntities = []
  49. this.lodRangeData = Cesium.defaultValue(rangeData, 16)
  50. this.lodRangeMode = Cesium.defaultValue(rangeMode, RangeMode.Pixel)
  51. this.contentState = this.isLeafTile
  52. ? ContentState.READY
  53. : ContentState.UNLOADED
  54. this.touchedFrame = 0
  55. this.requestedFrame = 0
  56. this.processFrame = 0
  57. this.selectedFrame = 0
  58. this.updatedVisibilityFrame = 0
  59. this.foveatedFactor = 0
  60. this.priority = 0
  61. this.priorityHolder = this
  62. this.wasMinPriorityChild = false
  63. this.shouldSelect = false
  64. this.selected = false
  65. this.finalResolution = true
  66. this.refines = false
  67. }
  68. Object.defineProperties(S3MTile.prototype, {
  69. renderable: {
  70. get: function() {
  71. let renderEntities = this.renderEntities
  72. let len = renderEntities.length
  73. if (len === 0) {
  74. return false
  75. }
  76. for (let i = 0; i < len; i++) {
  77. if (!renderEntities[i].ready) {
  78. return false
  79. }
  80. }
  81. return true
  82. }
  83. }
  84. })
  85. let scratchScale = new Cesium.Cartesian3()
  86. function createSphere(sphere, transform) {
  87. let center = Cesium.Cartesian3.clone(sphere.center)
  88. let radius = sphere.radius
  89. center = Cesium.Matrix4.multiplyByPoint(transform, center, center)
  90. let scale = Cesium.Matrix4.getScale(transform, scratchScale)
  91. let maxScale = Cesium.Cartesian3.maximumComponent(scale)
  92. radius *= maxScale
  93. return new Cesium.TileBoundingSphere(center, radius)
  94. }
  95. function getUrl(fileName, layer) {
  96. fileName = fileName.replace(/\+/g, '%2B')
  97. let url = layer._basePath
  98. let isRealspace = layer._basePath.indexOf('realspace') > -1
  99. if (!isRealspace) {
  100. return fileName
  101. }
  102. let afterRealspace = url.replace(/(.*realspace)/, '')
  103. let lastUrl = url
  104. .replace(/\/rest\/realspace/g, '')
  105. .replace(afterRealspace, '')
  106. return (
  107. lastUrl +
  108. '/rest/realspace' +
  109. afterRealspace +
  110. 'data/path/' +
  111. fileName
  112. .replace(/^\.*/, '')
  113. .replace(/^\//, '')
  114. .replace(/\/$/, '')
  115. )
  116. }
  117. function createBoundingBox(box, transform) {
  118. let min = new Cesium.Cartesian3(box.min.x, box.min.y, box.min.z)
  119. Cesium.Matrix4.multiplyByPoint(transform, min, min)
  120. let max = new Cesium.Cartesian3(box.max.x, box.max.y, box.max.z)
  121. Cesium.Matrix4.multiplyByPoint(transform, max, max)
  122. let sphere = Cesium.BoundingSphere.fromCornerPoints(
  123. min,
  124. max,
  125. new Cesium.BoundingSphere()
  126. )
  127. let center = sphere.center
  128. let radius = sphere.radius
  129. let scale = Cesium.Matrix4.getScale(transform, scratchScale)
  130. let maxScale = Cesium.Cartesian3.maximumComponent(scale)
  131. radius *= maxScale
  132. return new Cesium.TileBoundingSphere(center, radius)
  133. }
  134. S3MTile.prototype.createBoundingVolume = function(parameter, transform) {
  135. if (Cesium.defined(parameter.sphere)) {
  136. return createSphere(parameter.sphere, transform)
  137. } else if (Cesium.defined(parameter.box)) {
  138. return createBoundingBox(parameter.box, transform)
  139. }
  140. return undefined
  141. }
  142. S3MTile.prototype.canTraverse = function() {
  143. if (this.children.length === 0 || this.isLeafTile) {
  144. return false
  145. }
  146. if (!Cesium.defined(this.lodRangeData)) {
  147. return true
  148. }
  149. return this.pixel > this.lodRangeData
  150. }
  151. function getBoundingVolume(tile, frameState) {
  152. return tile.boundingVolume
  153. }
  154. S3MTile.prototype.getPixel = function(frameState) {
  155. let boundingVolume = this.boundingVolume
  156. let radius = boundingVolume.radius
  157. let center = boundingVolume.center
  158. let distance = Cesium.Cartesian3.distance(
  159. frameState.camera.positionWC,
  160. center
  161. )
  162. let height = frameState.context.drawingBufferHeight
  163. let theta = frameState.camera.frustum._fovy * 0.5
  164. let screenYPix = height * 0.5
  165. let lamat = screenYPix / Math.tan(theta)
  166. return (lamat * radius) / distance
  167. }
  168. S3MTile.prototype.distanceToTile = function(frameState) {
  169. let boundingVolume = getBoundingVolume(this, frameState)
  170. return boundingVolume.distanceToCamera(frameState)
  171. }
  172. let scratchToTileCenter = new Cesium.Cartesian3()
  173. S3MTile.prototype.distanceToTileCenter = function(frameState) {
  174. const boundingVolume = getBoundingVolume(this, frameState)
  175. const toCenter = Cesium.Cartesian3.subtract(
  176. boundingVolume.center,
  177. frameState.camera.positionWC,
  178. scratchToTileCenter
  179. )
  180. return Cesium.Cartesian3.dot(frameState.camera.directionWC, toCenter)
  181. }
  182. S3MTile.prototype.visibility = function(frameState, parentVisibilityPlaneMask) {
  183. let boundingVolume = getBoundingVolume(this, frameState)
  184. return frameState.cullingVolume.computeVisibilityWithPlaneMask(
  185. boundingVolume,
  186. parentVisibilityPlaneMask
  187. )
  188. }
  189. let scratchCartesian = new Cesium.Cartesian3()
  190. function priorityDeferred(tile, frameState) {
  191. let camera = frameState.camera
  192. let boundingVolume = tile.boundingVolume
  193. let radius = boundingVolume.radius
  194. let scaledCameraDirection = Cesium.Cartesian3.multiplyByScalar(
  195. camera.directionWC,
  196. tile.centerZDepth,
  197. scratchCartesian
  198. )
  199. let closestPointOnLine = Cesium.Cartesian3.add(
  200. camera.positionWC,
  201. scaledCameraDirection,
  202. scratchCartesian
  203. )
  204. let toLine = Cesium.Cartesian3.subtract(
  205. closestPointOnLine,
  206. boundingVolume.center,
  207. scratchCartesian
  208. )
  209. let distanceToCenterLine = Cesium.Cartesian3.magnitude(toLine)
  210. let notTouchingSphere = distanceToCenterLine > radius
  211. if (notTouchingSphere) {
  212. let toLineNormalized = Cesium.Cartesian3.normalize(toLine, scratchCartesian)
  213. let scaledToLine = Cesium.Cartesian3.multiplyByScalar(
  214. toLineNormalized,
  215. radius,
  216. scratchCartesian
  217. )
  218. let closestOnSphere = Cesium.Cartesian3.add(
  219. boundingVolume.center,
  220. scaledToLine,
  221. scratchCartesian
  222. )
  223. let toClosestOnSphere = Cesium.Cartesian3.subtract(
  224. closestOnSphere,
  225. camera.positionWC,
  226. scratchCartesian
  227. )
  228. let toClosestOnSphereNormalize = Cesium.Cartesian3.normalize(
  229. toClosestOnSphere,
  230. scratchCartesian
  231. )
  232. tile.foveatedFactor =
  233. 1.0 -
  234. Math.abs(
  235. Cesium.Cartesian3.dot(camera.directionWC, toClosestOnSphereNormalize)
  236. )
  237. } else {
  238. tile.foveatedFactor = 0.0
  239. }
  240. }
  241. S3MTile.prototype.updateVisibility = function(frameState, layer) {
  242. let parent = this.parent
  243. let parentVisibilityPlaneMask = Cesium.defined(parent)
  244. ? parent.visibilityPlaneMask
  245. : Cesium.CullingVolume.MASK_INDETERMINATE
  246. this.distanceToCamera = this.distanceToTile(frameState)
  247. this.centerZDepth = this.distanceToTileCenter(frameState)
  248. this.pixel = this.getPixel(frameState)
  249. this.visibilityPlaneMask = this.visibility(
  250. frameState,
  251. parentVisibilityPlaneMask
  252. )
  253. this.visible =
  254. this.visibilityPlaneMask !== Cesium.CullingVolume.MASK_OUTSIDE &&
  255. this.distanceToCamera >= layer.visibleDistanceMin &&
  256. this.distanceToCamera <= layer.visibleDistanceMax
  257. this.priorityDeferred = priorityDeferred(this, frameState)
  258. }
  259. function createPriorityFunction(tile) {
  260. return function() {
  261. return tile.priority
  262. }
  263. }
  264. function getContentFailedFunction(tile) {
  265. return function(error) {
  266. tile.contentState = ContentState.FAILED
  267. tile.contentReadyPromise.reject(error)
  268. }
  269. }
  270. function createChildren(parent, datas) {
  271. let layer = parent.layer
  272. let length = datas.length
  273. let minRangeData = Number.MAX_VALUE
  274. let maxRangeData = 0
  275. let mode = RangeMode.Pixel
  276. for (let i = 0; i < length; i++) {
  277. let data = datas[i]
  278. let boundingVolume = data.boundingVolume
  279. let fileName = data.rangeDataList
  280. fileName = parent.baseUri + fileName
  281. let rangeData = data.rangeList
  282. let rangeMode = data.rangeMode
  283. let renderEntitieMap = data.geoMap
  284. if (rangeData !== 0) {
  285. let tile = new S3MTile(
  286. layer,
  287. parent,
  288. boundingVolume,
  289. fileName,
  290. rangeData,
  291. rangeMode
  292. )
  293. parent.children.push(tile)
  294. layer._cache.add(tile)
  295. }
  296. for (let geoName in renderEntitieMap) {
  297. if (renderEntitieMap.hasOwnProperty(geoName)) {
  298. parent.renderEntities.push(renderEntitieMap[geoName])
  299. }
  300. }
  301. minRangeData = Math.min(minRangeData, rangeData)
  302. maxRangeData = Math.max(maxRangeData, rangeData)
  303. mode = rangeMode
  304. }
  305. if (parent.isRootTile) {
  306. parent.lodRangeData =
  307. mode === RangeMode.Pixel ? minRangeData / 2 : maxRangeData * 2
  308. parent.lodRangeMode = mode
  309. }
  310. }
  311. function contentReadyFunction(layer, tile, arrayBuffer) {
  312. layer._cache.add(tile)
  313. S3ModelParser.s3tc = layer.context.s3tc
  314. S3ModelParser.pvrtc = layer.context.pvrtc
  315. S3ModelParser.etc1 = layer.context.etc1
  316. let content = S3ModelParser.parseBuffer(arrayBuffer)
  317. if (!content) {
  318. tile.contentState = ContentState.FAILED
  319. tile.contentReadyPromise.reject()
  320. return
  321. }
  322. let data = S3MContentParser.parse(layer, content, tile)
  323. createChildren(tile, data)
  324. tile.selectedFrame = 0
  325. tile.contentState = ContentState.READY
  326. tile.contentReadyPromise.resolve(content)
  327. }
  328. S3MTile.prototype.requestContent = function() {
  329. let that = this
  330. let layer = this.layer
  331. let resource = this.contentResource.clone()
  332. let request = new Cesium.Request({
  333. throttle: true,
  334. throttleByServer: true,
  335. type: Cesium.RequestType.TILES3D,
  336. priorityFunction: createPriorityFunction(this),
  337. serverKey: this.serverKey
  338. })
  339. this.request = request
  340. resource.request = request
  341. let promise = resource.fetchArrayBuffer()
  342. if (!Cesium.defined(promise)) {
  343. return false
  344. }
  345. this.contentState = ContentState.LOADING
  346. this.contentReadyPromise = Cesium.when.defer()
  347. let contentFailedFunction = getContentFailedFunction(this)
  348. promise
  349. .then(function(arrayBuffer) {
  350. if (that.isDestroyed()) {
  351. contentFailedFunction()
  352. return
  353. }
  354. contentReadyFunction(layer, that, arrayBuffer)
  355. })
  356. .otherwise(function(error) {
  357. if (request.state === Cesium.RequestState.CANCELLED) {
  358. that.contentState = ContentState.UNLOADED
  359. return
  360. }
  361. contentFailedFunction(error)
  362. })
  363. return true
  364. }
  365. function priorityNormalizeAndClamp(value, minimum, maximum) {
  366. return Math.max(
  367. Cesium.Math.normalize(value, minimum, maximum) - Cesium.Math.EPSILON7,
  368. 0.0
  369. )
  370. }
  371. function isolateDigits(normalizedValue, numberOfDigits, leftShift) {
  372. let scaled = normalizedValue * Math.pow(10, numberOfDigits)
  373. let integer = parseInt(scaled)
  374. return integer * Math.pow(10, leftShift)
  375. }
  376. S3MTile.prototype.updatePriority = function(layer, frameState) {
  377. let minimumPriority = layer._minimumPriority
  378. let maximumPriority = layer._maximumPriority
  379. let leftShift = 4
  380. let digitsCount = 4
  381. let normalizedFoveatedFactor = priorityNormalizeAndClamp(
  382. this.foveatedFactor,
  383. minimumPriority.foveatedFactor,
  384. maximumPriority.foveatedFactor
  385. )
  386. let foveatedDigits = isolateDigits(
  387. normalizedFoveatedFactor,
  388. digitsCount,
  389. leftShift
  390. )
  391. leftShift = 8
  392. let normalizedPixel = priorityNormalizeAndClamp(
  393. this.pixel,
  394. minimumPriority.pixel,
  395. maximumPriority.pixel
  396. )
  397. let pixelDigits = isolateDigits(1.0 - normalizedPixel, digitsCount, leftShift)
  398. leftShift = 0
  399. let distancePriority = priorityNormalizeAndClamp(
  400. this.distanceToCamera,
  401. minimumPriority.distance,
  402. maximumPriority.distance
  403. )
  404. let distanceDigit = isolateDigits(distancePriority, digitsCount, leftShift)
  405. this.priority = foveatedDigits + pixelDigits + distanceDigit
  406. }
  407. S3MTile.prototype.update = function(frameState, layer) {
  408. for (let i = 0, j = this.renderEntities.length; i < j; i++) {
  409. this.renderEntities[i].update(frameState, layer)
  410. }
  411. }
  412. S3MTile.prototype.free = function() {
  413. this.contentState = ContentState.UNLOADED
  414. this.request = undefined
  415. this.cacheNode = undefined
  416. this.priorityHolder = undefined
  417. this.contentReadyPromise = undefined
  418. this.priorityHolder = undefined
  419. for (let i = 0, j = this.renderEntities.length; i < j; i++) {
  420. this.renderEntities[i].destroy()
  421. }
  422. this.renderEntities.length = 0
  423. this.children.length = 0
  424. }
  425. S3MTile.prototype.isDestroyed = function() {
  426. return false
  427. }
  428. S3MTile.prototype.destroy = function() {
  429. this.free()
  430. return Cesium.destroyObject(this)
  431. }
  432. export default S3MTile