DebugTilesRenderer.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. import { Box3Helper, Group, MeshBasicMaterial } from 'three';
  2. import { TilesRenderer } from './TilesRenderer.js';
  3. import { SphereHelper } from './SphereHelper.js';
  4. const ORIGINAL_MATERIAL = Symbol( 'ORIGINAL_MATERIAL' );
  5. const NONE = 0;
  6. const SCREEN_ERROR = 1;
  7. const GEOMETRIC_ERROR = 2;
  8. const DISTANCE = 3;
  9. const DEPTH = 4;
  10. const IS_LEAF = 5;
  11. const RANDOM_COLOR = 6;
  12. function emptyRaycast() {}
  13. export class DebugTilesRenderer extends TilesRenderer {
  14. constructor( ...args ) {
  15. super( ...args );
  16. const tilesGroup = this.group;
  17. const boxGroup = new Group();
  18. tilesGroup.add( boxGroup );
  19. const sphereGroup = new Group();
  20. tilesGroup.add( sphereGroup );
  21. this.displayBoxBounds = false;
  22. this.displaySphereBounds = false;
  23. this.colorMode = NONE;
  24. this.boxGroup = boxGroup;
  25. this.sphereGroup = sphereGroup;
  26. this.maxDepth = - 1;
  27. this.maxDistance = - 1;
  28. this.maxError = - 1;
  29. this.extremeDepth = - 1;
  30. this.extremeError = - 1;
  31. }
  32. initExtremes() {
  33. let maxDepth = - 1;
  34. this.traverse( tile => {
  35. maxDepth = Math.max( maxDepth, tile.__depth );
  36. } );
  37. let maxError = - 1;
  38. this.traverse( tile => {
  39. maxError = Math.max( maxError, tile.geometricError );
  40. } );
  41. this.extremeDepth = maxDepth;
  42. this.extremeError = maxError;
  43. }
  44. loadTileSet( ...args ) {
  45. const pr = super.loadTileSet( ...args );
  46. pr.then( () => this.initExtremes() );
  47. return pr;
  48. }
  49. getTileInformationFromActiveObject( object ) {
  50. let targetTile = null;
  51. const activeTiles = this.activeTiles;
  52. activeTiles.forEach( tile => {
  53. if ( targetTile ) {
  54. return true;
  55. }
  56. const scene = tile.cached.scene;
  57. if ( scene ) {
  58. scene.traverse( c => {
  59. if ( c === object ) {
  60. targetTile = tile;
  61. }
  62. } );
  63. }
  64. } );
  65. if ( targetTile ) {
  66. return {
  67. distanceToCamera: targetTile.cached.distance,
  68. geometricError: targetTile.geometricError,
  69. screenSpaceError: targetTile.__error,
  70. depth: targetTile.__depth,
  71. isLeaf: targetTile.__isLeaf
  72. };
  73. } else {
  74. return null;
  75. }
  76. }
  77. update() {
  78. super.update();
  79. if ( ! this.root ) {
  80. return;
  81. }
  82. this.boxGroup.visible = this.displayBoxBounds;
  83. this.sphereGroup.visible = this.displaySphereBounds;
  84. let maxDepth = - 1;
  85. if ( this.maxDepth === - 1 ) {
  86. maxDepth = this.extremeDepth;
  87. } else {
  88. maxDepth = this.maxDepth;
  89. }
  90. let maxError = - 1;
  91. if ( this.maxError === - 1 ) {
  92. maxError = this.extremeError;
  93. } else {
  94. maxError = this.maxError;
  95. }
  96. let maxDistance = - 1;
  97. if ( this.maxDistance === - 1 ) {
  98. maxDistance = this.root.cached.sphere.radius;
  99. } else {
  100. maxDistance = this.maxDistance;
  101. }
  102. const errorTarget = this.errorTarget;
  103. const colorMode = this.colorMode;
  104. const visibleTiles = this.visibleTiles;
  105. visibleTiles.forEach( tile => {
  106. const scene = tile.cached.scene;
  107. scene.traverse( c => {
  108. const currMaterial = c.material;
  109. if ( currMaterial ) {
  110. const originalMaterial = c[ ORIGINAL_MATERIAL ];
  111. if ( colorMode === NONE && currMaterial !== originalMaterial ) {
  112. c.material.dispose();
  113. c.material = c[ ORIGINAL_MATERIAL ];
  114. } else if ( colorMode !== NONE && currMaterial === originalMaterial ) {
  115. c.material = new MeshBasicMaterial();
  116. }
  117. if ( colorMode !== RANDOM_COLOR ) {
  118. delete c.material.__randomColor;
  119. }
  120. switch ( colorMode ) {
  121. case DEPTH: {
  122. const val = tile.__depth / maxDepth;
  123. c.material.color.setRGB( val, val, val );
  124. break;
  125. }
  126. case SCREEN_ERROR: {
  127. const val = tile.__error / errorTarget;
  128. if ( val > 1.0 ) {
  129. c.material.color.setRGB( 1.0, 0.0, 0.0 );
  130. } else {
  131. c.material.color.setRGB( val, val, val );
  132. }
  133. break;
  134. }
  135. case GEOMETRIC_ERROR: {
  136. const val = Math.min( tile.geometricError / maxError, 1 );
  137. c.material.color.setRGB( val, val, val );
  138. break;
  139. }
  140. case DISTANCE: {
  141. // We don't update the distance if the geometric error is 0.0 so
  142. // it will always be black.
  143. const val = Math.min( tile.cached.distance / maxDistance, 1 );
  144. c.material.color.setRGB( val, val, val );
  145. break;
  146. }
  147. case IS_LEAF: {
  148. if ( ! tile.children || tile.children.length === 0 ) {
  149. c.material.color.set( 0xffffff );
  150. } else {
  151. c.material.color.set( 0 );
  152. }
  153. break;
  154. }
  155. case RANDOM_COLOR: {
  156. if ( ! c.material.__randomColor ) {
  157. const h = Math.random();
  158. const s = 0.5 + Math.random() * 0.5;
  159. const l = 0.375 + Math.random() * 0.25;
  160. c.material.color.setHSL( h, s, l );
  161. c.material.__randomColor = true;
  162. }
  163. break;
  164. }
  165. }
  166. }
  167. } );
  168. } );
  169. }
  170. setTileVisible( tile, visible ) {
  171. super.setTileVisible( tile, visible );
  172. const cached = tile.cached;
  173. const sphereGroup = this.sphereGroup;
  174. const boxGroup = this.boxGroup;
  175. const boxHelperGroup = cached.boxHelperGroup;
  176. const sphereHelper = cached.sphereHelper;
  177. if ( ! visible ) {
  178. boxGroup.remove( boxHelperGroup );
  179. sphereGroup.remove( sphereHelper );
  180. } else {
  181. boxGroup.add( boxHelperGroup );
  182. boxHelperGroup.updateMatrixWorld( true );
  183. sphereGroup.add( sphereHelper );
  184. sphereHelper.updateMatrixWorld( true );
  185. }
  186. }
  187. parseTile( buffer, tile ) {
  188. return super
  189. .parseTile( buffer, tile )
  190. .then( () => {
  191. const cached = tile.cached;
  192. const scene = cached.scene;
  193. if ( scene ) {
  194. const cachedBox = cached.box;
  195. const cachedBoxMat = cached.boxTransform;
  196. const boxHelperGroup = new Group();
  197. boxHelperGroup.matrix.copy( cachedBoxMat );
  198. boxHelperGroup.matrix.decompose( boxHelperGroup.position, boxHelperGroup.quaternion, boxHelperGroup.scale );
  199. const boxHelper = new Box3Helper( cachedBox );
  200. boxHelper.raycast = emptyRaycast;
  201. boxHelperGroup.add( boxHelper );
  202. cached.boxHelperGroup = boxHelperGroup;
  203. if ( this.visibleTiles.has( tile ) && this.displayBoxBounds ) {
  204. this.boxGroup.add( boxHelperGroup );
  205. boxHelperGroup.updateMatrixWorld( true );
  206. }
  207. const cachedSphere = cached.sphere;
  208. const sphereHelper = new SphereHelper( cachedSphere );
  209. sphereHelper.raycast = emptyRaycast;
  210. cached.sphereHelper = sphereHelper;
  211. if ( this.visibleTiles.has( tile ) && this.displaySphereBounds ) {
  212. this.sphereGroup.add( sphereHelper );
  213. sphereHelper.updateMatrixWorld( true );
  214. }
  215. scene.traverse( c => {
  216. const material = c.material;
  217. if ( material ) {
  218. c[ ORIGINAL_MATERIAL ] = material;
  219. }
  220. } );
  221. }
  222. } );
  223. }
  224. disposeTile( tile ) {
  225. super.disposeTile( tile );
  226. const cached = tile.cached;
  227. if ( cached.boxHelperGroup ) {
  228. cached.boxHelperGroup.children[ 0 ].geometry.dispose();
  229. cached.sphereHelper.geometry.dispose();
  230. delete cached.boxHelperGroup;
  231. delete cached.sphereHelper;
  232. }
  233. }
  234. }