123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139 |
- /*!
- *
- * threeoctree.js (r60) / https://github.com/collinhover/threeoctree
- * (sparse) dynamic 3D spatial representation structure for fast searches.
- *
- * @author Collin Hover / http://collinhover.com/
- * based on Dynamic Octree by Piko3D @ http://www.piko3d.com/ and Octree by Marek Pawlowski @ pawlowski.it
- *
- */
- ( function ( THREE ) { "use strict";
-
- /*===================================================
- utility
- =====================================================*/
-
- function isNumber ( n ) {
- return !isNaN( n ) && isFinite( n );
- }
-
- function isArray ( target ) {
- return Object.prototype.toString.call( target ) === '[object Array]';
- }
-
- function toArray ( target ) {
- return target ? ( isArray ( target ) !== true ? [ target ] : target ) : [];
- }
-
- function indexOfValue( array, value ) {
-
- for ( var i = 0, il = array.length; i < il; i++ ) {
-
- if ( array[ i ] === value ) {
-
- return i;
-
- }
-
- }
-
- return -1;
-
- }
-
- function indexOfPropertyWithValue( array, property, value ) {
-
- for ( var i = 0, il = array.length; i < il; i++ ) {
-
- if ( array[ i ][ property ] === value ) {
-
- return i;
-
- }
-
- }
-
- return -1;
-
- }
- /*===================================================
- octree
- =====================================================*/
- THREE.Octree = function ( parameters ) {
-
- // handle parameters
-
- parameters = parameters || {};
-
- parameters.tree = this;
-
- // static properties ( modification is not recommended )
-
- this.nodeCount = 0;
-
- this.INDEX_INSIDE_CROSS = -1;
- this.INDEX_OUTSIDE_OFFSET = 2;
-
- this.INDEX_OUTSIDE_POS_X = isNumber( parameters.INDEX_OUTSIDE_POS_X ) ? parameters.INDEX_OUTSIDE_POS_X : 0;
- this.INDEX_OUTSIDE_NEG_X = isNumber( parameters.INDEX_OUTSIDE_NEG_X ) ? parameters.INDEX_OUTSIDE_NEG_X : 1;
- this.INDEX_OUTSIDE_POS_Y = isNumber( parameters.INDEX_OUTSIDE_POS_Y ) ? parameters.INDEX_OUTSIDE_POS_Y : 2;
- this.INDEX_OUTSIDE_NEG_Y = isNumber( parameters.INDEX_OUTSIDE_NEG_Y ) ? parameters.INDEX_OUTSIDE_NEG_Y : 3;
- this.INDEX_OUTSIDE_POS_Z = isNumber( parameters.INDEX_OUTSIDE_POS_Z ) ? parameters.INDEX_OUTSIDE_POS_Z : 4;
- this.INDEX_OUTSIDE_NEG_Z = isNumber( parameters.INDEX_OUTSIDE_NEG_Z ) ? parameters.INDEX_OUTSIDE_NEG_Z : 5;
-
- this.INDEX_OUTSIDE_MAP = [];
- this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_X ] = { index: this.INDEX_OUTSIDE_POS_X, count: 0, x: 1, y: 0, z: 0 };
- this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_X ] = { index: this.INDEX_OUTSIDE_NEG_X, count: 0, x: -1, y: 0, z: 0 };
- this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_Y ] = { index: this.INDEX_OUTSIDE_POS_Y, count: 0, x: 0, y: 1, z: 0 };
- this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_Y ] = { index: this.INDEX_OUTSIDE_NEG_Y, count: 0, x: 0, y: -1, z: 0 };
- this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_POS_Z ] = { index: this.INDEX_OUTSIDE_POS_Z, count: 0, x: 0, y: 0, z: 1 };
- this.INDEX_OUTSIDE_MAP[ this.INDEX_OUTSIDE_NEG_Z ] = { index: this.INDEX_OUTSIDE_NEG_Z, count: 0, x: 0, y: 0, z: -1 };
-
- this.FLAG_POS_X = 1 << ( this.INDEX_OUTSIDE_POS_X + 1 );
- this.FLAG_NEG_X = 1 << ( this.INDEX_OUTSIDE_NEG_X + 1 );
- this.FLAG_POS_Y = 1 << ( this.INDEX_OUTSIDE_POS_Y + 1 );
- this.FLAG_NEG_Y = 1 << ( this.INDEX_OUTSIDE_NEG_Y + 1 );
- this.FLAG_POS_Z = 1 << ( this.INDEX_OUTSIDE_POS_Z + 1 );
- this.FLAG_NEG_Z = 1 << ( this.INDEX_OUTSIDE_NEG_Z + 1 );
-
- this.utilVec31Search = new THREE.Vector3();
- this.utilVec32Search = new THREE.Vector3();
-
- // pass scene to see octree structure
-
- this.scene = parameters.scene;
-
- if ( this.scene ) {
-
- this.visualGeometry = new THREE.CubeGeometry( 1, 1, 1 );
- this.visualMaterial = new THREE.MeshBasicMaterial( { color: 0xFF0066, wireframe: true, wireframeLinewidth: 1 } );
-
- }
-
- // properties
-
- this.objects = [];
- this.objectsMap = {};
- this.objectsData = [];
- this.objectsDeferred = [];
-
- this.depthMax = isNumber( parameters.depthMax ) ? parameters.depthMax : Infinity;
- this.objectsThreshold = isNumber( parameters.objectsThreshold ) ? parameters.objectsThreshold : 8;
- this.overlapPct = isNumber( parameters.overlapPct ) ? parameters.overlapPct : 0.15;
- this.undeferred = parameters.undeferred || false;
-
- this.root = parameters.root instanceof THREE.OctreeNode ? parameters.root : new THREE.OctreeNode( parameters );
-
- };
- THREE.Octree.prototype = {
-
- update: function () {
-
- // add any deferred objects that were waiting for render cycle
-
- if ( this.objectsDeferred.length > 0 ) {
-
- for ( var i = 0, il = this.objectsDeferred.length; i < il; i++ ) {
-
- var deferred = this.objectsDeferred[ i ];
-
- this.addDeferred( deferred.object, deferred.options );
-
- }
-
- this.objectsDeferred.length = 0;
-
- }
-
- },
-
- add: function ( object, options ) {
-
- // add immediately
-
- if ( this.undeferred ) {
-
- this.updateObject( object );
-
- this.addDeferred( object, options );
-
- } else {
-
- // defer add until update called
-
- this.objectsDeferred.push( { object: object, options: options } );
-
- }
-
- },
-
- addDeferred: function ( object, options ) {
-
- var i, l,
- geometry,
- faces,
- useFaces,
- vertices,
- useVertices,
- objectData;
-
- // ensure object is not object data
-
- if ( object instanceof THREE.OctreeObjectData ) {
-
- object = object.object;
-
- }
-
- // check uuid to avoid duplicates
-
- if ( !object.uuid ) {
-
- object.uuid = THREE.Math.generateUUID();
-
- }
-
- if ( !this.objectsMap[ object.uuid ] ) {
-
- // store
-
- this.objects.push( object );
- this.objectsMap[ object.uuid ] = object;
-
- // check options
-
- if ( options ) {
-
- useFaces = options.useFaces;
- useVertices = options.useVertices;
-
- }
-
- if ( useVertices === true ) {
-
- geometry = object.geometry;
- vertices = geometry.vertices;
-
- for ( i = 0, l = vertices.length; i < l; i++ ) {
-
- this.addObjectData( object, vertices[ i ] );
-
- }
-
- } else if ( useFaces === true ) {
-
- geometry = object.geometry;
- faces = geometry.faces;
-
- for ( i = 0, l = faces.length; i < l; i++ ) {
-
- this.addObjectData( object, faces[ i ] );
-
- }
-
- } else {
-
- this.addObjectData( object );
-
- }
-
- }
-
- },
-
- addObjectData: function ( object, part ) {
-
- var objectData = new THREE.OctreeObjectData( object, part );
-
- // add to tree objects data list
-
- this.objectsData.push( objectData );
-
- // add to nodes
-
- this.root.addObject( objectData );
-
- },
-
- remove: function ( object ) {
-
- var i, l,
- objectData = object,
- index,
- objectsDataRemoved;
-
- // ensure object is not object data for index search
-
- if ( object instanceof THREE.OctreeObjectData ) {
-
- object = object.object;
-
- }
-
- // check uuid
-
- if ( this.objectsMap[ object.uuid ] ) {
-
- this.objectsMap[ object.uuid ] = undefined;
-
- // check and remove from objects, nodes, and data lists
-
- index = indexOfValue( this.objects, object );
-
- if ( index !== -1 ) {
-
- this.objects.splice( index, 1 );
-
- // remove from nodes
-
- objectsDataRemoved = this.root.removeObject( objectData );
-
- // remove from objects data list
-
- for ( i = 0, l = objectsDataRemoved.length; i < l; i++ ) {
-
- objectData = objectsDataRemoved[ i ];
-
- index = indexOfValue( this.objectsData, objectData );
-
- if ( index !== -1 ) {
-
- this.objectsData.splice( index, 1 );
-
- }
-
- }
-
- }
-
- } else if ( this.objectsDeferred.length > 0 ) {
-
- // check and remove from deferred
-
- index = indexOfPropertyWithValue( this.objectsDeferred, 'object', object );
-
- if ( index !== -1 ) {
-
- this.objectsDeferred.splice( index, 1 );
-
- }
-
- }
-
- },
-
- extend: function ( octree ) {
-
- var i, l,
- objectsData,
- objectData;
-
- if ( octree instanceof THREE.Octree ) {
-
- // for each object data
-
- objectsData = octree.objectsData;
-
- for ( i = 0, l = objectsData.length; i < l; i++ ) {
-
- objectData = objectsData[ i ];
-
- this.add( objectData, { useFaces: objectData.faces, useVertices: objectData.vertices } );
-
- }
-
- }
-
- },
-
- rebuild: function () {
-
- var i, l,
- node,
- object,
- objectData,
- indexOctant,
- indexOctantLast,
- objectsUpdate = [];
-
- // check all object data for changes in position
- // assumes all object matrices are up to date
-
- for ( i = 0, l = this.objectsData.length; i < l; i++ ) {
-
- objectData = this.objectsData[ i ];
-
- node = objectData.node;
-
- // update object
-
- objectData.update();
-
- // if position has changed since last organization of object in tree
-
- if ( node instanceof THREE.OctreeNode && !objectData.positionLast.equals( objectData.position ) ) {
-
- // get octant index of object within current node
-
- indexOctantLast = objectData.indexOctant;
-
- indexOctant = node.getOctantIndex( objectData );
-
- // if object octant index has changed
-
- if ( indexOctant !== indexOctantLast ) {
-
- // add to update list
-
- objectsUpdate.push( objectData );
-
- }
-
- }
-
- }
-
- // update changed objects
-
- for ( i = 0, l = objectsUpdate.length; i < l; i++ ) {
-
- objectData = objectsUpdate[ i ];
-
- // remove object from current node
-
- objectData.node.removeObject( objectData );
-
- // add object to tree root
-
- this.root.addObject( objectData );
-
- }
-
- },
-
- updateObject: function ( object ) {
-
- var i, l,
- parentCascade = [ object ],
- parent,
- parentUpdate;
-
- // search all parents between object and root for world matrix update
-
- parent = object.parent;
-
- while( parent ) {
-
- parentCascade.push( parent );
- parent = parent.parent;
-
- }
-
- for ( i = 0, l = parentCascade.length; i < l; i++ ) {
-
- parent = parentCascade[ i ];
-
- if ( parent.matrixWorldNeedsUpdate === true ) {
-
- parentUpdate = parent;
-
- }
-
- }
-
- // update world matrix starting at uppermost parent that needs update
-
- if ( typeof parentUpdate !== 'undefined' ) {
-
- parentUpdate.updateMatrixWorld();
-
- }
-
- },
-
- search: function ( position, radius, organizeByObject, direction ) {
-
- var i, l,
- node,
- objects,
- objectData,
- object,
- results,
- resultData,
- resultsObjectsIndices,
- resultObjectIndex,
- directionPct;
-
- // add root objects
-
- objects = [].concat( this.root.objects );
-
- // ensure radius (i.e. distance of ray) is a number
-
- if ( !( radius > 0 ) ) {
-
- radius = Number.MAX_VALUE;
-
- }
-
- // if direction passed, normalize and find pct
-
- if ( direction instanceof THREE.Vector3 ) {
-
- direction = this.utilVec31Search.copy( direction ).normalize();
- directionPct = this.utilVec32Search.set( 1, 1, 1 ).divide( direction );
-
- }
-
- // search each node of root
-
- for ( i = 0, l = this.root.nodesIndices.length; i < l; i++ ) {
-
- node = this.root.nodesByIndex[ this.root.nodesIndices[ i ] ];
-
- objects = node.search( position, radius, objects, direction, directionPct );
-
- }
-
- // if should organize results by object
-
- if ( organizeByObject === true ) {
-
- results = [];
- resultsObjectsIndices = [];
-
- // for each object data found
-
- for ( i = 0, l = objects.length; i < l; i++ ) {
-
- objectData = objects[ i ];
- object = objectData.object;
-
- resultObjectIndex = indexOfValue( resultsObjectsIndices, object );
-
- // if needed, create new result data
-
- if ( resultObjectIndex === -1 ) {
-
- resultData = {
- object: object,
- faces: [],
- vertices: []
- };
-
- results.push( resultData );
-
- resultsObjectsIndices.push( object );
-
- } else {
-
- resultData = results[ resultObjectIndex ];
-
- }
-
- // object data has faces or vertices, add to list
-
- if ( objectData.faces ) {
-
- resultData.faces.push( objectData.faces );
-
- } else if ( objectData.vertices ) {
-
- resultData.vertices.push( objectData.vertices );
-
- }
-
- }
-
- } else {
-
- results = objects;
-
- }
-
- return results;
-
- },
-
- setRoot: function ( root ) {
-
- if ( root instanceof THREE.OctreeNode ) {
-
- // store new root
-
- this.root = root;
-
- // update properties
-
- this.root.updateProperties();
-
- }
-
- },
-
- getDepthEnd: function () {
-
- return this.root.getDepthEnd();
-
- },
-
- getNodeCountEnd: function () {
-
- return this.root.getNodeCountEnd();
-
- },
-
- getObjectCountEnd: function () {
-
- return this.root.getObjectCountEnd();
-
- },
-
- toConsole: function () {
-
- this.root.toConsole();
-
- }
-
- };
- /*===================================================
- object data
- =====================================================*/
- THREE.OctreeObjectData = function ( object, part ) {
-
- // properties
-
- this.object = object;
-
- // handle part by type
-
- if ( part instanceof THREE.Face3 ) {
-
- this.faces = part;
- this.face3 = true;
- this.utilVec31FaceBounds = new THREE.Vector3();
-
- } else if ( part instanceof THREE.Face4 ) {
-
- this.face4 = true;
- this.faces = part;
- this.utilVec31FaceBounds = new THREE.Vector3();
-
- } else if ( part instanceof THREE.Vector3 ) {
-
- this.vertices = part;
-
- }
-
- this.radius = 0;
- this.position = new THREE.Vector3();
-
- // initial update
-
- if ( this.object instanceof THREE.Object3D ) {
-
- this.update();
-
- }
-
- this.positionLast = this.position.clone();
-
- };
- THREE.OctreeObjectData.prototype = {
-
- update: function () {
-
- if ( this.face3 ) {
-
- this.radius = this.getFace3BoundingRadius( this.object, this.faces );
- this.position.copy( this.faces.centroid ).applyMatrix4( this.object.matrixWorld );
-
- } else if ( this.face4 ) {
-
- this.radius = this.getFace4BoundingRadius( this.object, this.faces );
- this.position.copy( this.faces.centroid ).applyMatrix4( this.object.matrixWorld );
-
- } else if ( this.vertices ) {
-
- this.radius = this.object.material.size || 1;
- this.position.copy( this.vertices ).applyMatrix4( this.object.matrixWorld );
-
- } else {
-
- if ( this.object.geometry ) {
-
- if ( this.object.geometry.boundingSphere === null ) {
-
- this.object.geometry.computeBoundingSphere();
-
- }
-
- this.radius = this.object.geometry.boundingSphere.radius;
- this.position.copy( this.object.geometry.boundingSphere.center ).applyMatrix4( this.object.matrixWorld );
-
- } else {
-
- this.radius = this.object.boundRadius;
- this.position.setFromMatrixPosition( this.object.matrixWorld );
-
- }
-
- }
-
- this.radius = this.radius * Math.max( this.object.scale.x, this.object.scale.y, this.object.scale.z );
-
- },
-
- getFace3BoundingRadius: function ( object, face ) {
-
- var geometry = object.geometry || object,
- vertices = geometry.vertices,
- centroid = face.centroid,
- va = vertices[ face.a ], vb = vertices[ face.b ], vc = vertices[ face.c ],
- centroidToVert = this.utilVec31FaceBounds,
- radius;
-
- centroid.addVectors( va, vb ).add( vc ).divideScalar( 3 );
- radius = Math.max( centroidToVert.subVectors( centroid, va ).length(), centroidToVert.subVectors( centroid, vb ).length(), centroidToVert.subVectors( centroid, vc ).length() );
-
- return radius;
-
- },
-
- getFace4BoundingRadius: function ( object, face ) {
-
- var geometry = object.geometry || object,
- vertices = geometry.vertices,
- centroid = face.centroid,
- va = vertices[ face.a ], vb = vertices[ face.b ], vc = vertices[ face.c ], vd = vertices[ face.d ],
- centroidToVert = this.utilVec31FaceBounds,
- radius;
-
- centroid.addVectors( va, vb ).add( vc ).add( vd ).divideScalar( 4 );
- radius = Math.max( centroidToVert.subVectors( centroid, va ).length(), centroidToVert.subVectors( centroid, vb ).length(), centroidToVert.subVectors( centroid, vc ).length(), centroidToVert.subVectors( centroid, vd ).length() );
-
- return radius;
-
- }
-
- };
- /*===================================================
- node
- =====================================================*/
- THREE.OctreeNode = function ( parameters ) {
-
- // utility
-
- this.utilVec31Branch = new THREE.Vector3();
- this.utilVec31Expand = new THREE.Vector3();
- this.utilVec31Ray = new THREE.Vector3();
-
- // handle parameters
-
- parameters = parameters || {};
-
- // store or create tree
-
- if ( parameters.tree instanceof THREE.Octree ) {
-
- this.tree = parameters.tree;
-
- } else if ( parameters.parent instanceof THREE.OctreeNode !== true ) {
-
- parameters.root = this;
-
- this.tree = new THREE.Octree( parameters );
-
- }
-
- // basic properties
-
- this.id = this.tree.nodeCount++;
- this.position = parameters.position instanceof THREE.Vector3 ? parameters.position : new THREE.Vector3();
- this.radius = parameters.radius > 0 ? parameters.radius : 1;
- this.indexOctant = parameters.indexOctant;
- this.depth = 0;
-
- // reset and assign parent
-
- this.reset();
- this.setParent( parameters.parent );
-
- // additional properties
-
- this.overlap = this.radius * this.tree.overlapPct;
- this.radiusOverlap = this.radius + this.overlap;
- this.left = this.position.x - this.radiusOverlap;
- this.right = this.position.x + this.radiusOverlap;
- this.bottom = this.position.y - this.radiusOverlap;
- this.top = this.position.y + this.radiusOverlap;
- this.back = this.position.z - this.radiusOverlap;
- this.front = this.position.z + this.radiusOverlap;
-
- // visual
-
- if ( this.tree.scene ) {
-
- this.visual = new THREE.Mesh( this.tree.visualGeometry, this.tree.visualMaterial );
- this.visual.scale.set( this.radiusOverlap * 2, this.radiusOverlap * 2, this.radiusOverlap * 2 );
- this.visual.position.copy( this.position );
- this.tree.scene.add( this.visual );
-
- }
-
- };
- THREE.OctreeNode.prototype = {
-
- setParent: function ( parent ) {
-
- // store new parent
-
- if ( parent !== this && this.parent !== parent ) {
-
- this.parent = parent;
-
- // update properties
-
- this.updateProperties();
-
- }
-
- },
-
- updateProperties: function () {
-
- var i, l;
-
- // properties
-
- if ( this.parent instanceof THREE.OctreeNode ) {
-
- this.tree = this.parent.tree;
- this.depth = this.parent.depth + 1;
-
- } else {
-
- this.depth = 0;
-
- }
-
- // cascade
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- this.nodesByIndex[ this.nodesIndices[ i ] ].updateProperties();
-
- }
-
- },
-
- reset: function ( cascade, removeVisual ) {
-
- var i, l,
- node,
- nodesIndices = this.nodesIndices || [],
- nodesByIndex = this.nodesByIndex;
-
- this.objects = [];
- this.nodesIndices = [];
- this.nodesByIndex = {};
-
- // unset parent in nodes
-
- for ( i = 0, l = nodesIndices.length; i < l; i++ ) {
-
- node = nodesByIndex[ nodesIndices[ i ] ];
-
- node.setParent( undefined );
-
- if ( cascade === true ) {
-
- node.reset( cascade, removeVisual );
-
- }
-
- }
-
- // visual
-
- if ( removeVisual === true && this.visual && this.visual.parent ) {
-
- this.visual.parent.remove( this.visual );
-
- }
-
- },
-
- addNode: function ( node, indexOctant ) {
-
- node.indexOctant = indexOctant;
-
- if ( indexOfValue( this.nodesIndices, indexOctant ) === -1 ) {
-
- this.nodesIndices.push( indexOctant );
-
- }
-
- this.nodesByIndex[ indexOctant ] = node;
-
- if ( node.parent !== this ) {
-
- node.setParent( this );
-
- }
-
- },
-
- removeNode: function ( indexOctant ) {
-
- var index,
- node;
-
- index = indexOfValue( this.nodesIndices, indexOctant );
-
- this.nodesIndices.splice( index, 1 );
-
- node = node || this.nodesByIndex[ indexOctant ];
-
- delete this.nodesByIndex[ indexOctant ];
-
- if ( node.parent === this ) {
-
- node.setParent( undefined );
-
- }
-
- },
-
- addObject: function ( object ) {
-
- var index,
- indexOctant,
- node;
-
- // get object octant index
-
- indexOctant = this.getOctantIndex( object );
-
- // if object fully contained by an octant, add to subtree
- if ( indexOctant > -1 && this.nodesIndices.length > 0 ) {
-
- node = this.branch( indexOctant );
-
- node.addObject( object );
-
- } else if ( indexOctant < -1 && this.parent instanceof THREE.OctreeNode ) {
-
- // if object lies outside bounds, add to parent node
-
- this.parent.addObject( object );
-
- } else {
-
- // add to this objects list
-
- index = indexOfValue( this.objects, object );
-
- if ( index === -1 ) {
-
- this.objects.push( object );
-
- }
-
- // node reference
-
- object.node = this;
-
- // check if need to expand, split, or both
-
- this.checkGrow();
-
- }
-
- },
-
- addObjectWithoutCheck: function ( objects ) {
-
- var i, l,
- object;
- for ( i = 0, l = objects.length; i < l; i++ ) {
-
- object = objects[ i ];
-
- this.objects.push( object );
-
- object.node = this;
-
- }
-
- },
-
- removeObject: function ( object ) {
-
- var i, l,
- nodesRemovedFrom,
- removeData;
-
- // cascade through tree to find and remove object
-
- removeData = this.removeObjectRecursive( object, { searchComplete: false, nodesRemovedFrom: [], objectsDataRemoved: [] } );
-
- // if object removed, try to shrink the nodes it was removed from
-
- nodesRemovedFrom = removeData.nodesRemovedFrom;
-
- if ( nodesRemovedFrom.length > 0 ) {
-
- for ( i = 0, l = nodesRemovedFrom.length; i < l; i++ ) {
-
- nodesRemovedFrom[ i ].shrink();
-
- }
-
- }
-
- return removeData.objectsDataRemoved;
-
- },
-
- removeObjectRecursive: function ( object, removeData ) {
-
- var i, l,
- index = -1,
- objectData,
- node,
- objectRemoved;
-
- // find index of object in objects list
-
- // search and remove object data (fast)
- if ( object instanceof THREE.OctreeObjectData ) {
-
- // remove from this objects list
-
- index = indexOfValue( this.objects, object );
-
- if ( index !== -1 ) {
-
- this.objects.splice( index, 1 );
- object.node = undefined;
-
- removeData.objectsDataRemoved.push( object );
-
- removeData.searchComplete = objectRemoved = true;
-
- }
-
- } else {
-
- // search each object data for object and remove (slow)
-
- for ( i = this.objects.length - 1; i >= 0; i-- ) {
-
- objectData = this.objects[ i ];
-
- if ( objectData.object === object ) {
-
- this.objects.splice( i, 1 );
- objectData.node = undefined;
-
- removeData.objectsDataRemoved.push( objectData );
-
- objectRemoved = true;
-
- if ( !objectData.faces && !objectData.vertices ) {
-
- removeData.searchComplete = true;
- break;
-
- }
-
- }
-
- }
-
- }
-
- // if object data removed and this is not on nodes removed from
-
- if ( objectRemoved === true ) {
-
- removeData.nodesRemovedFrom.push( this );
-
- }
-
- // if search not complete, search nodes
-
- if ( removeData.searchComplete !== true ) {
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- node = this.nodesByIndex[ this.nodesIndices[ i ] ];
-
- // try removing object from node
-
- removeData = node.removeObjectRecursive( object, removeData );
-
- if ( removeData.searchComplete === true ) {
-
- break;
-
- }
-
- }
-
- }
-
- return removeData;
-
- },
-
- checkGrow: function () {
-
- // if object count above max
-
- if ( this.objects.length > this.tree.objectsThreshold && this.tree.objectsThreshold > 0 ) {
-
- this.grow();
-
- }
-
- },
-
- grow: function () {
-
- var indexOctant,
- object,
- objectsExpand = [],
- objectsExpandOctants = [],
- objectsSplit = [],
- objectsSplitOctants = [],
- objectsRemaining = [],
- i, l;
-
- // for each object
-
- for ( i = 0, l = this.objects.length; i < l; i++ ) {
-
- object = this.objects[ i ];
-
- // get object octant index
-
- indexOctant = this.getOctantIndex( object );
-
- // if lies within octant
- if ( indexOctant > -1 ) {
-
- objectsSplit.push( object );
- objectsSplitOctants.push( indexOctant );
-
- } else if ( indexOctant < -1 ) {
-
- // lies outside radius
-
- objectsExpand.push( object );
- objectsExpandOctants.push( indexOctant );
-
- } else {
-
- // lies across bounds between octants
-
- objectsRemaining.push( object );
-
- }
-
- }
-
- // if has objects to split
-
- if ( objectsSplit.length > 0) {
-
- objectsRemaining = objectsRemaining.concat( this.split( objectsSplit, objectsSplitOctants ) );
-
- }
-
- // if has objects to expand
-
- if ( objectsExpand.length > 0) {
-
- objectsRemaining = objectsRemaining.concat( this.expand( objectsExpand, objectsExpandOctants ) );
-
- }
-
- // store remaining
-
- this.objects = objectsRemaining;
-
- // merge check
-
- this.checkMerge();
-
- },
-
- split: function ( objects, octants ) {
-
- var i, l,
- indexOctant,
- object,
- node,
- objectsRemaining;
-
- // if not at max depth
-
- if ( this.depth < this.tree.depthMax ) {
-
- objects = objects || this.objects;
-
- octants = octants || [];
-
- objectsRemaining = [];
-
- // for each object
-
- for ( i = 0, l = objects.length; i < l; i++ ) {
-
- object = objects[ i ];
-
- // get object octant index
-
- indexOctant = octants[ i ];
-
- // if object contained by octant, branch this tree
-
- if ( indexOctant > -1 ) {
-
- node = this.branch( indexOctant );
-
- node.addObject( object );
-
- } else {
-
- objectsRemaining.push( object );
-
- }
-
- }
-
- // if all objects, set remaining as new objects
-
- if ( objects === this.objects ) {
-
- this.objects = objectsRemaining;
-
- }
-
- } else {
-
- objectsRemaining = this.objects;
-
- }
-
- return objectsRemaining;
-
- },
-
- branch: function ( indexOctant ) {
-
- var node,
- overlap,
- radius,
- radiusOffset,
- offset,
- position;
-
- // node exists
-
- if ( this.nodesByIndex[ indexOctant ] instanceof THREE.OctreeNode ) {
-
- node = this.nodesByIndex[ indexOctant ];
-
- } else {
-
- // properties
-
- radius = ( this.radiusOverlap ) * 0.5;
- overlap = radius * this.tree.overlapPct;
- radiusOffset = radius - overlap;
- offset = this.utilVec31Branch.set( indexOctant & 1 ? radiusOffset : -radiusOffset, indexOctant & 2 ? radiusOffset : -radiusOffset, indexOctant & 4 ? radiusOffset : -radiusOffset );
- position = new THREE.Vector3().addVectors( this.position, offset );
-
- // node
-
- node = new THREE.OctreeNode( {
- tree: this.tree,
- parent: this,
- position: position,
- radius: radius,
- indexOctant: indexOctant
- } );
-
- // store
-
- this.addNode( node, indexOctant );
-
- }
-
- return node;
-
- },
-
- expand: function ( objects, octants ) {
-
- var i, l,
- object,
- objectsRemaining,
- objectsExpand,
- indexOctant,
- flagsOutside,
- indexOutside,
- indexOctantInverse,
- iom = this.tree.INDEX_OUTSIDE_MAP,
- indexOutsideCounts,
- infoIndexOutside1,
- infoIndexOutside2,
- infoIndexOutside3,
- indexOutsideBitwise1,
- indexOutsideBitwise2,
- infoPotential1,
- infoPotential2,
- infoPotential3,
- indexPotentialBitwise1,
- indexPotentialBitwise2,
- octantX, octantY, octantZ,
- overlap,
- radius,
- radiusOffset,
- radiusParent,
- overlapParent,
- offset = this.utilVec31Expand,
- position,
- parent;
-
- // handle max depth down tree
-
- if ( this.tree.root.getDepthEnd() < this.tree.depthMax ) {
-
- objects = objects || this.objects;
- octants = octants || [];
-
- objectsRemaining = [];
- objectsExpand = [];
-
- // reset counts
-
- for ( i = 0, l = iom.length; i < l; i++ ) {
-
- iom[ i ].count = 0;
-
- }
-
- // for all outside objects, find outside octants containing most objects
-
- for ( i = 0, l = objects.length; i < l; i++ ) {
-
- object = objects[ i ];
-
- // get object octant index
-
- indexOctant = octants[ i ] ;
-
- // if object outside this, include in calculations
-
- if ( indexOctant < -1 ) {
-
- // convert octant index to outside flags
-
- flagsOutside = -indexOctant - this.tree.INDEX_OUTSIDE_OFFSET;
-
- // check against bitwise flags
-
- // x
-
- if ( flagsOutside & this.tree.FLAG_POS_X ) {
-
- iom[ this.tree.INDEX_OUTSIDE_POS_X ].count++;
-
- } else if ( flagsOutside & this.tree.FLAG_NEG_X ) {
-
- iom[ this.tree.INDEX_OUTSIDE_NEG_X ].count++;
-
- }
-
- // y
-
- if ( flagsOutside & this.tree.FLAG_POS_Y ) {
-
- iom[ this.tree.INDEX_OUTSIDE_POS_Y ].count++;
-
- } else if ( flagsOutside & this.tree.FLAG_NEG_Y ) {
-
- iom[ this.tree.INDEX_OUTSIDE_NEG_Y ].count++;
-
- }
-
- // z
-
- if ( flagsOutside & this.tree.FLAG_POS_Z ) {
-
- iom[ this.tree.INDEX_OUTSIDE_POS_Z ].count++;
-
- } else if ( flagsOutside & this.tree.FLAG_NEG_Z ) {
-
- iom[ this.tree.INDEX_OUTSIDE_NEG_Z ].count++;
-
- }
-
- // store in expand list
-
- objectsExpand.push( object );
-
- } else {
-
- objectsRemaining.push( object );
-
- }
-
- }
-
- // if objects to expand
-
- if ( objectsExpand.length > 0 ) {
-
- // shallow copy index outside map
-
- indexOutsideCounts = iom.slice( 0 );
-
- // sort outside index count so highest is first
-
- indexOutsideCounts.sort( function ( a, b ) {
-
- return b.count - a.count;
-
- } );
-
- // get highest outside indices
-
- // first is first
- infoIndexOutside1 = indexOutsideCounts[ 0 ];
- indexOutsideBitwise1 = infoIndexOutside1.index | 1;
-
- // second is ( one of next two bitwise OR 1 ) that is not opposite of ( first bitwise OR 1 )
-
- infoPotential1 = indexOutsideCounts[ 1 ];
- infoPotential2 = indexOutsideCounts[ 2 ];
-
- infoIndexOutside2 = ( infoPotential1.index | 1 ) !== indexOutsideBitwise1 ? infoPotential1 : infoPotential2;
- indexOutsideBitwise2 = infoIndexOutside2.index | 1;
-
- // third is ( one of next three bitwise OR 1 ) that is not opposite of ( first or second bitwise OR 1 )
-
- infoPotential1 = indexOutsideCounts[ 2 ];
- infoPotential2 = indexOutsideCounts[ 3 ];
- infoPotential3 = indexOutsideCounts[ 4 ];
-
- indexPotentialBitwise1 = infoPotential1.index | 1;
- indexPotentialBitwise2 = infoPotential2.index | 1;
-
- infoIndexOutside3 = indexPotentialBitwise1 !== indexOutsideBitwise1 && indexPotentialBitwise1 !== indexOutsideBitwise2 ? infoPotential1 : indexPotentialBitwise2 !== indexOutsideBitwise1 && indexPotentialBitwise2 !== indexOutsideBitwise2 ? infoPotential2 : infoPotential3;
-
- // get this octant normal based on outside octant indices
-
- octantX = infoIndexOutside1.x + infoIndexOutside2.x + infoIndexOutside3.x;
- octantY = infoIndexOutside1.y + infoIndexOutside2.y + infoIndexOutside3.y;
- octantZ = infoIndexOutside1.z + infoIndexOutside2.z + infoIndexOutside3.z;
-
- // get this octant indices based on octant normal
-
- indexOctant = this.getOctantIndexFromPosition( octantX, octantY, octantZ );
- indexOctantInverse = this.getOctantIndexFromPosition( -octantX, -octantY, -octantZ );
-
- // properties
-
- overlap = this.overlap;
- radius = this.radius;
-
- // radius of parent comes from reversing overlap of this, unless overlap percent is 0
-
- radiusParent = this.tree.overlapPct > 0 ? overlap / ( ( 0.5 * this.tree.overlapPct ) * ( 1 + this.tree.overlapPct ) ) : radius * 2;
- overlapParent = radiusParent * this.tree.overlapPct;
-
- // parent offset is difference between radius + overlap of parent and child
-
- radiusOffset = ( radiusParent + overlapParent ) - ( radius + overlap );
- offset.set( indexOctant & 1 ? radiusOffset : -radiusOffset, indexOctant & 2 ? radiusOffset : -radiusOffset, indexOctant & 4 ? radiusOffset : -radiusOffset );
- position = new THREE.Vector3().addVectors( this.position, offset );
-
- // parent
-
- parent = new THREE.OctreeNode( {
- tree: this.tree,
- position: position,
- radius: radiusParent
- } );
-
- // set self as node of parent
-
- parent.addNode( this, indexOctantInverse );
-
- // set parent as root
-
- this.tree.setRoot( parent );
-
- // add all expand objects to parent
-
- for ( i = 0, l = objectsExpand.length; i < l; i++ ) {
-
- this.tree.root.addObject( objectsExpand[ i ] );
-
- }
-
- }
-
- // if all objects, set remaining as new objects
-
- if ( objects === this.objects ) {
-
- this.objects = objectsRemaining;
-
- }
-
- } else {
-
- objectsRemaining = objects;
-
- }
-
- return objectsRemaining;
-
- },
-
- shrink: function () {
-
- // merge check
-
- this.checkMerge();
-
- // contract check
-
- this.tree.root.checkContract();
-
- },
-
- checkMerge: function () {
-
- var nodeParent = this,
- nodeMerge;
-
- // traverse up tree as long as node + entire subtree's object count is under minimum
-
- while ( nodeParent.parent instanceof THREE.OctreeNode && nodeParent.getObjectCountEnd() < this.tree.objectsThreshold ) {
-
- nodeMerge = nodeParent;
- nodeParent = nodeParent.parent;
-
- }
-
- // if parent node is not this, merge entire subtree into merge node
-
- if ( nodeParent !== this ) {
-
- nodeParent.merge( nodeMerge );
-
- }
-
- },
-
- merge: function ( nodes ) {
-
- var i, l,
- j, k,
- node;
-
- // handle nodes
-
- nodes = toArray( nodes );
-
- for ( i = 0, l = nodes.length; i < l; i++ ) {
-
- node = nodes[ i ];
-
- // gather node + all subtree objects
-
- this.addObjectWithoutCheck( node.getObjectsEnd() );
-
- // reset node + entire subtree
-
- node.reset( true, true );
-
- // remove node
-
- this.removeNode( node.indexOctant, node );
-
- }
-
- // merge check
-
- this.checkMerge();
-
- },
-
- checkContract: function () {
-
- var i, l,
- node,
- nodeObjectsCount,
- nodeHeaviest,
- nodeHeaviestObjectsCount,
- outsideHeaviestObjectsCount;
-
- // find node with highest object count
-
- if ( this.nodesIndices.length > 0 ) {
-
- nodeHeaviestObjectsCount = 0;
- outsideHeaviestObjectsCount = this.objects.length;
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- node = this.nodesByIndex[ this.nodesIndices[ i ] ];
-
- nodeObjectsCount = node.getObjectCountEnd();
- outsideHeaviestObjectsCount += nodeObjectsCount;
-
- if ( nodeHeaviest instanceof THREE.OctreeNode === false || nodeObjectsCount > nodeHeaviestObjectsCount ) {
-
- nodeHeaviest = node;
- nodeHeaviestObjectsCount = nodeObjectsCount;
-
- }
-
- }
-
- // subtract heaviest count from outside count
-
- outsideHeaviestObjectsCount -= nodeHeaviestObjectsCount;
-
- // if should contract
-
- if ( outsideHeaviestObjectsCount < this.tree.objectsThreshold && nodeHeaviest instanceof THREE.OctreeNode ) {
-
- this.contract( nodeHeaviest );
-
- }
-
- }
-
- },
-
- contract: function ( nodeRoot ) {
-
- var i, l,
- node;
-
- // handle all nodes
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- node = this.nodesByIndex[ this.nodesIndices[ i ] ];
-
- // if node is not new root
-
- if ( node !== nodeRoot ) {
-
- // add node + all subtree objects to root
-
- nodeRoot.addObjectWithoutCheck( node.getObjectsEnd() );
-
- // reset node + entire subtree
-
- node.reset( true, true );
-
- }
-
- }
-
- // add own objects to root
-
- nodeRoot.addObjectWithoutCheck( this.objects );
-
- // reset self
-
- this.reset( false, true );
-
- // set new root
-
- this.tree.setRoot( nodeRoot );
-
- // contract check on new root
-
- nodeRoot.checkContract();
-
- },
-
- getOctantIndex: function ( objectData ) {
-
- var i, l,
- positionObj,
- radiusObj,
- position = this.position,
- radiusOverlap = this.radiusOverlap,
- overlap = this.overlap,
- deltaX, deltaY, deltaZ,
- distX, distY, distZ,
- distance,
- indexOctant = 0;
-
- // handle type
-
- if ( objectData instanceof THREE.OctreeObjectData ) {
-
- radiusObj = objectData.radius;
-
- positionObj = objectData.position;
-
- // update object data position last
-
- objectData.positionLast.copy( positionObj );
-
- } else if ( objectData instanceof THREE.OctreeNode ) {
-
- positionObj = objectData.position;
-
- radiusObj = 0;
-
- }
-
- // find delta and distance
-
- deltaX = positionObj.x - position.x;
- deltaY = positionObj.y - position.y;
- deltaZ = positionObj.z - position.z;
-
- distX = Math.abs( deltaX );
- distY = Math.abs( deltaY );
- distZ = Math.abs( deltaZ );
- distance = Math.max( distX, distY, distZ );
-
- // if outside, use bitwise flags to indicate on which sides object is outside of
-
- if ( distance + radiusObj > radiusOverlap ) {
-
- // x
-
- if ( distX + radiusObj > radiusOverlap ) {
-
- indexOctant = indexOctant ^ ( deltaX > 0 ? this.tree.FLAG_POS_X : this.tree.FLAG_NEG_X );
-
- }
-
- // y
-
- if ( distY + radiusObj > radiusOverlap ) {
-
- indexOctant = indexOctant ^ ( deltaY > 0 ? this.tree.FLAG_POS_Y : this.tree.FLAG_NEG_Y );
-
- }
-
- // z
-
- if ( distZ + radiusObj > radiusOverlap ) {
-
- indexOctant = indexOctant ^ ( deltaZ > 0 ? this.tree.FLAG_POS_Z : this.tree.FLAG_NEG_Z );
-
- }
-
- objectData.indexOctant = -indexOctant - this.tree.INDEX_OUTSIDE_OFFSET;
-
- return objectData.indexOctant;
-
- }
-
- // return octant index from delta xyz
-
- if ( deltaX - radiusObj > -overlap ) {
-
- // x right
-
- indexOctant = indexOctant | 1;
-
- } else if ( !( deltaX + radiusObj < overlap ) ) {
-
- // x left
-
- objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS;
- return objectData.indexOctant;
-
- }
-
- if ( deltaY - radiusObj > -overlap ) {
-
- // y right
-
- indexOctant = indexOctant | 2;
-
- } else if ( !( deltaY + radiusObj < overlap ) ) {
-
- // y left
-
- objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS;
- return objectData.indexOctant;
-
- }
-
-
- if ( deltaZ - radiusObj > -overlap ) {
-
- // z right
-
- indexOctant = indexOctant | 4;
-
- } else if ( !( deltaZ + radiusObj < overlap ) ) {
-
- // z left
-
- objectData.indexOctant = this.tree.INDEX_INSIDE_CROSS;
- return objectData.indexOctant;
-
- }
-
- objectData.indexOctant = indexOctant;
- return objectData.indexOctant;
-
- },
-
- getOctantIndexFromPosition: function ( x, y, z ) {
-
- var indexOctant = 0;
-
- if ( x > 0 ) {
-
- indexOctant = indexOctant | 1;
-
- }
-
- if ( y > 0 ) {
-
- indexOctant = indexOctant | 2;
-
- }
-
- if ( z > 0 ) {
-
- indexOctant = indexOctant | 4;
-
- }
-
- return indexOctant;
-
- },
-
- search: function ( position, radius, objects, direction, directionPct ) {
-
- var i, l,
- node,
- intersects;
-
- // test intersects by parameters
-
- if ( direction ) {
-
- intersects = this.intersectRay( position, direction, radius, directionPct );
-
- } else {
-
- intersects = this.intersectSphere( position, radius );
-
- }
-
- // if intersects
-
- if ( intersects === true ) {
-
- // gather objects
-
- objects = objects.concat( this.objects );
-
- // search subtree
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- node = this.nodesByIndex[ this.nodesIndices[ i ] ];
-
- objects = node.search( position, radius, objects, direction );
-
- }
-
- }
-
- return objects;
-
- },
-
- intersectSphere: function ( position, radius ) {
-
- var distance = radius * radius,
- px = position.x,
- py = position.y,
- pz = position.z;
-
- if ( px < this.left ) {
- distance -= Math.pow( px - this.left, 2 );
- } else if ( px > this.right ) {
- distance -= Math.pow( px - this.right, 2 );
- }
-
- if ( py < this.bottom ) {
- distance -= Math.pow( py - this.bottom, 2 );
- } else if ( py > this.top ) {
- distance -= Math.pow( py - this.top, 2 );
- }
-
- if ( pz < this.back ) {
- distance -= Math.pow( pz - this.back, 2 );
- } else if ( pz > this.front ) {
- distance -= Math.pow( pz - this.front, 2 );
- }
-
- return distance >= 0;
-
- },
-
- intersectRay: function ( origin, direction, distance, directionPct ) {
-
- if ( typeof directionPct === 'undefined' ) {
-
- directionPct = this.utilVec31Ray.set( 1, 1, 1 ).divide( direction );
-
- }
-
- var t1 = ( this.left - origin.x ) * directionPct.x,
- t2 = ( this.right - origin.x ) * directionPct.x,
- t3 = ( this.bottom - origin.y ) * directionPct.y,
- t4 = ( this.top - origin.y ) * directionPct.y,
- t5 = ( this.back - origin.z ) * directionPct.z,
- t6 = ( this.front - origin.z ) * directionPct.z,
- tmax = Math.min( Math.min( Math.max( t1, t2), Math.max( t3, t4) ), Math.max( t5, t6) ),
- tmin;
- // ray would intersect in reverse direction, i.e. this is behind ray
- if (tmax < 0)
- {
- return false;
- }
-
- tmin = Math.max( Math.max( Math.min( t1, t2), Math.min( t3, t4)), Math.min( t5, t6));
-
- // if tmin > tmax or tmin > ray distance, ray doesn't intersect AABB
- if( tmin > tmax || tmin > distance ) {
- return false;
- }
-
- return true;
-
- },
-
- getDepthEnd: function ( depth ) {
-
- var i, l,
- node;
- if ( this.nodesIndices.length > 0 ) {
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
- node = this.nodesByIndex[ this.nodesIndices[ i ] ];
- depth = node.getDepthEnd( depth );
- }
-
- } else {
- depth = !depth || this.depth > depth ? this.depth : depth;
- }
- return depth;
-
- },
-
- getNodeCountEnd: function () {
-
- return this.tree.root.getNodeCountRecursive() + 1;
-
- },
-
- getNodeCountRecursive: function () {
-
- var i, l,
- count = this.nodesIndices.length;
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- count += this.nodesByIndex[ this.nodesIndices[ i ] ].getNodeCountRecursive();
-
- }
-
- return count;
-
- },
-
- getObjectsEnd: function ( objects ) {
-
- var i, l,
- node;
-
- objects = ( objects || [] ).concat( this.objects );
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- node = this.nodesByIndex[ this.nodesIndices[ i ] ];
-
- objects = node.getObjectsEnd( objects );
-
- }
-
- return objects;
-
- },
-
- getObjectCountEnd: function () {
-
- var i, l,
- count = this.objects.length;
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- count += this.nodesByIndex[ this.nodesIndices[ i ] ].getObjectCountEnd();
-
- }
-
- return count;
-
- },
-
- getObjectCountStart: function () {
-
- var count = this.objects.length,
- parent = this.parent;
-
- while( parent instanceof THREE.OctreeNode ) {
-
- count += parent.objects.length;
- parent = parent.parent;
-
- }
-
- return count;
-
- },
-
- toConsole: function ( space ) {
-
- var i, l,
- node,
- spaceAddition = ' ';
-
- space = typeof space === 'string' ? space : spaceAddition;
-
- 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 );
- console.log( ( this.parent ? space + ' ' : ' ' ), '+ objects ( ', this.objects.length, ' ) ', this.objects );
- console.log( ( this.parent ? space + ' ' : ' ' ), '+ children ( ', this.nodesIndices.length, ' )', this.nodesIndices, this.nodesByIndex );
-
- for ( i = 0, l = this.nodesIndices.length; i < l; i++ ) {
-
- node = this.nodesByIndex[ this.nodesIndices[ i ] ];
-
- node.toConsole( space + spaceAddition );
-
- }
-
- }
-
- };
- /*===================================================
- raycaster additional functionality
- =====================================================*/
-
- THREE.Raycaster.prototype.intersectOctreeObject = function ( object, recursive ) {
-
- var intersects,
- octreeObject,
- facesAll,
- facesSearch;
-
- if ( object.object instanceof THREE.Object3D ) {
-
- octreeObject = object;
- object = octreeObject.object;
-
- // temporarily replace object geometry's faces with octree object faces
-
- facesSearch = octreeObject.faces;
- facesAll = object.geometry.faces;
-
- if ( facesSearch.length > 0 ) {
-
- object.geometry.faces = facesSearch;
-
- }
-
- // intersect
-
- intersects = this.intersectObject( object, recursive );
-
- // revert object geometry's faces
-
- if ( facesSearch.length > 0 ) {
-
- object.geometry.faces = facesAll;
-
- }
-
- } else {
-
- intersects = this.intersectObject( object, recursive );
-
- }
-
- return intersects;
-
- };
-
- THREE.Raycaster.prototype.intersectOctreeObjects = function ( objects, recursive ) {
-
- var i, il,
- intersects = [];
-
- for ( i = 0, il = objects.length; i < il; i++ ) {
-
- intersects = intersects.concat( this.intersectOctreeObject( objects[ i ], recursive ) );
-
- }
-
- return intersects;
-
- };
- }( THREE ) );
|