Octree.js 45 KB

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