Octree.js 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138
  1. /*!
  2. *
  3. * threeoctree.js (r60) / https://github.com/collinhover/threeoctree
  4. * (sparse) dynamic 3D spatial representation structure for fast searches.
  5. *
  6. * @author Collin Hover / http://collinhover.com/
  7. * based on Dynamic Octree by Piko3D @ http://www.piko3d.com/ and Octree by Marek Pawlowski @ pawlowski.it
  8. *
  9. */
  10. ( function ( THREE ) {
  11. "use strict";
  12. /*===================================================
  13. utility
  14. =====================================================*/
  15. function isNumber( n ) {
  16. return ! isNaN( n ) && isFinite( n );
  17. }
  18. function isArray( target ) {
  19. return Object.prototype.toString.call( target ) === '[object Array]';
  20. }
  21. function toArray( target ) {
  22. return target ? ( isArray( target ) !== true ? [ target ] : target ) : [];
  23. }
  24. function indexOfValue( array, value ) {
  25. for ( var i = 0, il = array.length; i < il; i ++ ) {
  26. if ( array[ i ] === value ) {
  27. return i;
  28. }
  29. }
  30. return - 1;
  31. }
  32. function indexOfPropertyWithValue( array, property, value ) {
  33. for ( var i = 0, il = array.length; i < il; i ++ ) {
  34. if ( array[ i ][ property ] === value ) {
  35. return i;
  36. }
  37. }
  38. return - 1;
  39. }
  40. /*===================================================
  41. octree
  42. =====================================================*/
  43. THREE.Octree = function ( parameters ) {
  44. // handle parameters
  45. parameters = parameters || {};
  46. parameters.tree = this;
  47. // static properties ( modification is not recommended )
  48. this.nodeCount = 0;
  49. this.INDEX_INSIDE_CROSS = - 1;
  50. this.INDEX_OUTSIDE_OFFSET = 2;
  51. this.INDEX_OUTSIDE_POS_X = isNumber( parameters.INDEX_OUTSIDE_POS_X ) ? parameters.INDEX_OUTSIDE_POS_X : 0;
  52. this.INDEX_OUTSIDE_NEG_X = isNumber( parameters.INDEX_OUTSIDE_NEG_X ) ? parameters.INDEX_OUTSIDE_NEG_X : 1;
  53. this.INDEX_OUTSIDE_POS_Y = isNumber( parameters.INDEX_OUTSIDE_POS_Y ) ? parameters.INDEX_OUTSIDE_POS_Y : 2;
  54. this.INDEX_OUTSIDE_NEG_Y = isNumber( parameters.INDEX_OUTSIDE_NEG_Y ) ? parameters.INDEX_OUTSIDE_NEG_Y : 3;
  55. this.INDEX_OUTSIDE_POS_Z = isNumber( parameters.INDEX_OUTSIDE_POS_Z ) ? parameters.INDEX_OUTSIDE_POS_Z : 4;
  56. this.INDEX_OUTSIDE_NEG_Z = isNumber( parameters.INDEX_OUTSIDE_NEG_Z ) ? parameters.INDEX_OUTSIDE_NEG_Z : 5;
  57. this.INDEX_OUTSIDE_MAP = [];
  58. this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_X ] = { index: this.INDEX_OUTSIDE_POS_X, count: 0, x: 1, y: 0, z: 0 };
  59. this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_X ] = { index: this.INDEX_OUTSIDE_NEG_X, count: 0, x: - 1, y: 0, z: 0 };
  60. this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_Y ] = { index: this.INDEX_OUTSIDE_POS_Y, count: 0, x: 0, y: 1, z: 0 };
  61. this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_Y ] = { index: this.INDEX_OUTSIDE_NEG_Y, count: 0, x: 0, y: - 1, z: 0 };
  62. this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_Z ] = { index: this.INDEX_OUTSIDE_POS_Z, count: 0, x: 0, y: 0, z: 1 };
  63. this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_Z ] = { index: this.INDEX_OUTSIDE_NEG_Z, count: 0, x: 0, y: 0, z: - 1 };
  64. this.FLAG_POS_X = 1 << ( this.INDEX_OUTSIDE_POS_X + 1 );
  65. this.FLAG_NEG_X = 1 << ( this.INDEX_OUTSIDE_NEG_X + 1 );
  66. this.FLAG_POS_Y = 1 << ( this.INDEX_OUTSIDE_POS_Y + 1 );
  67. this.FLAG_NEG_Y = 1 << ( this.INDEX_OUTSIDE_NEG_Y + 1 );
  68. this.FLAG_POS_Z = 1 << ( this.INDEX_OUTSIDE_POS_Z + 1 );
  69. this.FLAG_NEG_Z = 1 << ( this.INDEX_OUTSIDE_NEG_Z + 1 );
  70. this.utilVec31Search = new THREE.Vector3();
  71. this.utilVec32Search = new THREE.Vector3();
  72. // pass scene to see octree structure
  73. this.scene = parameters.scene;
  74. if ( this.scene ) {
  75. var helper = new THREE.BoxHelper( new THREE.Mesh( new THREE.BoxBufferGeometry( 1, 1, 1 ) ), 0xff0066 );
  76. this.visualGeometry = helper.geometry;
  77. this.visualMaterial = helper.material;
  78. }
  79. // properties
  80. this.objects = [];
  81. this.objectsMap = {};
  82. this.objectsData = [];
  83. this.objectsDeferred = [];
  84. this.depthMax = isNumber( parameters.depthMax ) ? parameters.depthMax : Infinity;
  85. this.objectsThreshold = isNumber( parameters.objectsThreshold ) ? parameters.objectsThreshold : 8;
  86. this.overlapPct = isNumber( parameters.overlapPct ) ? parameters.overlapPct : 0.15;
  87. this.undeferred = parameters.undeferred || false;
  88. this.root = parameters.root instanceof THREE.OctreeNode ? parameters.root : new THREE.OctreeNode( parameters );
  89. };
  90. THREE.Octree.prototype = {
  91. update: function () {
  92. // add any deferred objects that were waiting for render cycle
  93. if ( this.objectsDeferred.length > 0 ) {
  94. for ( var i = 0, il = this.objectsDeferred.length; i < il; i ++ ) {
  95. var deferred = this.objectsDeferred[ i ];
  96. this.addDeferred( deferred.object, deferred.options );
  97. }
  98. this.objectsDeferred.length = 0;
  99. }
  100. },
  101. add: function ( object, options ) {
  102. // add immediately
  103. if ( this.undeferred ) {
  104. this.updateObject( object );
  105. this.addDeferred( object, options );
  106. } else {
  107. // defer add until update called
  108. this.objectsDeferred.push( { object: object, options: options } );
  109. }
  110. },
  111. addDeferred: function ( object, options ) {
  112. var i, l,
  113. geometry,
  114. faces,
  115. useFaces,
  116. vertices,
  117. useVertices,
  118. objectData;
  119. // ensure object is not object data
  120. if ( object instanceof THREE.OctreeObjectData ) {
  121. object = object.object;
  122. }
  123. // check uuid to avoid duplicates
  124. if ( ! object.uuid ) {
  125. object.uuid = THREE.Math.generateUUID();
  126. }
  127. if ( ! this.objectsMap[ object.uuid ] ) {
  128. // store
  129. this.objects.push( object );
  130. this.objectsMap[ object.uuid ] = object;
  131. // check options
  132. if ( options ) {
  133. useFaces = options.useFaces;
  134. useVertices = options.useVertices;
  135. }
  136. if ( useVertices === true ) {
  137. geometry = object.geometry;
  138. vertices = geometry.vertices;
  139. for ( i = 0, l = vertices.length; i < l; i ++ ) {
  140. this.addObjectData( object, vertices[ i ] );
  141. }
  142. } else if ( useFaces === true ) {
  143. geometry = object.geometry;
  144. faces = geometry.faces;
  145. for ( i = 0, l = faces.length; i < l; i ++ ) {
  146. this.addObjectData( object, faces[ i ] );
  147. }
  148. } else {
  149. this.addObjectData( object );
  150. }
  151. }
  152. },
  153. addObjectData: function ( object, part ) {
  154. var objectData = new THREE.OctreeObjectData( object, part );
  155. // add to tree objects data list
  156. this.objectsData.push( objectData );
  157. // add to nodes
  158. this.root.addObject( objectData );
  159. },
  160. remove: function ( object ) {
  161. var i, l,
  162. objectData = object,
  163. index,
  164. objectsDataRemoved;
  165. // ensure object is not object data for index search
  166. if ( object instanceof THREE.OctreeObjectData ) {
  167. object = object.object;
  168. }
  169. // check uuid
  170. if ( this.objectsMap[ object.uuid ] ) {
  171. this.objectsMap[ object.uuid ] = undefined;
  172. // check and remove from objects, nodes, and data lists
  173. index = indexOfValue( this.objects, object );
  174. if ( index !== - 1 ) {
  175. this.objects.splice( index, 1 );
  176. // remove from nodes
  177. objectsDataRemoved = this.root.removeObject( objectData );
  178. // remove from objects data list
  179. for ( i = 0, l = objectsDataRemoved.length; i < l; i ++ ) {
  180. objectData = objectsDataRemoved[ i ];
  181. index = indexOfValue( this.objectsData, objectData );
  182. if ( index !== - 1 ) {
  183. this.objectsData.splice( index, 1 );
  184. }
  185. }
  186. }
  187. } else if ( this.objectsDeferred.length > 0 ) {
  188. // check and remove from deferred
  189. index = indexOfPropertyWithValue( this.objectsDeferred, 'object', object );
  190. if ( index !== - 1 ) {
  191. this.objectsDeferred.splice( index, 1 );
  192. }
  193. }
  194. },
  195. extend: function ( octree ) {
  196. var i, l,
  197. objectsData,
  198. objectData;
  199. if ( octree instanceof THREE.Octree ) {
  200. // for each object data
  201. objectsData = octree.objectsData;
  202. for ( i = 0, l = objectsData.length; i < l; i ++ ) {
  203. objectData = objectsData[ i ];
  204. this.add( objectData, { useFaces: objectData.faces, useVertices: objectData.vertices } );
  205. }
  206. }
  207. },
  208. rebuild: function () {
  209. var i, l,
  210. node,
  211. object,
  212. objectData,
  213. indexOctant,
  214. indexOctantLast,
  215. objectsUpdate = [];
  216. // check all object data for changes in position
  217. // assumes all object matrices are up to date
  218. for ( i = 0, l = this.objectsData.length; i < l; i ++ ) {
  219. objectData = this.objectsData[ i ];
  220. node = objectData.node;
  221. // update object
  222. objectData.update();
  223. // if position has changed since last organization of object in tree
  224. if ( node instanceof THREE.OctreeNode && ! objectData.positionLast.equals( objectData.position ) ) {
  225. // get octant index of object within current node
  226. indexOctantLast = objectData.indexOctant;
  227. indexOctant = node.getOctantIndex( objectData );
  228. // if object octant index has changed
  229. if ( indexOctant !== indexOctantLast ) {
  230. // add to update list
  231. objectsUpdate.push( objectData );
  232. }
  233. }
  234. }
  235. // update changed objects
  236. for ( i = 0, l = objectsUpdate.length; i < l; i ++ ) {
  237. objectData = objectsUpdate[ i ];
  238. // remove object from current node
  239. objectData.node.removeObject( objectData );
  240. // add object to tree root
  241. this.root.addObject( objectData );
  242. }
  243. },
  244. updateObject: function ( object ) {
  245. var i, l,
  246. parentCascade = [ object ],
  247. parent,
  248. parentUpdate;
  249. // search all parents between object and root for world matrix update
  250. parent = object.parent;
  251. while ( parent ) {
  252. parentCascade.push( parent );
  253. parent = parent.parent;
  254. }
  255. for ( i = 0, l = parentCascade.length; i < l; i ++ ) {
  256. parent = parentCascade[ i ];
  257. if ( parent.matrixWorldNeedsUpdate === true ) {
  258. parentUpdate = parent;
  259. }
  260. }
  261. // update world matrix starting at uppermost parent that needs update
  262. if ( typeof parentUpdate !== 'undefined' ) {
  263. parentUpdate.updateMatrixWorld();
  264. }
  265. },
  266. search: function ( position, radius, organizeByObject, direction ) {
  267. var i, l,
  268. node,
  269. objects,
  270. objectData,
  271. object,
  272. results,
  273. resultData,
  274. resultsObjectsIndices,
  275. resultObjectIndex,
  276. directionPct;
  277. // add root objects
  278. objects = [].concat( this.root.objects );
  279. // ensure radius (i.e. distance of ray) is a number
  280. if ( ! ( radius > 0 ) ) {
  281. radius = Number.MAX_VALUE;
  282. }
  283. // if direction passed, normalize and find pct
  284. if ( direction instanceof THREE.Vector3 ) {
  285. direction = this.utilVec31Search.copy( direction ).normalize();
  286. directionPct = this.utilVec32Search.set( 1, 1, 1 ).divide( direction );
  287. }
  288. // search each node of root
  289. for ( i = 0, l = this.root.nodesIndices.length; i < l; i ++ ) {
  290. node = this.root.nodesByIndex[ this.root.nodesIndices[ i ] ];
  291. objects = node.search( position, radius, objects, direction, directionPct );
  292. }
  293. // if should organize results by object
  294. if ( organizeByObject === true ) {
  295. results = [];
  296. resultsObjectsIndices = [];
  297. // for each object data found
  298. for ( i = 0, l = objects.length; i < l; i ++ ) {
  299. objectData = objects[ i ];
  300. object = objectData.object;
  301. resultObjectIndex = indexOfValue( resultsObjectsIndices, object );
  302. // if needed, create new result data
  303. if ( resultObjectIndex === - 1 ) {
  304. resultData = {
  305. object: object,
  306. faces: [],
  307. vertices: []
  308. };
  309. results.push( resultData );
  310. resultsObjectsIndices.push( object );
  311. } else {
  312. resultData = results[ resultObjectIndex ];
  313. }
  314. // object data has faces or vertices, add to list
  315. if ( objectData.faces ) {
  316. resultData.faces.push( objectData.faces );
  317. } else if ( objectData.vertices ) {
  318. resultData.vertices.push( objectData.vertices );
  319. }
  320. }
  321. } else {
  322. results = objects;
  323. }
  324. return results;
  325. },
  326. setRoot: function ( root ) {
  327. if ( root instanceof THREE.OctreeNode ) {
  328. // store new root
  329. this.root = root;
  330. // update properties
  331. this.root.updateProperties();
  332. }
  333. },
  334. getDepthEnd: function () {
  335. return this.root.getDepthEnd();
  336. },
  337. getNodeCountEnd: function () {
  338. return this.root.getNodeCountEnd();
  339. },
  340. getObjectCountEnd: function () {
  341. return this.root.getObjectCountEnd();
  342. },
  343. toConsole: function () {
  344. this.root.toConsole();
  345. }
  346. };
  347. /*===================================================
  348. object data
  349. =====================================================*/
  350. THREE.OctreeObjectData = function ( object, part ) {
  351. // properties
  352. this.object = object;
  353. // handle part by type
  354. if ( part instanceof THREE.Face3 ) {
  355. this.faces = part;
  356. this.face3 = true;
  357. this.utilVec31FaceBounds = new THREE.Vector3();
  358. } else if ( part instanceof THREE.Vector3 ) {
  359. this.vertices = part;
  360. }
  361. this.radius = 0;
  362. this.position = new THREE.Vector3();
  363. // initial update
  364. if ( this.object instanceof THREE.Object3D ) {
  365. this.update();
  366. }
  367. this.positionLast = this.position.clone();
  368. };
  369. THREE.OctreeObjectData.prototype = {
  370. update: function () {
  371. if ( this.face3 ) {
  372. this.radius = this.getFace3BoundingRadius( this.object, this.faces );
  373. this.position.copy( this.faces.centroid ).applyMatrix4( this.object.matrixWorld );
  374. } else if ( this.vertices ) {
  375. this.radius = this.object.material.size || 1;
  376. this.position.copy( this.vertices ).applyMatrix4( this.object.matrixWorld );
  377. } else {
  378. if ( this.object.geometry ) {
  379. if ( this.object.geometry.boundingSphere === null ) {
  380. this.object.geometry.computeBoundingSphere();
  381. }
  382. this.radius = this.object.geometry.boundingSphere.radius;
  383. this.position.copy( this.object.geometry.boundingSphere.center ).applyMatrix4( this.object.matrixWorld );
  384. } else {
  385. this.radius = this.object.boundRadius;
  386. this.position.setFromMatrixPosition( this.object.matrixWorld );
  387. }
  388. }
  389. this.radius = this.radius * Math.max( this.object.scale.x, this.object.scale.y, this.object.scale.z );
  390. },
  391. getFace3BoundingRadius: function ( object, face ) {
  392. if ( face.centroid === undefined ) face.centroid = new THREE.Vector3();
  393. var geometry = object.geometry || object,
  394. vertices = geometry.vertices,
  395. centroid = face.centroid,
  396. va = vertices[ face.a ], vb = vertices[ face.b ], vc = vertices[ face.c ],
  397. centroidToVert = this.utilVec31FaceBounds,
  398. radius;
  399. centroid.addVectors( va, vb ).add( vc ).divideScalar( 3 );
  400. radius = Math.max( centroidToVert.subVectors( centroid, va ).length(), centroidToVert.subVectors( centroid, vb ).length(), centroidToVert.subVectors( centroid, vc ).length() );
  401. return radius;
  402. }
  403. };
  404. /*===================================================
  405. node
  406. =====================================================*/
  407. THREE.OctreeNode = function ( parameters ) {
  408. // utility
  409. this.utilVec31Branch = new THREE.Vector3();
  410. this.utilVec31Expand = new THREE.Vector3();
  411. this.utilVec31Ray = new THREE.Vector3();
  412. // handle parameters
  413. parameters = parameters || {};
  414. // store or create tree
  415. if ( parameters.tree instanceof THREE.Octree ) {
  416. this.tree = parameters.tree;
  417. } else if ( parameters.parent instanceof THREE.OctreeNode !== true ) {
  418. parameters.root = this;
  419. this.tree = new THREE.Octree( parameters );
  420. }
  421. // basic properties
  422. this.id = this.tree.nodeCount ++;
  423. this.position = parameters.position instanceof THREE.Vector3 ? parameters.position : new THREE.Vector3();
  424. this.radius = parameters.radius > 0 ? parameters.radius : 1;
  425. this.indexOctant = parameters.indexOctant;
  426. this.depth = 0;
  427. // reset and assign parent
  428. this.reset();
  429. this.setParent( parameters.parent );
  430. // additional properties
  431. this.overlap = this.radius * this.tree.overlapPct;
  432. this.radiusOverlap = this.radius + this.overlap;
  433. this.left = this.position.x - this.radiusOverlap;
  434. this.right = this.position.x + this.radiusOverlap;
  435. this.bottom = this.position.y - this.radiusOverlap;
  436. this.top = this.position.y + this.radiusOverlap;
  437. this.back = this.position.z - this.radiusOverlap;
  438. this.front = this.position.z + this.radiusOverlap;
  439. // visual
  440. if ( this.tree.scene ) {
  441. this.visual = new THREE.LineSegments( this.tree.visualGeometry, this.tree.visualMaterial );
  442. this.visual.scale.set( this.radiusOverlap * 2, this.radiusOverlap * 2, this.radiusOverlap * 2 );
  443. this.visual.position.copy( this.position );
  444. this.tree.scene.add( this.visual );
  445. }
  446. };
  447. THREE.OctreeNode.prototype = {
  448. setParent: function ( parent ) {
  449. // store new parent
  450. if ( parent !== this && this.parent !== parent ) {
  451. this.parent = parent;
  452. // update properties
  453. this.updateProperties();
  454. }
  455. },
  456. updateProperties: function () {
  457. var i, l;
  458. // properties
  459. if ( this.parent instanceof THREE.OctreeNode ) {
  460. this.tree = this.parent.tree;
  461. this.depth = this.parent.depth + 1;
  462. } else {
  463. this.depth = 0;
  464. }
  465. // cascade
  466. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  467. this.nodesByIndex[ this.nodesIndices[ i ] ].updateProperties();
  468. }
  469. },
  470. reset: function ( cascade, removeVisual ) {
  471. var i, l,
  472. node,
  473. nodesIndices = this.nodesIndices || [],
  474. nodesByIndex = this.nodesByIndex;
  475. this.objects = [];
  476. this.nodesIndices = [];
  477. this.nodesByIndex = {};
  478. // unset parent in nodes
  479. for ( i = 0, l = nodesIndices.length; i < l; i ++ ) {
  480. node = nodesByIndex[ nodesIndices[ i ] ];
  481. node.setParent( undefined );
  482. if ( cascade === true ) {
  483. node.reset( cascade, removeVisual );
  484. }
  485. }
  486. // visual
  487. if ( removeVisual === true && this.visual && this.visual.parent ) {
  488. this.visual.parent.remove( this.visual );
  489. }
  490. },
  491. addNode: function ( node, indexOctant ) {
  492. node.indexOctant = indexOctant;
  493. if ( indexOfValue( this.nodesIndices, indexOctant ) === - 1 ) {
  494. this.nodesIndices.push( indexOctant );
  495. }
  496. this.nodesByIndex[ indexOctant ] = node;
  497. if ( node.parent !== this ) {
  498. node.setParent( this );
  499. }
  500. },
  501. removeNode: function ( indexOctant ) {
  502. var index,
  503. node;
  504. index = indexOfValue( this.nodesIndices, indexOctant );
  505. this.nodesIndices.splice( index, 1 );
  506. node = node || this.nodesByIndex[ indexOctant ];
  507. delete this.nodesByIndex[ indexOctant ];
  508. if ( node.parent === this ) {
  509. node.setParent( undefined );
  510. }
  511. },
  512. addObject: function ( object ) {
  513. var index,
  514. indexOctant,
  515. node;
  516. // get object octant index
  517. indexOctant = this.getOctantIndex( object );
  518. // if object fully contained by an octant, add to subtree
  519. if ( indexOctant > - 1 && this.nodesIndices.length > 0 ) {
  520. node = this.branch( indexOctant );
  521. node.addObject( object );
  522. } else if ( indexOctant < - 1 && this.parent instanceof THREE.OctreeNode ) {
  523. // if object lies outside bounds, add to parent node
  524. this.parent.addObject( object );
  525. } else {
  526. // add to this objects list
  527. index = indexOfValue( this.objects, object );
  528. if ( index === - 1 ) {
  529. this.objects.push( object );
  530. }
  531. // node reference
  532. object.node = this;
  533. // check if need to expand, split, or both
  534. this.checkGrow();
  535. }
  536. },
  537. addObjectWithoutCheck: function ( objects ) {
  538. var i, l,
  539. object;
  540. for ( i = 0, l = objects.length; i < l; i ++ ) {
  541. object = objects[ i ];
  542. this.objects.push( object );
  543. object.node = this;
  544. }
  545. },
  546. removeObject: function ( object ) {
  547. var i, l,
  548. nodesRemovedFrom,
  549. removeData;
  550. // cascade through tree to find and remove object
  551. removeData = this.removeObjectRecursive( object, { searchComplete: false, nodesRemovedFrom: [], objectsDataRemoved: [] } );
  552. // if object removed, try to shrink the nodes it was removed from
  553. nodesRemovedFrom = removeData.nodesRemovedFrom;
  554. if ( nodesRemovedFrom.length > 0 ) {
  555. for ( i = 0, l = nodesRemovedFrom.length; i < l; i ++ ) {
  556. nodesRemovedFrom[ i ].shrink();
  557. }
  558. }
  559. return removeData.objectsDataRemoved;
  560. },
  561. removeObjectRecursive: function ( object, removeData ) {
  562. var i, l,
  563. index = - 1,
  564. objectData,
  565. node,
  566. objectRemoved;
  567. // find index of object in objects list
  568. // search and remove object data (fast)
  569. if ( object instanceof THREE.OctreeObjectData ) {
  570. // remove from this objects list
  571. index = indexOfValue( this.objects, object );
  572. if ( index !== - 1 ) {
  573. this.objects.splice( index, 1 );
  574. object.node = undefined;
  575. removeData.objectsDataRemoved.push( object );
  576. removeData.searchComplete = objectRemoved = true;
  577. }
  578. } else {
  579. // search each object data for object and remove (slow)
  580. for ( i = this.objects.length - 1; i >= 0; i -- ) {
  581. objectData = this.objects[ i ];
  582. if ( objectData.object === object ) {
  583. this.objects.splice( i, 1 );
  584. objectData.node = undefined;
  585. removeData.objectsDataRemoved.push( objectData );
  586. objectRemoved = true;
  587. if ( ! objectData.faces && ! objectData.vertices ) {
  588. removeData.searchComplete = true;
  589. break;
  590. }
  591. }
  592. }
  593. }
  594. // if object data removed and this is not on nodes removed from
  595. if ( objectRemoved === true ) {
  596. removeData.nodesRemovedFrom.push( this );
  597. }
  598. // if search not complete, search nodes
  599. if ( removeData.searchComplete !== true ) {
  600. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  601. node = this.nodesByIndex[ this.nodesIndices[ i ] ];
  602. // try removing object from node
  603. removeData = node.removeObjectRecursive( object, removeData );
  604. if ( removeData.searchComplete === true ) {
  605. break;
  606. }
  607. }
  608. }
  609. return removeData;
  610. },
  611. checkGrow: function () {
  612. // if object count above max
  613. if ( this.objects.length > this.tree.objectsThreshold && this.tree.objectsThreshold > 0 ) {
  614. this.grow();
  615. }
  616. },
  617. grow: function () {
  618. var indexOctant,
  619. object,
  620. objectsExpand = [],
  621. objectsExpandOctants = [],
  622. objectsSplit = [],
  623. objectsSplitOctants = [],
  624. objectsRemaining = [],
  625. i, l;
  626. // for each object
  627. for ( i = 0, l = this.objects.length; i < l; i ++ ) {
  628. object = this.objects[ i ];
  629. // get object octant index
  630. indexOctant = this.getOctantIndex( object );
  631. // if lies within octant
  632. if ( indexOctant > - 1 ) {
  633. objectsSplit.push( object );
  634. objectsSplitOctants.push( indexOctant );
  635. } else if ( indexOctant < - 1 ) {
  636. // lies outside radius
  637. objectsExpand.push( object );
  638. objectsExpandOctants.push( indexOctant );
  639. } else {
  640. // lies across bounds between octants
  641. objectsRemaining.push( object );
  642. }
  643. }
  644. // if has objects to split
  645. if ( objectsSplit.length > 0 ) {
  646. objectsRemaining = objectsRemaining.concat( this.split( objectsSplit, objectsSplitOctants ) );
  647. }
  648. // if has objects to expand
  649. if ( objectsExpand.length > 0 ) {
  650. objectsRemaining = objectsRemaining.concat( this.expand( objectsExpand, objectsExpandOctants ) );
  651. }
  652. // store remaining
  653. this.objects = objectsRemaining;
  654. // merge check
  655. this.checkMerge();
  656. },
  657. split: function ( objects, octants ) {
  658. var i, l,
  659. indexOctant,
  660. object,
  661. node,
  662. objectsRemaining;
  663. // if not at max depth
  664. if ( this.depth < this.tree.depthMax ) {
  665. objects = objects || this.objects;
  666. octants = octants || [];
  667. objectsRemaining = [];
  668. // for each object
  669. for ( i = 0, l = objects.length; i < l; i ++ ) {
  670. object = objects[ i ];
  671. // get object octant index
  672. indexOctant = octants[ i ];
  673. // if object contained by octant, branch this tree
  674. if ( indexOctant > - 1 ) {
  675. node = this.branch( indexOctant );
  676. node.addObject( object );
  677. } else {
  678. objectsRemaining.push( object );
  679. }
  680. }
  681. // if all objects, set remaining as new objects
  682. if ( objects === this.objects ) {
  683. this.objects = objectsRemaining;
  684. }
  685. } else {
  686. objectsRemaining = this.objects;
  687. }
  688. return objectsRemaining;
  689. },
  690. branch: function ( indexOctant ) {
  691. var node,
  692. overlap,
  693. radius,
  694. radiusOffset,
  695. offset,
  696. position;
  697. // node exists
  698. if ( this.nodesByIndex[ indexOctant ] instanceof THREE.OctreeNode ) {
  699. node = this.nodesByIndex[ indexOctant ];
  700. } else {
  701. // properties
  702. radius = ( this.radiusOverlap ) * 0.5;
  703. overlap = radius * this.tree.overlapPct;
  704. radiusOffset = radius - overlap;
  705. offset = this.utilVec31Branch.set( indexOctant & 1 ? radiusOffset : - radiusOffset, indexOctant & 2 ? radiusOffset : - radiusOffset, indexOctant & 4 ? radiusOffset : - radiusOffset );
  706. position = new THREE.Vector3().addVectors( this.position, offset );
  707. // node
  708. node = new THREE.OctreeNode( {
  709. tree: this.tree,
  710. parent: this,
  711. position: position,
  712. radius: radius,
  713. indexOctant: indexOctant
  714. } );
  715. // store
  716. this.addNode( node, indexOctant );
  717. }
  718. return node;
  719. },
  720. expand: function ( objects, octants ) {
  721. var i, l,
  722. object,
  723. objectsRemaining,
  724. objectsExpand,
  725. indexOctant,
  726. flagsOutside,
  727. indexOutside,
  728. indexOctantInverse,
  729. iom = this.tree.INDEX_OUTSIDE_MAP,
  730. indexOutsideCounts,
  731. infoIndexOutside1,
  732. infoIndexOutside2,
  733. infoIndexOutside3,
  734. indexOutsideBitwise1,
  735. indexOutsideBitwise2,
  736. infoPotential1,
  737. infoPotential2,
  738. infoPotential3,
  739. indexPotentialBitwise1,
  740. indexPotentialBitwise2,
  741. octantX, octantY, octantZ,
  742. overlap,
  743. radius,
  744. radiusOffset,
  745. radiusParent,
  746. overlapParent,
  747. offset = this.utilVec31Expand,
  748. position,
  749. parent;
  750. // handle max depth down tree
  751. if ( this.tree.root.getDepthEnd() < this.tree.depthMax ) {
  752. objects = objects || this.objects;
  753. octants = octants || [];
  754. objectsRemaining = [];
  755. objectsExpand = [];
  756. // reset counts
  757. for ( i = 0, l = iom.length; i < l; i ++ ) {
  758. iom[ i ].count = 0;
  759. }
  760. // for all outside objects, find outside octants containing most objects
  761. for ( i = 0, l = objects.length; i < l; i ++ ) {
  762. object = objects[ i ];
  763. // get object octant index
  764. indexOctant = octants[ i ];
  765. // if object outside this, include in calculations
  766. if ( indexOctant < - 1 ) {
  767. // convert octant index to outside flags
  768. flagsOutside = - indexOctant - this.tree.INDEX_OUTSIDE_OFFSET;
  769. // check against bitwise flags
  770. // x
  771. if ( flagsOutside & this.tree.FLAG_POS_X ) {
  772. iom[ this.tree.INDEX_OUTSIDE_POS_X ].count ++;
  773. } else if ( flagsOutside & this.tree.FLAG_NEG_X ) {
  774. iom[ this.tree.INDEX_OUTSIDE_NEG_X ].count ++;
  775. }
  776. // y
  777. if ( flagsOutside & this.tree.FLAG_POS_Y ) {
  778. iom[ this.tree.INDEX_OUTSIDE_POS_Y ].count ++;
  779. } else if ( flagsOutside & this.tree.FLAG_NEG_Y ) {
  780. iom[ this.tree.INDEX_OUTSIDE_NEG_Y ].count ++;
  781. }
  782. // z
  783. if ( flagsOutside & this.tree.FLAG_POS_Z ) {
  784. iom[ this.tree.INDEX_OUTSIDE_POS_Z ].count ++;
  785. } else if ( flagsOutside & this.tree.FLAG_NEG_Z ) {
  786. iom[ this.tree.INDEX_OUTSIDE_NEG_Z ].count ++;
  787. }
  788. // store in expand list
  789. objectsExpand.push( object );
  790. } else {
  791. objectsRemaining.push( object );
  792. }
  793. }
  794. // if objects to expand
  795. if ( objectsExpand.length > 0 ) {
  796. // shallow copy index outside map
  797. indexOutsideCounts = iom.slice( 0 );
  798. // sort outside index count so highest is first
  799. indexOutsideCounts.sort( function ( a, b ) {
  800. return b.count - a.count;
  801. } );
  802. // get highest outside indices
  803. // first is first
  804. infoIndexOutside1 = indexOutsideCounts[ 0 ];
  805. indexOutsideBitwise1 = infoIndexOutside1.index | 1;
  806. // second is ( one of next two bitwise OR 1 ) that is not opposite of ( first bitwise OR 1 )
  807. infoPotential1 = indexOutsideCounts[ 1 ];
  808. infoPotential2 = indexOutsideCounts[ 2 ];
  809. infoIndexOutside2 = ( infoPotential1.index | 1 ) !== indexOutsideBitwise1 ? infoPotential1 : infoPotential2;
  810. indexOutsideBitwise2 = infoIndexOutside2.index | 1;
  811. // third is ( one of next three bitwise OR 1 ) that is not opposite of ( first or second bitwise OR 1 )
  812. infoPotential1 = indexOutsideCounts[ 2 ];
  813. infoPotential2 = indexOutsideCounts[ 3 ];
  814. infoPotential3 = indexOutsideCounts[ 4 ];
  815. indexPotentialBitwise1 = infoPotential1.index | 1;
  816. indexPotentialBitwise2 = infoPotential2.index | 1;
  817. infoIndexOutside3 = indexPotentialBitwise1 !== indexOutsideBitwise1 && indexPotentialBitwise1 !== indexOutsideBitwise2 ? infoPotential1 : indexPotentialBitwise2 !== indexOutsideBitwise1 && indexPotentialBitwise2 !== indexOutsideBitwise2 ? infoPotential2 : infoPotential3;
  818. // get this octant normal based on outside octant indices
  819. octantX = infoIndexOutside1.x + infoIndexOutside2.x + infoIndexOutside3.x;
  820. octantY = infoIndexOutside1.y + infoIndexOutside2.y + infoIndexOutside3.y;
  821. octantZ = infoIndexOutside1.z + infoIndexOutside2.z + infoIndexOutside3.z;
  822. // get this octant indices based on octant normal
  823. indexOctant = this.getOctantIndexFromPosition( octantX, octantY, octantZ );
  824. indexOctantInverse = this.getOctantIndexFromPosition( - octantX, - octantY, - octantZ );
  825. // properties
  826. overlap = this.overlap;
  827. radius = this.radius;
  828. // radius of parent comes from reversing overlap of this, unless overlap percent is 0
  829. radiusParent = this.tree.overlapPct > 0 ? overlap / ( ( 0.5 * this.tree.overlapPct ) * ( 1 + this.tree.overlapPct ) ) : radius * 2;
  830. overlapParent = radiusParent * this.tree.overlapPct;
  831. // parent offset is difference between radius + overlap of parent and child
  832. radiusOffset = ( radiusParent + overlapParent ) - ( radius + overlap );
  833. offset.set( indexOctant & 1 ? radiusOffset : - radiusOffset, indexOctant & 2 ? radiusOffset : - radiusOffset, indexOctant & 4 ? radiusOffset : - radiusOffset );
  834. position = new THREE.Vector3().addVectors( this.position, offset );
  835. // parent
  836. parent = new THREE.OctreeNode( {
  837. tree: this.tree,
  838. position: position,
  839. radius: radiusParent
  840. } );
  841. // set self as node of parent
  842. parent.addNode( this, indexOctantInverse );
  843. // set parent as root
  844. this.tree.setRoot( parent );
  845. // add all expand objects to parent
  846. for ( i = 0, l = objectsExpand.length; i < l; i ++ ) {
  847. this.tree.root.addObject( objectsExpand[ i ] );
  848. }
  849. }
  850. // if all objects, set remaining as new objects
  851. if ( objects === this.objects ) {
  852. this.objects = objectsRemaining;
  853. }
  854. } else {
  855. objectsRemaining = objects;
  856. }
  857. return objectsRemaining;
  858. },
  859. shrink: function () {
  860. // merge check
  861. this.checkMerge();
  862. // contract check
  863. this.tree.root.checkContract();
  864. },
  865. checkMerge: function () {
  866. var nodeParent = this,
  867. nodeMerge;
  868. // traverse up tree as long as node + entire subtree's object count is under minimum
  869. while ( nodeParent.parent instanceof THREE.OctreeNode && nodeParent.getObjectCountEnd() < this.tree.objectsThreshold ) {
  870. nodeMerge = nodeParent;
  871. nodeParent = nodeParent.parent;
  872. }
  873. // if parent node is not this, merge entire subtree into merge node
  874. if ( nodeParent !== this ) {
  875. nodeParent.merge( nodeMerge );
  876. }
  877. },
  878. merge: function ( nodes ) {
  879. var i, l,
  880. j, k,
  881. node;
  882. // handle nodes
  883. nodes = toArray( nodes );
  884. for ( i = 0, l = nodes.length; i < l; i ++ ) {
  885. node = nodes[ i ];
  886. // gather node + all subtree objects
  887. this.addObjectWithoutCheck( node.getObjectsEnd() );
  888. // reset node + entire subtree
  889. node.reset( true, true );
  890. // remove node
  891. this.removeNode( node.indexOctant, node );
  892. }
  893. // merge check
  894. this.checkMerge();
  895. },
  896. checkContract: function () {
  897. var i, l,
  898. node,
  899. nodeObjectsCount,
  900. nodeHeaviest,
  901. nodeHeaviestObjectsCount,
  902. outsideHeaviestObjectsCount;
  903. // find node with highest object count
  904. if ( this.nodesIndices.length > 0 ) {
  905. nodeHeaviestObjectsCount = 0;
  906. outsideHeaviestObjectsCount = this.objects.length;
  907. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  908. node = this.nodesByIndex[ this.nodesIndices[ i ] ];
  909. nodeObjectsCount = node.getObjectCountEnd();
  910. outsideHeaviestObjectsCount += nodeObjectsCount;
  911. if ( nodeHeaviest instanceof THREE.OctreeNode === false || nodeObjectsCount > nodeHeaviestObjectsCount ) {
  912. nodeHeaviest = node;
  913. nodeHeaviestObjectsCount = nodeObjectsCount;
  914. }
  915. }
  916. // subtract heaviest count from outside count
  917. outsideHeaviestObjectsCount -= nodeHeaviestObjectsCount;
  918. // if should contract
  919. if ( outsideHeaviestObjectsCount < this.tree.objectsThreshold && nodeHeaviest instanceof THREE.OctreeNode ) {
  920. this.contract( nodeHeaviest );
  921. }
  922. }
  923. },
  924. contract: function ( nodeRoot ) {
  925. var i, l,
  926. node;
  927. // handle all nodes
  928. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  929. node = this.nodesByIndex[ this.nodesIndices[ i ] ];
  930. // if node is not new root
  931. if ( node !== nodeRoot ) {
  932. // add node + all subtree objects to root
  933. nodeRoot.addObjectWithoutCheck( node.getObjectsEnd() );
  934. // reset node + entire subtree
  935. node.reset( true, true );
  936. }
  937. }
  938. // add own objects to root
  939. nodeRoot.addObjectWithoutCheck( this.objects );
  940. // reset self
  941. this.reset( false, true );
  942. // set new root
  943. this.tree.setRoot( nodeRoot );
  944. // contract check on new root
  945. nodeRoot.checkContract();
  946. },
  947. getOctantIndex: function ( objectData ) {
  948. var i, l,
  949. positionObj,
  950. radiusObj,
  951. position = this.position,
  952. radiusOverlap = this.radiusOverlap,
  953. overlap = this.overlap,
  954. deltaX, deltaY, deltaZ,
  955. distX, distY, distZ,
  956. distance,
  957. indexOctant = 0;
  958. // handle type
  959. if ( objectData instanceof THREE.OctreeObjectData ) {
  960. radiusObj = objectData.radius;
  961. positionObj = objectData.position;
  962. // update object data position last
  963. objectData.positionLast.copy( positionObj );
  964. } else if ( objectData instanceof THREE.OctreeNode ) {
  965. positionObj = objectData.position;
  966. radiusObj = 0;
  967. }
  968. // find delta and distance
  969. deltaX = positionObj.x - position.x;
  970. deltaY = positionObj.y - position.y;
  971. deltaZ = positionObj.z - position.z;
  972. distX = Math.abs( deltaX );
  973. distY = Math.abs( deltaY );
  974. distZ = Math.abs( deltaZ );
  975. distance = Math.max( distX, distY, distZ );
  976. // if outside, use bitwise flags to indicate on which sides object is outside of
  977. if ( distance + radiusObj > radiusOverlap ) {
  978. // x
  979. if ( distX + radiusObj > radiusOverlap ) {
  980. indexOctant = indexOctant ^ ( deltaX > 0 ? this.tree.FLAG_POS_X : this.tree.FLAG_NEG_X );
  981. }
  982. // y
  983. if ( distY + radiusObj > radiusOverlap ) {
  984. indexOctant = indexOctant ^ ( deltaY > 0 ? this.tree.FLAG_POS_Y : this.tree.FLAG_NEG_Y );
  985. }
  986. // z
  987. if ( distZ + radiusObj > radiusOverlap ) {
  988. indexOctant = indexOctant ^ ( deltaZ > 0 ? this.tree.FLAG_POS_Z : this.tree.FLAG_NEG_Z );
  989. }
  990. objectData.indexOctant = - indexOctant - this.tree.INDEX_OUTSIDE_OFFSET;
  991. return objectData.indexOctant;
  992. }
  993. // return octant index from delta xyz
  994. if ( deltaX - radiusObj > - overlap ) {
  995. // x right
  996. indexOctant = indexOctant | 1;
  997. } else if ( ! ( deltaX + radiusObj < overlap ) ) {
  998. // x left
  999. objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS;
  1000. return objectData.indexOctant;
  1001. }
  1002. if ( deltaY - radiusObj > - overlap ) {
  1003. // y right
  1004. indexOctant = indexOctant | 2;
  1005. } else if ( ! ( deltaY + radiusObj < overlap ) ) {
  1006. // y left
  1007. objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS;
  1008. return objectData.indexOctant;
  1009. }
  1010. if ( deltaZ - radiusObj > - overlap ) {
  1011. // z right
  1012. indexOctant = indexOctant | 4;
  1013. } else if ( ! ( deltaZ + radiusObj < overlap ) ) {
  1014. // z left
  1015. objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS;
  1016. return objectData.indexOctant;
  1017. }
  1018. objectData.indexOctant = indexOctant;
  1019. return objectData.indexOctant;
  1020. },
  1021. getOctantIndexFromPosition: function ( x, y, z ) {
  1022. var indexOctant = 0;
  1023. if ( x > 0 ) {
  1024. indexOctant = indexOctant | 1;
  1025. }
  1026. if ( y > 0 ) {
  1027. indexOctant = indexOctant | 2;
  1028. }
  1029. if ( z > 0 ) {
  1030. indexOctant = indexOctant | 4;
  1031. }
  1032. return indexOctant;
  1033. },
  1034. search: function ( position, radius, objects, direction, directionPct ) {
  1035. var i, l,
  1036. node,
  1037. intersects;
  1038. // test intersects by parameters
  1039. if ( direction ) {
  1040. intersects = this.intersectRay( position, direction, radius, directionPct );
  1041. } else {
  1042. intersects = this.intersectSphere( position, radius );
  1043. }
  1044. // if intersects
  1045. if ( intersects === true ) {
  1046. // gather objects
  1047. objects = objects.concat( this.objects );
  1048. // search subtree
  1049. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  1050. node = this.nodesByIndex[ this.nodesIndices[ i ] ];
  1051. objects = node.search( position, radius, objects, direction );
  1052. }
  1053. }
  1054. return objects;
  1055. },
  1056. intersectSphere: function ( position, radius ) {
  1057. var distance = radius * radius,
  1058. px = position.x,
  1059. py = position.y,
  1060. pz = position.z;
  1061. if ( px < this.left ) {
  1062. distance -= Math.pow( px - this.left, 2 );
  1063. } else if ( px > this.right ) {
  1064. distance -= Math.pow( px - this.right, 2 );
  1065. }
  1066. if ( py < this.bottom ) {
  1067. distance -= Math.pow( py - this.bottom, 2 );
  1068. } else if ( py > this.top ) {
  1069. distance -= Math.pow( py - this.top, 2 );
  1070. }
  1071. if ( pz < this.back ) {
  1072. distance -= Math.pow( pz - this.back, 2 );
  1073. } else if ( pz > this.front ) {
  1074. distance -= Math.pow( pz - this.front, 2 );
  1075. }
  1076. return distance >= 0;
  1077. },
  1078. intersectRay: function ( origin, direction, distance, directionPct ) {
  1079. if ( typeof directionPct === 'undefined' ) {
  1080. directionPct = this.utilVec31Ray.set( 1, 1, 1 ).divide( direction );
  1081. }
  1082. var t1 = ( this.left - origin.x ) * directionPct.x,
  1083. t2 = ( this.right - origin.x ) * directionPct.x,
  1084. t3 = ( this.bottom - origin.y ) * directionPct.y,
  1085. t4 = ( this.top - origin.y ) * directionPct.y,
  1086. t5 = ( this.back - origin.z ) * directionPct.z,
  1087. t6 = ( this.front - origin.z ) * directionPct.z,
  1088. tmax = Math.min( Math.min( Math.max( t1, t2 ), Math.max( t3, t4 ) ), Math.max( t5, t6 ) ),
  1089. tmin;
  1090. // ray would intersect in reverse direction, i.e. this is behind ray
  1091. if ( tmax < 0 ) {
  1092. return false;
  1093. }
  1094. tmin = Math.max( Math.max( Math.min( t1, t2 ), Math.min( t3, t4 ) ), Math.min( t5, t6 ) );
  1095. // if tmin > tmax or tmin > ray distance, ray doesn't intersect AABB
  1096. if ( tmin > tmax || tmin > distance ) {
  1097. return false;
  1098. }
  1099. return true;
  1100. },
  1101. getDepthEnd: function ( depth ) {
  1102. var i, l,
  1103. node;
  1104. if ( this.nodesIndices.length > 0 ) {
  1105. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  1106. node = this.nodesByIndex[ this.nodesIndices[ i ] ];
  1107. depth = node.getDepthEnd( depth );
  1108. }
  1109. } else {
  1110. depth = ! depth || this.depth > depth ? this.depth : depth;
  1111. }
  1112. return depth;
  1113. },
  1114. getNodeCountEnd: function () {
  1115. return this.tree.root.getNodeCountRecursive() + 1;
  1116. },
  1117. getNodeCountRecursive: function () {
  1118. var i, l,
  1119. count = this.nodesIndices.length;
  1120. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  1121. count += this.nodesByIndex[ this.nodesIndices[ i ] ].getNodeCountRecursive();
  1122. }
  1123. return count;
  1124. },
  1125. getObjectsEnd: function ( objects ) {
  1126. var i, l,
  1127. node;
  1128. objects = ( objects || [] ).concat( this.objects );
  1129. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  1130. node = this.nodesByIndex[ this.nodesIndices[ i ] ];
  1131. objects = node.getObjectsEnd( objects );
  1132. }
  1133. return objects;
  1134. },
  1135. getObjectCountEnd: function () {
  1136. var i, l,
  1137. count = this.objects.length;
  1138. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  1139. count += this.nodesByIndex[ this.nodesIndices[ i ] ].getObjectCountEnd();
  1140. }
  1141. return count;
  1142. },
  1143. getObjectCountStart: function () {
  1144. var count = this.objects.length,
  1145. parent = this.parent;
  1146. while ( parent instanceof THREE.OctreeNode ) {
  1147. count += parent.objects.length;
  1148. parent = parent.parent;
  1149. }
  1150. return count;
  1151. },
  1152. toConsole: function ( space ) {
  1153. var i, l,
  1154. node,
  1155. spaceAddition = ' ';
  1156. space = typeof space === 'string' ? space : spaceAddition;
  1157. console.log( ( this.parent ? space + ' octree NODE > ' : ' octree ROOT > ' ), this, ' // id: ', this.id, ' // indexOctant: ', this.indexOctant, ' // position: ', this.position.x, this.position.y, this.position.z, ' // radius: ', this.radius, ' // depth: ', this.depth );
  1158. console.log( ( this.parent ? space + ' ' : ' ' ), '+ objects ( ', this.objects.length, ' ) ', this.objects );
  1159. console.log( ( this.parent ? space + ' ' : ' ' ), '+ children ( ', this.nodesIndices.length, ' )', this.nodesIndices, this.nodesByIndex );
  1160. for ( i = 0, l = this.nodesIndices.length; i < l; i ++ ) {
  1161. node = this.nodesByIndex[ this.nodesIndices[ i ] ];
  1162. node.toConsole( space + spaceAddition );
  1163. }
  1164. }
  1165. };
  1166. /*===================================================
  1167. raycaster additional functionality
  1168. =====================================================*/
  1169. THREE.Raycaster.prototype.intersectOctreeObject = function ( object, recursive ) {
  1170. var intersects,
  1171. octreeObject,
  1172. facesAll,
  1173. facesSearch;
  1174. if ( object.object instanceof THREE.Object3D ) {
  1175. octreeObject = object;
  1176. object = octreeObject.object;
  1177. // temporarily replace object geometry's faces with octree object faces
  1178. facesSearch = octreeObject.faces;
  1179. facesAll = object.geometry.faces;
  1180. if ( facesSearch.length > 0 ) {
  1181. object.geometry.faces = facesSearch;
  1182. }
  1183. // intersect
  1184. intersects = this.intersectObject( object, recursive );
  1185. // revert object geometry's faces
  1186. if ( facesSearch.length > 0 ) {
  1187. object.geometry.faces = facesAll;
  1188. }
  1189. } else {
  1190. intersects = this.intersectObject( object, recursive );
  1191. }
  1192. return intersects;
  1193. };
  1194. THREE.Raycaster.prototype.intersectOctreeObjects = function ( objects, recursive ) {
  1195. var i, il,
  1196. intersects = [];
  1197. for ( i = 0, il = objects.length; i < il; i ++ ) {
  1198. intersects = intersects.concat( this.intersectOctreeObject( objects[ i ], recursive ) );
  1199. }
  1200. return intersects;
  1201. };
  1202. }( THREE ) );