threejs.js 156 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747
  1. "use strict";
  2. // Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
  3. // Secretary of Defense (Personnel & Readiness).
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  6. // in compliance with the License. You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software distributed under the License
  11. // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  12. // or implied. See the License for the specific language governing permissions and limitations under
  13. // the License.
  14. define( [ "module",
  15. "vwf/view",
  16. "vwf/utility",
  17. "hammer",
  18. "jquery"
  19. ],
  20. function( module, view, utility, Hammer, $ ) {
  21. var self;
  22. // Navigation: Private global variables for navigation
  23. var navObjectRequested;
  24. var navObjectName;
  25. var navmode;
  26. var touchmode;
  27. var ownerlessNavObjects = [];
  28. var numNavCandidates;
  29. var translationSpeed = 100; // Units per second
  30. var rotationSpeed = 90; // Degrees per second
  31. var makeOwnAvatarVisible = false;
  32. var pointerLockImplemented = "pointerLockElement" in document ||
  33. "mozPointerLockElement" in document ||
  34. "webkitPointerLockElement" in document;
  35. var pointerLocked = false;
  36. var pickDirection = undefined;
  37. var raycaster = undefined;
  38. var pitchMatrix;
  39. var rollMatrix;
  40. var yawMatrix;
  41. var translationMatrix;
  42. var positionUnderMouseClick;
  43. var boundingBox = undefined;
  44. var userObjectRequested = false;
  45. var usersShareView = true;
  46. var degreesToRadians = Math.PI / 180;
  47. var movingForward = false;
  48. var movingBack = false;
  49. var movingLeft = false;
  50. var movingRight = false;
  51. var rotatingLeft = false;
  52. var rotatingRight = false;
  53. var startMousePosition;
  54. var startTouchPosition;
  55. // HACK: This is to deal with an issue with webkitMovementX in Chrome:
  56. // https://code.google.com/p/chromium/issues/detail?id=386791&thanks=386791&ts=1403213097
  57. // where the two values after pointerLock are inaccurate.
  58. // This is used to ignore those values.
  59. // Please check frequently to see if this can be removed.
  60. // Last checked status of issue on 6/19/14.
  61. var nextMouseMoveIsErroneous = false;
  62. var nextTwoMouseMovesAreErroneous = false;
  63. // END HACK
  64. // End Navigation
  65. var lastXPos = -1;
  66. var lastYPos = -1;
  67. var mouseDown = {
  68. left: false,
  69. right: false,
  70. middle: false
  71. };
  72. var touchGesture = false;
  73. var prevGesture = undefined;
  74. var Vec3 = goog.vec.Vec3;
  75. var Quaternion = goog.vec.Quaternion;
  76. var enableStereo = false;
  77. return view.load( module, {
  78. initialize: function( options ) {
  79. self = this;
  80. checkCompatibility.call(this);
  81. this.state.appInitialized = false;
  82. this.pickInterval = 10;
  83. this.enableInputs = true;
  84. this.applicationWantsPointerEvents = false;
  85. // Store parameter options for persistence functionality
  86. this.parameters = options;
  87. if ( typeof options == "object" ) {
  88. this.rootSelector = options[ "application-root" ];
  89. if ( "pick-interval" in options ) {
  90. this.pickInterval = options[ "pick-interval" ];
  91. }
  92. if ( "enable-inputs" in options ) {
  93. this.enableInputs = options[ "enable-inputs" ];
  94. }
  95. enableStereo = ( options.stereo !== undefined ) ? options.stereo : false;
  96. if ( options.shaders ) {
  97. var scriptEle = undefined;
  98. // jQuery.getScript()
  99. for ( var i = 0; i < options.shaders.length; i++ ) {
  100. var scriptEle = document.createElement( 'script' );
  101. scriptEle.setAttribute( "type", "text/javascript" );
  102. scriptEle.setAttribute( "src", options.shaders[ i ] );
  103. }
  104. }
  105. }
  106. else {
  107. this.rootSelector = options;
  108. }
  109. this.height = 600;
  110. this.width = 800;
  111. this.canvasQuery = null;
  112. if ( window && window.innerHeight ) this.height = window.innerHeight;
  113. if ( window && window.innerWidth ) this.width = window.innerWidth;
  114. this.keyStates = { keysDown: {}, mods: {}, keysUp: {} };
  115. pitchMatrix = new THREE.Matrix4();
  116. rollMatrix = new THREE.Matrix4();
  117. yawMatrix = new THREE.Matrix4();
  118. translationMatrix = new THREE.Matrix4();
  119. pickDirection = new THREE.Vector3();
  120. raycaster = new THREE.Raycaster();
  121. window._dView = this;
  122. this.nodes = {};
  123. this.interpolateTransforms = true;
  124. this.tickTime = 0;
  125. this.realTickDif = 50;
  126. this.lastrealTickDif = 50;
  127. this.lastRealTick = performance.now();
  128. this.leftover = 0;
  129. },
  130. createdNode: function( nodeID, childID, childExtendsID, childImplementsIDs,
  131. childSource, childType, childIndex, childName, callback /* ( ready ) */) {
  132. //the created node is a scene, and has already been added to the state by the model.
  133. //how/when does the model set the state object?
  134. if ( this.state.scenes[ childID ] )
  135. {
  136. this.canvasQuery = $(this.rootSelector).append("<canvas id='" + this.kernel.application() + "' width='"+this.width+"' height='"+this.height+"' class='vwf-scene'/>"
  137. ).children(":last");
  138. initScene.call(this,this.state.scenes[childID]);
  139. }
  140. else if ( this.state.scenes[ this.kernel.application() ] ) {
  141. var sceneNode = this.state.scenes[ this.kernel.application() ];
  142. if ( sceneNode.camera.ID == childID ) {
  143. setActiveCamera.call( this, sceneNode.camera.ID );
  144. }
  145. }
  146. if(this.state.nodes[childID] && this.state.nodes[childID].threeObject instanceof THREE.Object3D) {
  147. this.nodes[childID] = {id:childID,extends:childExtendsID};
  148. }
  149. },
  150. initializedNode: function( nodeID, childID ) {
  151. // If the node that was initialized is the application node, find the user's navigation object
  152. var appID = this.kernel.application();
  153. if ( childID == appID ) {
  154. if ( enableStereo ) {
  155. var viewCam = this.state.cameraInUse;
  156. var sceneNode = this.state.scenes[ childID ];
  157. if ( sceneNode ) {
  158. sceneNode.stereo = {
  159. "effect": new THREE.StereoEffect( sceneNode.renderer ),
  160. "element": sceneNode.renderer.domElement,
  161. "controls": viewCam ? createControls( viewCam, sceneNode.renderer.domElement ) : undefined
  162. }
  163. var effect = sceneNode.stereo.effect;
  164. effect.separation = this.parameters.IPD ? this.parameters.IPD : 0.2;
  165. effect.offset = this.parameters.offset ? this.parameters.offset : -0.2;
  166. effect.delta = this.parameters.delta ? this.parameters.delta : 0.01;
  167. effect.setSize( this.width, this.height );
  168. }
  169. }
  170. this.state.appInitialized = true;
  171. } else {
  172. //TODO: This is a temporary workaround until the callback functionality is implemented for
  173. // kernel.createChild()
  174. // Listening specifically for this.findNavObject>>createChild() creating a new navObject if
  175. // one does not exist.
  176. // Can be removed once kernel.createChild callback works properly
  177. var initNode = this.state.nodes[ childID ];
  178. if ( initNode && ( initNode.name == navObjectName ) ) {
  179. initNode.owner = this.kernel.moniker();
  180. controlNavObject( initNode );
  181. }
  182. // in the case that camera was not defined when the scene was created
  183. // we need to see if that camera is now defined, and set up the camera controls
  184. if ( enableStereo ) {
  185. var sceneNode = this.state.scenes[ appID ];
  186. if ( sceneNode && sceneNode.stereo && sceneNode.stereo.controls === undefined ) {
  187. if ( this.state.cameraInUse !== undefined ) {
  188. sceneNode.stereo.controls = createControls( this.state.cameraInUse, sceneNode.renderer.domElement );
  189. }
  190. }
  191. }
  192. }
  193. //End TODO
  194. },
  195. // -- deletedNode ------------------------------------------------------------------------------
  196. deletedNode: function(childID)
  197. {
  198. delete this.nodes[childID];
  199. },
  200. // -- addedChild -------------------------------------------------------------------------------
  201. //addedChild: function( nodeID, childID, childName ) { },
  202. // -- removedChild -----------------------------------------------------------------------------
  203. //removedChild: function( nodeID, childID ) { },
  204. // -- createdProperty --------------------------------------------------------------------------
  205. //createdProperty: function (nodeID, propertyName, propertyValue) { },
  206. // -- initializedProperty ----------------------------------------------------------------------
  207. initializedProperty: function ( nodeID, propertyName, propertyValue ) {
  208. this.satProperty(nodeID, propertyName, propertyValue);
  209. },
  210. // TODO: deletedProperty
  211. // -- satProperty ------------------------------------------------------------------------------
  212. satProperty: function ( nodeID, propertyName, propertyValue ) {
  213. // If this is this user's navObject, pay attention to changes in navmode, translationSpeed, and
  214. // rotationSpeed
  215. if ( navObject && ( nodeID == navObject.ID ) ) {
  216. if ( propertyName == "navmode" ) {
  217. navmode = propertyValue;
  218. if ( pointerLockImplemented && !self.appRequestsPointerLock( navmode, mouseDown ) ) {
  219. document.exitPointerLock();
  220. }
  221. } else if ( propertyName == "translationSpeed" ) {
  222. translationSpeed = propertyValue;
  223. } else if ( propertyName == "rotationSpeed" ) {
  224. rotationSpeed = propertyValue;
  225. }
  226. } else if ( nodeID == this.kernel.application() ) {
  227. if ( propertyName == "makeOwnAvatarVisible" ) {
  228. makeOwnAvatarVisible = propertyValue;
  229. if ( navObject ) {
  230. setVisibleRecursively( navObject.threeObject, makeOwnAvatarVisible );
  231. }
  232. } else if ( propertyName == "boundingBox" ) {
  233. boundingBox = propertyValue;
  234. } else if ( propertyName == "activeCamera" ) {
  235. setActiveCamera.call( this, this.state.scenes[ this.kernel.application() ].camera.ID );
  236. } else if ( propertyName == "usersShareView" ) {
  237. usersShareView = propertyValue;
  238. }
  239. }
  240. // Pay attention to these properties for all nodes
  241. if ( propertyName == "transform" ) {
  242. receiveModelTransformChanges( nodeID, propertyValue );
  243. } else if ( propertyName == "lookAt") {
  244. var node = this.state.nodes[ nodeID ];
  245. // If the state knows about the node, it is in the scene and should be updated
  246. // Otherwise, it is a prototype and can be ignored
  247. if ( node ) {
  248. nodeLookAt( node );
  249. }
  250. }
  251. },
  252. // -- gotProperty ------------------------------------------------------------------------------
  253. gotProperty: function ( nodeID, propertyName, propertyValue ) {
  254. var clientThatGotProperty = this.kernel.client();
  255. var me = this.kernel.moniker();
  256. var sceneRootID = this.kernel.application();
  257. if ( clientThatGotProperty == me ) {
  258. if ( propertyName == "owner") {
  259. // Get the navigable object
  260. var navCandidate = this.state.nodes[ nodeID ];
  261. // If a node w/ nodeID exists, then this is a real owner value to be processed
  262. // (otherwise it is the behavior itself and should be ignored)
  263. if ( navCandidate ) {
  264. // If we haven't already found the navigation object....
  265. if ( !navObject ) {
  266. var owner = propertyValue;
  267. // If I'm the owner, take control
  268. // Else, if it doesn't have an owner, push it on the list of ownerless navigation
  269. // objects that we can pull from if none is found that has an owner of this client
  270. if ( owner == me ) {
  271. controlNavObject( navCandidate );
  272. } else if ( !owner ) {
  273. ownerlessNavObjects.push( navCandidate );
  274. }
  275. }
  276. // If we did not take control of this navigation object (its owner wasn't this client)
  277. if ( !navObject ) {
  278. // Decrement the counter of navigation objects that we are waiting for
  279. numNavCandidates--;
  280. // If we're out of navigation candidates for which we might be the owner, see if
  281. // there are any ownerless nav objects that we could control
  282. // If so, take control
  283. // Else, create one
  284. if ( !numNavCandidates ) {
  285. if ( ownerlessNavObjects.length ) {
  286. controlNavObject( ownerlessNavObjects[ 0 ] );
  287. } else {
  288. // Retrieve the userObject property so we may create a navigation object from
  289. // it for this user (the rest of the logic is in the gotProperty call for
  290. // userObject)
  291. this.kernel.getProperty( this.kernel.application(), "userObject" );
  292. userObjectRequested = true;
  293. }
  294. }
  295. }
  296. }
  297. } else if ( propertyName == "userObject" ) {
  298. if ( userObjectRequested ) {
  299. // The userObject property is only requested when the system wishes to create one for this
  300. // user. We do that here.
  301. // Set the userObject from the value received or a default if it is null/undefined
  302. var userObject = propertyValue || {
  303. "extends": "http://vwf.example.com/camera.vwf",
  304. "implements": [ "http://vwf.example.com/navigable.vwf" ]
  305. };
  306. // Makes sure that the userObject has a properties field
  307. userObject[ "properties" ] = userObject[ "properties" ] || {};
  308. // Set the object's owner to be this object
  309. userObject[ "properties" ][ "owner" ] = me;
  310. // Save the name of the object globally so we can recognize it in
  311. // initializedNode so we can take control of it there
  312. navObjectName = "navobj_" + me;
  313. // TODO: The callback function is commented out because callbacks have not yet been
  314. // implemented for createChild - see workaround in initializedNode
  315. this.kernel.createChild( this.kernel.application(), navObjectName, userObject, undefined, undefined /*,
  316. function( nodeID ) {
  317. controlNavObject( this.state.nodes[ nodeID ] );
  318. } */ );
  319. userObjectRequested = false;
  320. }
  321. } else if ( propertyName == "makeOwnAvatarVisible" ) {
  322. makeOwnAvatarVisible = propertyValue;
  323. if ( navObject ) {
  324. setVisibleRecursively( navObject.threeObject, makeOwnAvatarVisible );
  325. }
  326. } else if ( propertyName == "boundingBox" ) {
  327. boundingBox = propertyValue;
  328. } else if ( navObject && ( nodeID == navObject.ID ) ) {
  329. // These were requested in controlNavObject
  330. if ( propertyName == "navmode" ) {
  331. navmode = propertyValue;
  332. } else if ( propertyName == "touchmode" ) {
  333. touchmode = propertyValue;
  334. } else if ( propertyName == "translationSpeed" ) {
  335. translationSpeed = propertyValue;
  336. } else if ( propertyName == "rotationSpeed" ) {
  337. rotationSpeed = propertyValue;
  338. }
  339. }
  340. }
  341. },
  342. // -- calledMethod -----------------------------------------------------------------------------
  343. calledMethod: function( nodeID, methodName, methodParameters, methodValue ) {
  344. switch(methodName) {
  345. case "translateBy":
  346. case "translateTo":
  347. // No need for rotateBy or rotateTo because they call the quaternion methods
  348. case "quaterniateBy":
  349. case "quaterniateTo":
  350. case "scaleBy":
  351. case "scaleTo":
  352. // No need for transformBy or worldTransformBy because they call transformTo and worldTransformTo
  353. case "transformTo":
  354. case "worldTransformTo":
  355. // If the duration of the transform is 0, set the transforms to their final value so it doesn't interpolate
  356. if(methodParameters.length < 2 || methodParameters[1] == 0) {
  357. this.nodes[nodeID].lastTickTransform = getTransform(nodeID);
  358. this.nodes[nodeID].selfTickTransform = goog.vec.Mat4.clone(this.nodes[nodeID].lastTickTransform);
  359. }
  360. break;
  361. }
  362. },
  363. // -- addedEventListener -------------------------------------------------------------------
  364. addedEventListener: function( nodeID, eventName, eventHandler, eventContextID, eventPhases ) {
  365. switch( eventName ) {
  366. case "pointerClick":
  367. case "pointerDown":
  368. case "pointerMove":
  369. case "pointerUp":
  370. case "pointerOver":
  371. case "pointerOut":
  372. case "pointerWheel":
  373. case "touchHold":
  374. case "touchTap":
  375. case "touchDoubleTap":
  376. case "touchDrag":
  377. case "touchDragStart":
  378. case "touchDragEnd":
  379. case "touchDragUp":
  380. case "touchDragDown":
  381. case "touchDragLeft":
  382. case "touchDragRight":
  383. case "touchSwipe":
  384. case "touchSwipeUp":
  385. case "touchSwipeDown":
  386. case "touchSwipeLeft":
  387. case "touchSwipeRight":
  388. case "touchTransform":
  389. case "touchTransformStart":
  390. case "touchTransformEnd":
  391. case "touchRotate":
  392. case "touchPinch":
  393. case "touchPinchIn":
  394. case "touchPinchOut":
  395. case "touchStart":
  396. case "touchRelease":
  397. if ( this.kernel.find( nodeID,
  398. "self::element(*,'http://vwf.example.com/node3.vwf')" ).length ||
  399. this.kernel.find( nodeID,
  400. "self::element(*,'http://vwf.example.com/scene.vwf')" ).length ) {
  401. this.applicationWantsPointerEvents = true;
  402. }
  403. break;
  404. }
  405. },
  406. // -- firedEvent -----------------------------------------------------------------------------
  407. firedEvent: function( nodeID, eventName ) {
  408. if ( eventName == "changingTransformFromView" ) {
  409. var clientThatSatProperty = self.kernel.client();
  410. var me = self.kernel.moniker();
  411. // If the transform property was initially updated by this view....
  412. if ( clientThatSatProperty == me ) {
  413. var node = this.state.nodes[ nodeID ];
  414. node.ignoreNextTransformUpdate = true;
  415. }
  416. }
  417. else if (eventName == "resetViewport") {
  418. if(this.state.scenes[nodeID]) {
  419. this.state.scenes[nodeID].renderer.setViewport(0,0,window.innerWidth,window.innerHeight);
  420. }
  421. }
  422. },
  423. // -- ticked -----------------------------------------------------------------------------------
  424. ticked: function() {
  425. // This is the first place that we know that the entire app is loaded because the queue has been
  426. // resumed (and therefore, it is ticking) - we will search for the user's navigation object here
  427. // We want to only search for the navigation object if we haven't before (!navObjectRequested),
  428. // and we want to make sure that the app has been initialized, and where not at the brief period of
  429. // ticking before the app starts loading (appInitialized)
  430. if ( !navObjectRequested && this.state.appInitialized ) {
  431. navObjectRequested = true;
  432. findNavObject();
  433. }
  434. lerpTick();
  435. },
  436. // -- render -----------------------------------------------------------------------------------
  437. render: function( renderer, scene, camera ) {
  438. renderer.render( scene, camera );
  439. },
  440. // -- Navigation -------------------------------------------------------------------------------
  441. navigationKeyMapping: {
  442. "w": "forward",
  443. "a": "left",
  444. "s": "back",
  445. "d": "right",
  446. "uparrow": "forward",
  447. "leftarrow": "left",
  448. "downarrow": "back",
  449. "rightarrow": "right",
  450. "q": "rotateLeft",
  451. "e": "rotateRight"
  452. },
  453. handleMouseNavigation: function( deltaX, deltaY, navObj, navMode, rotationSpeed, translationSpeed, mouseDown, mouseEventData ) {
  454. var yawQuat = new THREE.Quaternion();
  455. var pitchQuat = new THREE.Quaternion();
  456. var rotationSpeedRadians = degreesToRadians * rotationSpeed;
  457. var orbiting = mouseDown.middle && ( navmode == "fly" );
  458. // We will soon want to use the yawMatrix and pitchMatrix,
  459. // so let's update them
  460. extractRotationAndTranslation( navObject.threeObject );
  461. if ( orbiting ) {
  462. var pitchRadians = deltaY * rotationSpeedRadians;
  463. var yawRadians = deltaX * rotationSpeedRadians;
  464. orbit( pitchRadians, yawRadians );
  465. } else if ( mouseDown.right ) {
  466. var navThreeObject = navObj.threeObject;
  467. // --------------------
  468. // Calculate new pitch
  469. // --------------------
  470. // deltaY is negated because a positive change (downward) generates a negative rotation
  471. // around the horizontal x axis (clockwise as viewed from the right)
  472. pitchQuat.setFromAxisAngle( new THREE.Vector3( 1, 0, 0 ), -deltaY * rotationSpeedRadians );
  473. var pitchDeltaMatrix = new THREE.Matrix4();
  474. pitchDeltaMatrix.makeRotationFromQuaternion( pitchQuat );
  475. if ( ( navMode == "fly" ) ||
  476. ( ( navMode == "walk" ) && ( cameraNode == navObj ) ) ) {
  477. pitchMatrix.multiplyMatrices( pitchDeltaMatrix, pitchMatrix );
  478. // Constrain the camera's pitch to +/- 90 degrees
  479. // We need to do something if zAxis.z is < 0
  480. var pitchMatrixElements = pitchMatrix.elements;
  481. if ( pitchMatrixElements[ 10 ] < 0 ) {
  482. var xAxis = goog.vec.Vec3.create();
  483. xAxis = goog.vec.Vec3.setFromArray( xAxis, [ pitchMatrixElements[ 0 ],
  484. pitchMatrixElements[ 1 ],
  485. pitchMatrixElements[ 2 ] ] );
  486. var yAxis = goog.vec.Vec3.create();
  487. // If forward vector is tipped up
  488. if ( pitchMatrixElements[ 6 ] > 0 ) {
  489. yAxis = goog.vec.Vec3.setFromArray( yAxis, [ 0, 0, 1 ] );
  490. } else {
  491. yAxis = goog.vec.Vec3.setFromArray( yAxis, [ 0, 0, -1 ] );
  492. }
  493. // Calculate the zAxis as a crossProduct of x and y
  494. var zAxis = goog.vec.Vec3.cross( xAxis, yAxis, goog.vec.Vec3.create() );
  495. // Put these values back in the camera matrix
  496. pitchMatrixElements[ 4 ] = yAxis[ 0 ];
  497. pitchMatrixElements[ 5 ] = yAxis[ 1 ];
  498. pitchMatrixElements[ 6 ] = yAxis[ 2 ];
  499. pitchMatrixElements[ 8 ] = zAxis[ 0 ];
  500. pitchMatrixElements[ 9 ] = zAxis[ 1 ];
  501. pitchMatrixElements[ 10 ] = zAxis[ 2 ];
  502. }
  503. } else if ( navMode == "walk" ) {
  504. // Perform pitch on camera - right-multiply to keep pitch separate from yaw
  505. var camera = self.state.cameraInUse;
  506. if ( camera ) {
  507. var cameraMatrix = camera.matrix;
  508. var originalCameraTransform = goog.vec.Mat4.clone( cameraMatrix.elements );
  509. var cameraPos = new THREE.Vector3();
  510. cameraPos.setFromMatrixPosition( cameraMatrix );
  511. cameraMatrix.multiply( pitchDeltaMatrix );
  512. // Constrain the camera's pitch to +/- 90 degrees
  513. var camWorldMatrix = camera.matrixWorld;
  514. var camWorldMatrixElements = camWorldMatrix.elements;
  515. // We need to do something if zAxis.z is < 0
  516. // This can get a little weird because this matrix is in three.js coordinates,
  517. // but we care about VWF coordinates:
  518. // -the VWF y-axis is the three.js -z axis
  519. // -the VWF z-axis is the three.js y axis
  520. if ( camWorldMatrixElements[ 6 ] < 0 ) {
  521. var xAxis = goog.vec.Vec3.create();
  522. xAxis = goog.vec.Vec3.setFromArray( xAxis, [ camWorldMatrixElements[ 0 ],
  523. camWorldMatrixElements[ 1 ],
  524. camWorldMatrixElements[ 2 ] ] );
  525. var yAxis = goog.vec.Vec3.create();
  526. // If forward vector is tipped up
  527. if ( camWorldMatrixElements[ 10 ] > 0 ) {
  528. yAxis = goog.vec.Vec3.setFromArray( yAxis, [ 0, 0, -1 ] );
  529. } else {
  530. yAxis = goog.vec.Vec3.setFromArray( yAxis, [ 0, 0, 1 ] );
  531. }
  532. // Calculate the zAxis as a crossProduct of x and y
  533. var zAxis = goog.vec.Vec3.cross( xAxis, yAxis, goog.vec.Vec3.create() );
  534. // Put these values back in the camera matrix
  535. camWorldMatrixElements[ 4 ] = zAxis[ 0 ];
  536. camWorldMatrixElements[ 5 ] = zAxis[ 1 ];
  537. camWorldMatrixElements[ 6 ] = zAxis[ 2 ];
  538. camWorldMatrixElements[ 8 ] = -yAxis[ 0 ];
  539. camWorldMatrixElements[ 9 ] = -yAxis[ 1 ];
  540. camWorldMatrixElements[ 10 ] = -yAxis[ 2 ];
  541. setTransformFromWorldTransform( camera );
  542. }
  543. // Restore camera position so rotation is done around camera center
  544. cameraMatrix.setPosition( cameraPos );
  545. updateRenderObjectTransform( camera );
  546. callModelTransformBy( cameraNode, originalCameraTransform,
  547. cameraMatrix.elements );
  548. } else {
  549. self.logger.warnx( "There is no camera to move" );
  550. }
  551. }
  552. // ------------------
  553. // Calculate new yaw
  554. // ------------------
  555. // deltaX is negated because a positive change (to the right) generates a negative rotation
  556. // around the vertical z axis (clockwise as viewed from above)
  557. yawQuat.setFromAxisAngle( new THREE.Vector3( 0, 0, 1 ), -deltaX * rotationSpeedRadians );
  558. var yawDeltaMatrix = new THREE.Matrix4();
  559. yawDeltaMatrix.makeRotationFromQuaternion( yawQuat );
  560. yawMatrix.multiplyMatrices( yawDeltaMatrix, yawMatrix );
  561. // -------------------------------------------------
  562. // Put all components together and set the new pose
  563. // -------------------------------------------------
  564. var navObjectWorldMatrix = navObject.threeObject.matrixWorld;
  565. navObjectWorldMatrix.multiplyMatrices( yawMatrix, pitchMatrix );
  566. navObjectWorldMatrix.multiplyMatrices( translationMatrix, navObjectWorldMatrix );
  567. if ( navObject.threeObject instanceof THREE.Camera ) {
  568. var navObjWrldTrnsfmArr = navObjectWorldMatrix.elements;
  569. navObjectWorldMatrix.elements = convertCameraTransformFromVWFtoThreejs( navObjWrldTrnsfmArr );
  570. }
  571. }
  572. },
  573. handleScroll: function ( wheelDelta, navObj, navMode, rotationSpeed, translationSpeed, distanceToTarget ) {
  574. if ( navMode !== "fly" ) {
  575. return;
  576. }
  577. var orbiting = ( navMode == "fly" ) && ( mouseDown.middle )
  578. if ( orbiting || !pickDirection ) {
  579. return;
  580. }
  581. var navThreeObject = navObj.threeObject;
  582. // wheelDelta has a value of 3 for every click
  583. var numClicks = Math.abs( wheelDelta / 3 );
  584. // Prepare variables for calculation
  585. var dist = Math.min( Math.max( distanceToTarget || translationSpeed,
  586. 2 * self.state.cameraInUse.near ),
  587. 9 * translationSpeed );
  588. var percentDistRemainingEachStep = 0.8;
  589. var amountToMove = 0;
  590. // If wheelDelta is negative, user pushed wheel forward - move toward the object
  591. // Else, user pulled wheel back - move away from object
  592. if ( wheelDelta < 0 ) {
  593. amountToMove = dist * ( 1 - Math.pow( percentDistRemainingEachStep, numClicks ) );
  594. } else {
  595. amountToMove = dist * ( 1 - Math.pow( 1 / percentDistRemainingEachStep, numClicks ) );
  596. }
  597. // We are about to use the translationMatrix, so let's update it
  598. extractRotationAndTranslation( navObject.threeObject );
  599. var translationArray = translationMatrix.elements;
  600. translationArray[ 12 ] += amountToMove * pickDirection.x;
  601. translationArray[ 13 ] += amountToMove * pickDirection.y;
  602. translationArray[ 14 ] += amountToMove * pickDirection.z;
  603. if ( boundingBox != undefined ) {
  604. if ( translationArray[ 12 ] < boundingBox[ 0 ][ 0 ] ) {
  605. translationArray[ 12 ] = boundingBox[ 0 ][ 0 ];
  606. }
  607. else if ( translationArray[ 12 ] > boundingBox[ 0 ][ 1 ] ) {
  608. translationArray[ 12 ] = boundingBox[ 0 ][ 1 ];
  609. }
  610. if ( translationArray[ 13 ] < boundingBox[ 1 ][ 0 ] ) {
  611. translationArray[ 13 ] = boundingBox[ 1 ][ 0 ];
  612. }
  613. else if ( translationArray[ 13 ] > boundingBox[ 1 ][ 1 ] ) {
  614. translationArray[ 13 ] = boundingBox[ 1 ][ 1 ];
  615. }
  616. if ( translationArray[ 14 ] < boundingBox[ 2 ][ 0 ] ) {
  617. translationArray[ 14 ] = boundingBox[ 2 ][ 0 ];
  618. }
  619. else if ( translationArray[ 14 ] > boundingBox[ 2 ][ 1 ] ) {
  620. translationArray[ 14 ] = boundingBox[ 2 ][ 1 ];
  621. }
  622. }
  623. var worldTransformArray = navThreeObject.matrixWorld.elements;
  624. worldTransformArray[ 12 ] = translationArray[ 12 ];
  625. worldTransformArray[ 13 ] = translationArray[ 13 ];
  626. worldTransformArray[ 14 ] = translationArray[ 14 ];
  627. },
  628. handleTouchNavigation: function ( touchEventData ) {
  629. var currentMousePosition = touchEventData[ 0 ].position;
  630. var deltaX = 0;
  631. var deltaY = 0;
  632. if ( startTouchPosition ) {
  633. deltaX = currentMousePosition[ 0 ] - startTouchPosition [ 0 ];
  634. deltaY = currentMousePosition[ 1 ] - startTouchPosition [ 1 ];
  635. }
  636. // We will soon want to use the yawMatrix and pitchMatrix,
  637. // so let's update them
  638. extractRotationAndTranslation( navObject.threeObject );
  639. if ( deltaX || deltaY ) {
  640. var yawQuat = new THREE.Quaternion();
  641. var pitchQuat = new THREE.Quaternion();
  642. var rotationSpeedRadians = degreesToRadians * rotationSpeed;
  643. if ( touchmode == "orbit" ) {
  644. var pitchRadians = deltaY * rotationSpeedRadians;
  645. var yawRadians = deltaX * rotationSpeedRadians;
  646. orbit( pitchRadians, yawRadians );
  647. } else if ( touchmode == "look" ) {
  648. if ( navObject ) {
  649. var navThreeObject = navObject.threeObject;
  650. var originalTransform = goog.vec.Mat4.clone( navThreeObject.matrix.elements );
  651. // --------------------
  652. // Calculate new pitch
  653. // --------------------
  654. // deltaY is negated because a positive change (downward) generates a negative rotation
  655. // around the horizontal x axis (clockwise as viewed from the right)
  656. pitchQuat.setFromAxisAngle( new THREE.Vector3( 1, 0, 0 ), -deltaY * rotationSpeedRadians );
  657. var pitchDeltaMatrix = new THREE.Matrix4();
  658. pitchDeltaMatrix.makeRotationFromQuaternion( pitchQuat );
  659. if ( ( touchmode == "look" ) ||
  660. ( ( touchmode == "orbit" ) && ( cameraNode == navObject ) ) ) {
  661. pitchMatrix.multiplyMatrices( pitchDeltaMatrix, pitchMatrix );
  662. // Constrain the camera's pitch to +/- 90 degrees
  663. // We need to do something if zAxis.z is < 0
  664. var pitchMatrixElements = pitchMatrix.elements;
  665. if ( pitchMatrixElements[ 10 ] < 0 ) {
  666. var xAxis = goog.vec.Vec3.create();
  667. xAxis = goog.vec.Vec3.setFromArray( xAxis, [ pitchMatrixElements[ 0 ],
  668. pitchMatrixElements[ 1 ],
  669. pitchMatrixElements[ 2 ] ] );
  670. var yAxis = goog.vec.Vec3.create();
  671. // If forward vector is tipped up
  672. if ( pitchMatrixElements[ 6 ] > 0 ) {
  673. yAxis = goog.vec.Vec3.setFromArray( yAxis, [ 0, 0, 1 ] );
  674. } else {
  675. yAxis = goog.vec.Vec3.setFromArray( yAxis, [ 0, 0, -1 ] );
  676. }
  677. // Calculate the zAxis as a crossProduct of x and y
  678. var zAxis = goog.vec.Vec3.cross( xAxis, yAxis, goog.vec.Vec3.create() );
  679. // Put these values back in the camera matrix
  680. pitchMatrixElements[ 4 ] = yAxis[ 0 ];
  681. pitchMatrixElements[ 5 ] = yAxis[ 1 ];
  682. pitchMatrixElements[ 6 ] = yAxis[ 2 ];
  683. pitchMatrixElements[ 8 ] = zAxis[ 0 ];
  684. pitchMatrixElements[ 9 ] = zAxis[ 1 ];
  685. pitchMatrixElements[ 10 ] = zAxis[ 2 ];
  686. }
  687. }
  688. // ------------------
  689. // Calculate new yaw
  690. // ------------------
  691. // deltaX is negated because a positive change (to the right) generates a negative rotation
  692. // around the vertical z axis (clockwise as viewed from above)
  693. yawQuat.setFromAxisAngle( new THREE.Vector3( 0, 0, 1 ), -deltaX * rotationSpeedRadians );
  694. var yawDeltaMatrix = new THREE.Matrix4();
  695. yawDeltaMatrix.makeRotationFromQuaternion( yawQuat );
  696. yawMatrix.multiplyMatrices( yawDeltaMatrix, yawMatrix );
  697. // -------------------------------------------------
  698. // Put all components together and set the new pose
  699. // -------------------------------------------------
  700. var navObjectWorldMatrix = navThreeObject.matrixWorld;
  701. navObjectWorldMatrix.multiplyMatrices( yawMatrix, pitchMatrix );
  702. navObjectWorldMatrix.multiplyMatrices( translationMatrix, navObjectWorldMatrix );
  703. if ( navThreeObject instanceof THREE.Camera ) {
  704. var navObjWrldTrnsfmArr = navObjectWorldMatrix.elements;
  705. navObjectWorldMatrix.elements = convertCameraTransformFromVWFtoThreejs( navObjWrldTrnsfmArr );
  706. }
  707. setTransformFromWorldTransform( navObject.threeObject );
  708. callModelTransformBy( navObject, originalTransform, navThreeObject.matrix.elements );
  709. } else {
  710. self.logger.warnx( "handleTouchNavigation: There is no navigation object to move" );
  711. }
  712. }
  713. }
  714. startTouchPosition = currentMousePosition;
  715. },
  716. appRequestsPointerLock: function( navmode, mouseDown ) {
  717. // By default, an app will request pointer lock when:
  718. // - the middle mouse button is hit in fly mode (for orbit)
  719. // - the right mouse button is hit in any mode other than "none" (for look)
  720. if ( mouseDown.middle && ( navmode === "fly" ) ) {
  721. return true;
  722. }
  723. if ( mouseDown.right && ( navmode !== "none" ) ) {
  724. return true;
  725. }
  726. return false;
  727. }
  728. } );
  729. // private ===============================================================================
  730. var navObject = undefined;
  731. var cameraNode = undefined;
  732. function lerpTick () {
  733. var now = performance.now();
  734. self.realTickDif = now - self.lastRealTick;
  735. self.lastRealTick = now;
  736. //reset - loading can cause us to get behind and always but up against the max prediction value
  737. self.tickTime = 0;
  738. for ( var nodeID in self.nodes ) {
  739. if ( self.state.nodes[nodeID] ) {
  740. self.nodes[nodeID].lastTickTransform = self.nodes[nodeID].selfTickTransform;
  741. self.nodes[nodeID].selfTickTransform = getTransform(nodeID);
  742. }
  743. }
  744. }
  745. function lerp(a,b,l,c) {
  746. if(c) l = Math.min(1,Math.max(l,0));
  747. return (b*l) + a*(1.0-l);
  748. }
  749. function matCmp (a,b,delta) {
  750. for(var i =0; i < 16; i++) {
  751. if(Math.abs(a[i] - b[i]) > delta)
  752. return false;
  753. }
  754. return true;
  755. }
  756. function rotMatFromVec(x,y,z) {
  757. var n = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
  758. n[0] = x[0];n[1] = x[1];n[2] = x[2];
  759. n[4] = y[0];n[5] = y[1];n[6] = y[2];
  760. n[8] = z[0];n[9] = z[1];n[10] = z[2];
  761. return n;
  762. }
  763. function isLeftHandedOrthogonalMatrix( elements ) {
  764. if ( !elements ) {
  765. throw new Error('matrix was null');
  766. }
  767. var xAxis = new THREE.Vector3(elements[0],elements[1],elements[2]);
  768. var yAxis = new THREE.Vector3(elements[4],elements[5],elements[6]);
  769. var zAxis = new THREE.Vector3(elements[8],elements[9],elements[10]);
  770. xAxis.normalize();
  771. yAxis.normalize();
  772. zAxis.normalize();
  773. var XYdotZ = xAxis.cross( yAxis ).dot( zAxis );
  774. if( XYdotZ > 0.999999 ) {
  775. return true;
  776. } else {
  777. return false;
  778. }
  779. }
  780. function matrixLerp( a, b, l ) {
  781. // If either of the matrices is not left-handed or not orthogonal, interpolation won't work
  782. // Just return the second matrix
  783. if ( !( isLeftHandedOrthogonalMatrix( a ) && isLeftHandedOrthogonalMatrix( b ) ) ) {
  784. return b;
  785. }
  786. var n = goog.vec.Mat4.clone(a);
  787. n[12] = lerp(a[12],b[12],l);
  788. n[13] = lerp(a[13],b[13],l);
  789. n[14] = lerp(a[14],b[14],l);
  790. var x = [a[0],a[1],a[2]];
  791. var xl = Vec3.magnitude(x);
  792. var y = [a[4],a[5],a[6]];
  793. var yl = Vec3.magnitude(y);
  794. var z = [a[8],a[9],a[10]];
  795. var zl = Vec3.magnitude(z);
  796. var x2 = [b[0],b[1],b[2]];
  797. var xl2 = Vec3.magnitude(x2);
  798. var y2 = [b[4],b[5],b[6]];
  799. var yl2 = Vec3.magnitude(y2);
  800. var z2 = [b[8],b[9],b[10]];
  801. var zl2 = Vec3.magnitude(z2);
  802. var nxl = lerp(xl,xl2,l);
  803. var nyl = lerp(yl,yl2,l);
  804. var nzl = lerp(zl,zl2,l);
  805. x = Vec3.normalize(x,[]);
  806. y = Vec3.normalize(y,[]);
  807. z = Vec3.normalize(z,[]);
  808. x2 = Vec3.normalize(x2,[]);
  809. y2 = Vec3.normalize(y2,[]);
  810. z2 = Vec3.normalize(z2,[]);
  811. var q = Quaternion.fromRotationMatrix4(rotMatFromVec(x,y,z),[]);
  812. var q2 = Quaternion.fromRotationMatrix4(rotMatFromVec(x2,y2,z2),[]);
  813. var nq = Quaternion.slerp(q,q2,l,[]);
  814. var nqm = Quaternion.toRotationMatrix4(nq,[]);
  815. var nx = [nqm[0],nqm[1],nqm[2]];
  816. var ny = [nqm[4],nqm[5],nqm[6]];
  817. var nz = [nqm[8],nqm[9],nqm[10]];
  818. nx = Vec3.scale(nx,nxl,[]);
  819. ny = Vec3.scale(ny,nyl,[]);
  820. nz = Vec3.scale(nz,nzl,[]);
  821. nqm = rotMatFromVec(nx,ny,nz);
  822. nqm[12] = n[12];
  823. nqm[13] = n[13];
  824. nqm[14] = n[14];
  825. return nqm;
  826. }
  827. function getTransform(id) {
  828. var interp = goog.vec.Mat4.clone(self.state.nodes[id].threeObject.matrix.elements);
  829. return interp;
  830. }
  831. function setTransform(id,interp) {
  832. interp = goog.vec.Mat4.clone(interp)
  833. self.state.nodes[id].threeObject.matrix.elements = interp;
  834. self.state.nodes[id].threeObject.updateMatrixWorld(true);
  835. }
  836. function setInterpolatedTransforms(deltaTime) {
  837. var step = (self.tickTime) / (self.realTickDif);
  838. step = Math.min(step,1);
  839. deltaTime = Math.min(deltaTime, self.realTickDif)
  840. self.tickTime += deltaTime || 0;
  841. for(var nodeID in self.nodes) {
  842. var last = self.nodes[nodeID].lastTickTransform;
  843. var now = self.nodes[nodeID].selfTickTransform;
  844. if(last && now && !matCmp(last,now,.0001) ) {
  845. var interp = matrixLerp(last, now, step || 0);
  846. var objectIsControlledByUser = ( ( navmode !== "none" ) &&
  847. ( ( navObject && ( nodeID === navObject.ID ) ) ||
  848. ( cameraNode && ( nodeID === cameraNode.ID ) ) ) );
  849. if ( !objectIsControlledByUser ) {
  850. setTransform(nodeID, interp);
  851. self.nodes[nodeID].needTransformRestore = true;
  852. }
  853. }
  854. }
  855. }
  856. function restoreTransforms() {
  857. for(var nodeID in self.nodes) {
  858. var now = self.nodes[nodeID].selfTickTransform;
  859. if(self.node != navObject && now && self.nodes[nodeID].needTransformRestore) {
  860. self.state.nodes[nodeID].threeObject.matrix.elements = goog.vec.Mat4.clone(now);
  861. self.state.nodes[nodeID].threeObject.updateMatrixWorld(true);
  862. self.nodes[nodeID].needTransformRestore = false;
  863. }
  864. }
  865. }
  866. function checkCompatibility() {
  867. this.compatibilityStatus = { compatible:true, errors:{} }
  868. var contextNames = ["webgl","experimental-webgl","moz-webgl","webkit-3d"];
  869. for(var i = 0; i < contextNames.length; i++){
  870. try{
  871. var canvas = document.createElement('canvas');
  872. var gl = canvas.getContext(contextNames[i]);
  873. if(gl){
  874. return true;
  875. }
  876. }
  877. catch(e){}
  878. }
  879. this.compatibilityStatus.compatible = false;
  880. this.compatibilityStatus.errors["WGL"] = "This browser is not compatible. The vwf/view/threejs driver requires WebGL.";
  881. return false;
  882. }
  883. function initScene( sceneNode ) {
  884. var lastPickTime = 0;
  885. var mobileDevice = isMobile();
  886. function GetParticleSystems(node,list)
  887. {
  888. if(!list)
  889. list = [];
  890. for(var i =0; i<node.children.length; i++)
  891. {
  892. if(node.children[i] instanceof THREE.PointCloud)
  893. list.push(node.children[i]);
  894. list = GetParticleSystems(node.children[i],list);
  895. }
  896. return list;
  897. }
  898. function GetShaderMaterials(node,list)
  899. {
  900. if(!list)
  901. list = [];
  902. for(var i =0; i<node.children.length; i++)
  903. {
  904. if(node.children[i] instanceof THREE.Mesh && node.children[i].material instanceof THREE.ShaderMaterial)
  905. list.push(node.children[i].material);
  906. list = GetShaderMaterials(node.children[i],list);
  907. }
  908. return list;
  909. }
  910. function renderScene(time) {
  911. // Schedule the next render
  912. window.requestAnimationFrame( renderScene );
  913. if ( !self.state.appInitialized ) {
  914. return;
  915. }
  916. // Verify that there is a camera to render from before going any farther
  917. var camera = self.state.cameraInUse;
  918. if ( !camera ) {
  919. self.logger.debugx( "Cannot render because there is no valid camera" );
  920. return;
  921. }
  922. var now = performance.now();
  923. var timepassed = now - sceneNode.lastTime;
  924. if(self.interpolateTransforms) {
  925. setInterpolatedTransforms(timepassed);
  926. }
  927. if ( timepassed ) {
  928. var pss = GetParticleSystems(sceneNode.threeScene);
  929. for ( var i in pss )
  930. {
  931. if(pss[i].update)
  932. pss[i].update(timepassed);
  933. }
  934. var shaderMaterials = GetShaderMaterials( sceneNode.threeScene );
  935. for ( var i = 0; i < shaderMaterials.length; i++ ) {
  936. if( shaderMaterials[ i ].updateFunction ) {
  937. shaderMaterials[ i ].update();
  938. }
  939. }
  940. if ( navmode != "none" && self.enableInputs ) {
  941. // Move the user's camera according to their input
  942. inputMoveNavObject( timepassed );
  943. inputRotateNavObjectByKey( timepassed );
  944. // If the camera has been created, turn it to look back at its lookat position after
  945. // moving/rotating
  946. if ( cameraNode ) {
  947. nodeLookAt( cameraNode );
  948. }
  949. }
  950. }
  951. if ( self.mouseOverCanvas ) {
  952. // Only do a pick every "pickInterval" ms. Defaults to 10 ms.
  953. // Note: this is a costly operation and should be optimized if possible
  954. if ( ( self.mouseJustEnteredCanvas || ( ( now - lastPickTime ) > self.pickInterval ) ) && self.enableInputs )
  955. {
  956. sceneNode.frameCount = 0;
  957. var newPick, newPickId;
  958. if ( self.applicationWantsPointerEvents ) {
  959. newPick = ThreeJSPick.call( self, mycanvas, sceneNode, false );
  960. newPickId = newPick ? getPickObjectID.call( view, newPick.object ) : view.kernel.application();
  961. } else {
  962. newPick = undefined;
  963. newPickId = undefined;
  964. }
  965. if ( self.lastPickId != newPickId && self.lastEventData )
  966. {
  967. if ( self.lastPickId ) {
  968. view.kernel.dispatchEvent( self.lastPickId, "pointerOut",
  969. self.lastEventData.eventData,
  970. self.lastEventData.eventNodeData );
  971. }
  972. if ( newPickId ) {
  973. view.kernel.dispatchEvent( newPickId, "pointerOver",
  974. self.lastEventData.eventData,
  975. self.lastEventData.eventNodeData );
  976. }
  977. }
  978. if ( view.lastEventData &&
  979. ( view.lastEventData.eventData[0].screenPosition[0] != oldMouseX ||
  980. view.lastEventData.eventData[0].screenPosition[1] != oldMouseY ) ) {
  981. oldMouseX = view.lastEventData.eventData[0].screenPosition[0];
  982. oldMouseY = view.lastEventData.eventData[0].screenPosition[1];
  983. hovering = false;
  984. }
  985. else if(self.lastEventData && self.mouseOverCanvas && !hovering && newPick) {
  986. view.kernel.dispatchEvent( newPickId, "pointerHover", self.lastEventData.eventData, self.lastEventData.eventNodeData );
  987. hovering = true;
  988. }
  989. self.lastPickId = newPickId;
  990. self.lastPick = newPick;
  991. lastPickTime = now;
  992. }
  993. self.mouseJustEnteredCanvas = false;
  994. }
  995. if ( enableStereo && sceneNode && sceneNode.stereo ) {
  996. if ( mobileDevice && sceneNode.stereo.controls ) {
  997. sceneNode.stereo.controls.update( timepassed );
  998. }
  999. sceneNode.stereo.effect.render( scene, camera );
  1000. } else {
  1001. self.render( renderer, scene, camera );
  1002. }
  1003. sceneNode.lastTime = now;
  1004. if ( self.interpolateTransforms ) {
  1005. restoreTransforms();
  1006. }
  1007. };
  1008. var mycanvas = this.canvasQuery.get( 0 );
  1009. function detectWebGL()
  1010. {
  1011. var asa; var canvas; var dcanvas; var gl; var expmt;
  1012. $(document.body).append('<canvas width="100" height="100" id="testWebGLSupport" />');
  1013. canvas = $('#testWebGLSupport');
  1014. console.log(canvas);
  1015. // check to see if we can do webgl
  1016. // ALERT FOR JQUERY PEEPS: canvas is a jquery obj - access the dom obj at canvas[0]
  1017. dcanvas = canvas[0];
  1018. expmt = false;
  1019. if ("WebGLRenderingContext" in window) {
  1020. console.log("browser at least knows what webgl is.");
  1021. }
  1022. // some browsers don't have a .getContext for canvas...
  1023. try { gl = dcanvas.getContext("webgl"); }
  1024. catch (x) { gl = null; }
  1025. if (gl == null) {
  1026. try { gl = dcanvas.getContext("experimental-webgl"); }
  1027. catch (x) { gl = null; }
  1028. if (gl == null) { console.log('but can\'t speak it'); }
  1029. else { expmt = true; console.log('and speaks it experimentally.'); }
  1030. } else {
  1031. console.log('and speaks it natively.');
  1032. }
  1033. if (gl || expmt) {
  1034. console.log("loading webgl content."); canvas.remove(); return true;
  1035. } else {
  1036. console.log("image-only fallback. no webgl.");
  1037. canvas.remove();
  1038. return false;
  1039. }
  1040. }
  1041. function getURLParameter(name) {
  1042. return decodeURI(
  1043. (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]
  1044. );
  1045. }
  1046. if ( mycanvas ) {
  1047. var oldMouseX = 0;
  1048. var oldMouseY = 0;
  1049. var hovering = false;
  1050. var view = this;
  1051. var viewCam;
  1052. window.onresize = function () {
  1053. var origWidth = self.width;
  1054. var origHeight = self.height;
  1055. var viewWidth = mycanvas.width / sceneNode.renderer.devicePixelRatio;
  1056. var viewHeight = mycanvas.height / sceneNode.renderer.devicePixelRatio;
  1057. if ( window && window.innerHeight ) self.height = window.innerHeight;
  1058. if ( window && window.innerWidth ) self.width = window.innerWidth;
  1059. if ( ( origWidth != self.width || origHeight != self.height ) ) {
  1060. // If canvas changed size, use canvas dimentions instead
  1061. if ( viewWidth != mycanvas.clientWidth || viewHeight != mycanvas.clientHeight ) {
  1062. self.width = mycanvas.clientWidth;
  1063. self.height = mycanvas.clientHeight;
  1064. }
  1065. sceneNode.renderer.setSize( self.width, self.height, true );
  1066. if ( enableStereo && sceneNode.stereo && sceneNode.stereo.effect ) {
  1067. sceneNode.stereo.effect.setSize( self.width, self.height );
  1068. }
  1069. }
  1070. viewCam = view.state.cameraInUse;
  1071. if ( viewCam ) {
  1072. viewCam.aspect = mycanvas.clientWidth / mycanvas.clientHeight;
  1073. viewCam.updateProjectionMatrix();
  1074. }
  1075. }
  1076. if ( detectWebGL() && getURLParameter('disableWebGL') == 'null' ){
  1077. sceneNode.renderer = new THREE.WebGLRenderer( { canvas: mycanvas, antialias: true } );
  1078. } else {
  1079. sceneNode.renderer = new THREE.CanvasRenderer( { canvas: mycanvas, antialias: true } );
  1080. sceneNode.renderer.setSize( window.innerWidth,window.innerHeight );
  1081. }
  1082. sceneNode.renderer.setSize( self.width, self.height, true );
  1083. // backgroundColor, enableShadows, shadowMapCullFace and shadowMapType are dependent on the renderer object, but if they are set in a prototype,
  1084. // the renderer is not available yet, so set them now.
  1085. for(var key in sceneNode.rendererProperties) {
  1086. if(key == "backgroundColor") {
  1087. var vwfColor = new utility.color( sceneNode.rendererProperties["backgroundColor"] );
  1088. if ( vwfColor ) {
  1089. sceneNode.renderer.setClearColor( vwfColor.getHex(), vwfColor.alpha() );
  1090. }
  1091. }
  1092. else if(key == "enableShadows") {
  1093. value = Boolean( sceneNode.rendererProperties["enableShadows"] );
  1094. sceneNode.renderer.shadowMapEnabled = value;
  1095. }
  1096. else if(key == 'shadowMapCullFace') {
  1097. sceneNode.renderer.shadowMapCullFace = Number( sceneNode.rendererProperties["shadowMapCullFace"] );
  1098. }
  1099. else if(key == 'shadowMapType') {
  1100. sceneNode.renderer.shadowMapType = Number( sceneNode.rendererProperties["shadowMapType"] );
  1101. }
  1102. }
  1103. rebuildAllMaterials.call(this);
  1104. if ( sceneNode.renderer.setFaceCulling )
  1105. sceneNode.renderer.setFaceCulling( THREE.CullFaceBack );
  1106. // Schedule the renderer.
  1107. var scene = sceneNode.threeScene;
  1108. var renderer = sceneNode.renderer;
  1109. var scenenode = sceneNode;
  1110. window._dScene = scene;
  1111. window._dRenderer = renderer;
  1112. window._dSceneNode = sceneNode;
  1113. if ( this.enableInputs ) {
  1114. initInputEvents.call(this,mycanvas);
  1115. }
  1116. renderScene( ( +new Date ) );
  1117. }
  1118. // If scene is already loaded, find the user's navigation object
  1119. var sceneView = this;
  1120. var appID = sceneView.kernel.application( true );
  1121. if ( appID ) {
  1122. this.state.appInitialized = true;
  1123. }
  1124. } // initScene
  1125. function rebuildAllMaterials(start)
  1126. {
  1127. if(!start)
  1128. {
  1129. for(var i in this.state.scenes)
  1130. {
  1131. rebuildAllMaterials(this.state.scenes[i].threeScene);
  1132. }
  1133. }else
  1134. {
  1135. if(start && start.material)
  1136. {
  1137. start.material.needsUpdate = true;
  1138. }
  1139. if(start && start.children)
  1140. {
  1141. for(var i in start.children)
  1142. rebuildAllMaterials(start.children[i]);
  1143. }
  1144. }
  1145. }
  1146. // -- initInputEvents ------------------------------------------------------------------------
  1147. function initInputEvents( canvas ) {
  1148. var sceneID = this.kernel.application();
  1149. var sceneNode = this.state.scenes[ sceneID ], child;
  1150. var sceneView = this;
  1151. var touchID = undefined;
  1152. var touchPick = undefined;
  1153. var pointerDownID = undefined;
  1154. var pointerOverID = undefined;
  1155. var pointerPickID = undefined;
  1156. var threeActualObj = undefined;
  1157. var win = window;
  1158. var container = document.getElementById( "container" );
  1159. var sceneCanvas = canvas;
  1160. //var mouse = new GLGE.MouseInput( sceneCanvas );
  1161. canvas.requestPointerLock = canvas.requestPointerLock ||
  1162. canvas.mozRequestPointerLock ||
  1163. canvas.webkitRequestPointerLock ||
  1164. function() {};
  1165. document.exitPointerLock = document.exitPointerLock ||
  1166. document.mozExitPointerLock ||
  1167. document.webkitExitPointerLock ||
  1168. function() {};
  1169. var getEventData = function( e, debug ) {
  1170. var returnData = { eventData: undefined, eventNodeData: undefined };
  1171. var pickInfo = self.lastPick;
  1172. pointerPickID = undefined;
  1173. threeActualObj = pickInfo ? pickInfo.object : undefined;
  1174. pointerPickID = pickInfo ? getPickObjectID.call( sceneView, pickInfo.object, debug ) : undefined;
  1175. var mouseButton = "left";
  1176. switch( e.button ) {
  1177. case 2:
  1178. mouseButton = "right";
  1179. break;
  1180. case 1:
  1181. mouseButton = "middle";
  1182. break;
  1183. default:
  1184. mouseButton = "left";
  1185. break;
  1186. };
  1187. var mousePos = utility.coordinates.contentFromWindow( e.target, { x: e.clientX, y: e.clientY } ); // canvas coordinates from window coordinates
  1188. returnData.eventData = [ {
  1189. /*client: "123456789ABCDEFG", */
  1190. button: mouseButton,
  1191. clicks: 1,
  1192. buttons: {
  1193. left: mouseDown.left,
  1194. middle: mouseDown.middle,
  1195. right: mouseDown.right,
  1196. },
  1197. gestures: touchGesture,
  1198. modifiers: {
  1199. alt: e.altKey,
  1200. ctrl: e.ctrlKey,
  1201. shift: e.shiftKey,
  1202. meta: e.metaKey,
  1203. },
  1204. position: [ mousePos.x / canvas.clientWidth, mousePos.y / canvas.clientHeight ],
  1205. screenPosition: [ mousePos.x, mousePos.y ]
  1206. } ];
  1207. if ( pointerLocked ) {
  1208. returnData.eventData.movementX = e.movementX || e.mozMovementX || e.webkitMovementX || 0;
  1209. returnData.eventData.movementY = e.movementY || e.mozMovementY || e.webkitMovementY || 0;
  1210. }
  1211. var camera = sceneView.state.cameraInUse;
  1212. var worldCamPos, worldCamTrans, camInverse;
  1213. var localPickNormal, worldPickNormal, worldTransform;
  1214. if ( camera ) {
  1215. var worldCamTrans = new THREE.Vector3();
  1216. worldCamTrans.setFromMatrixPosition( camera.matrixWorld );
  1217. // Convert THREE.Vector3 to array
  1218. // QUESTION: Is the double use of y a bug? I would assume so, but then why not
  1219. // just use worldCamTrans as-is?
  1220. worldCamPos = [ worldCamTrans.x, worldCamTrans.y, worldCamTrans.z];
  1221. }
  1222. if ( pickInfo ) {
  1223. if ( sceneView.state.nodes[ pointerPickID ] ) {
  1224. var pickObj = sceneView.state.nodes[ pointerPickID ];
  1225. var nml;
  1226. if ( pickObj.threeObject.matrixWorld ) {
  1227. worldTransform = goog.vec.Mat4.createFromArray( pickObj.threeObject.matrixWorld.elements );
  1228. } else {
  1229. worldTransform = goog.vec.Mat4.createFromArray( getWorldTransform( pickObj ).elements );
  1230. }
  1231. if ( pickInfo.face ) {
  1232. nml = pickInfo.face.normal
  1233. localPickNormal = goog.vec.Vec3.createFloat32FromValues( nml.x, nml.y, nml.z );
  1234. } else if ( pickInfo.normal ) {
  1235. nml = pickInfo.normal;
  1236. localPickNormal = goog.vec.Vec3.createFloat32FromValues( nml[0], nml[1], nml[2] );
  1237. }
  1238. if ( localPickNormal !== undefined ) {
  1239. localPickNormal = goog.vec.Vec3.normalize( localPickNormal, goog.vec.Vec3.create() );
  1240. worldPickNormal = goog.vec.Mat4.multVec3NoTranslate( worldTransform, localPickNormal, goog.vec.Vec3.create() );
  1241. }
  1242. }
  1243. }
  1244. returnData.eventNodeData = { "": [ {
  1245. pickID: pointerPickID,
  1246. pointerVector: pickDirection ? vec3ToArray( pickDirection ) : undefined,
  1247. distance: pickInfo ? pickInfo.distance : undefined,
  1248. origin: pickInfo ? pickInfo.worldCamPos : undefined,
  1249. globalPosition: pickInfo ? [pickInfo.point.x,pickInfo.point.y,pickInfo.point.z] : undefined,
  1250. globalNormal: worldPickNormal ? worldPickNormal : localPickNormal, //** not implemented by threejs
  1251. globalSource: worldCamPos
  1252. } ] };
  1253. if ( sceneView && sceneView.state.nodes[ pointerPickID ] ) {
  1254. var camera = sceneView.state.cameraInUse;
  1255. var childID = pointerPickID;
  1256. var child = sceneView.state.nodes[ childID ];
  1257. var parentID = child.parentID;
  1258. var parent = sceneView.state.nodes[ child.parentID ];
  1259. var transform, parentTrans, localTrans, localNormal, parentInverse, relativeCamPos;
  1260. while ( child ) {
  1261. transform = goog.vec.Mat4.createFromArray( child.threeObject.matrix.elements );
  1262. goog.vec.Mat4.transpose( transform, transform );
  1263. if ( parent ) {
  1264. parentTrans = goog.vec.Mat4.createFromArray( parent.threeObject.matrix.elements );
  1265. goog.vec.Mat4.transpose( parentTrans, parentTrans );
  1266. } else {
  1267. parentTrans = undefined;
  1268. }
  1269. if ( transform && parentTrans ) {
  1270. // get the parent inverse, and multiply by the world
  1271. // transform to get the local transform
  1272. parentInverse = goog.vec.Mat4.create();
  1273. if ( goog.vec.Mat4.invert( parentTrans, parentInverse ) ) {
  1274. localTrans = goog.vec.Mat4.multMat( parentInverse, transform,
  1275. goog.vec.Mat4.create()
  1276. );
  1277. }
  1278. }
  1279. // transform the global normal into local
  1280. if ( transform && pickInfo && pickInfo.face ) {
  1281. localNormal = goog.vec.Mat4.multVec3Projective( transform, pickInfo.face.normal,
  1282. goog.vec.Vec3.create() );
  1283. } else {
  1284. localNormal = undefined;
  1285. }
  1286. if ( worldCamPos ) {
  1287. relativeCamPos = goog.vec.Mat4.multVec3Projective( transform, worldCamPos,
  1288. goog.vec.Vec3.create() );
  1289. } else {
  1290. relativeCamPos = undefined;
  1291. }
  1292. returnData.eventNodeData[ childID ] = [ {
  1293. pickID: pointerPickID,
  1294. pointerVector: pickDirection ? vec3ToArray( pickDirection ) : undefined,
  1295. position: localTrans,
  1296. normal: localNormal,
  1297. source: relativeCamPos,
  1298. distance: pickInfo ? pickInfo.distance : undefined,
  1299. globalPosition: pickInfo ? [pickInfo.point.x,pickInfo.point.y,pickInfo.point.z] : undefined,
  1300. globalNormal: worldPickNormal ? worldPickNormal : localPickNormal,
  1301. globalSource: worldCamPos,
  1302. } ];
  1303. childID = parentID;
  1304. child = sceneView.state.nodes[ childID ];
  1305. parentID = child ? child.parentID : undefined;
  1306. parent = parentID ? sceneView.state.nodes[ child.parentID ] : undefined;
  1307. }
  1308. }
  1309. self.lastEventData = returnData;
  1310. return returnData;
  1311. }
  1312. var getTouchEventData = function( e, debug ) {
  1313. var returnData = { eventData: undefined, eventNodeData: undefined };
  1314. var mousePos = utility.coordinates.contentFromWindow( e.target, { x: e.gesture.center.pageX, y: e.gesture.center.pageY } ); // canvas coordinates from window coordinates
  1315. touchPick = ThreeJSTouchPick.call( self, canvas, sceneNode, mousePos );
  1316. var pickInfo = touchPick;
  1317. var gestureTouches = {};
  1318. for (var i = 0; i < e.gesture.touches.length; i++) {
  1319. gestureTouches[i] = {x: e.gesture.touches[i].clientX, y: e.gesture.touches[i].clientY};
  1320. gestureTouches.length = i + 1;
  1321. }
  1322. returnData.eventData = [ {
  1323. gestures: touchGesture,
  1324. position: [ mousePos.x / sceneView.width, mousePos.y / sceneView.height ],
  1325. screenPosition: [ mousePos.x, mousePos.y ],
  1326. angle: e.gesture.angle,
  1327. touches: gestureTouches
  1328. } ];
  1329. returnData.eventNodeData = { "": [ {
  1330. distance: pickInfo ? pickInfo.distance : undefined,
  1331. globalPosition: pickInfo ? [pickInfo.point.x,pickInfo.point.y,pickInfo.point.z] : undefined
  1332. } ] };
  1333. if ( returnData.eventNodeData[ "" ][ 0 ].globalPosition ) {
  1334. positionUnderMouseClick = returnData.eventNodeData[ "" ][ 0 ].globalPosition;
  1335. }
  1336. self.lastEventData = returnData;
  1337. return returnData;
  1338. }
  1339. // Do not emulate mouse events on touch
  1340. Hammer.NO_MOUSEEVENTS = true;
  1341. $(canvas).hammer({ drag_lock_to_axis: false }).on("touch release", handleHammer);
  1342. $(canvas).hammer({ drag_lock_to_axis: false }).on("hold tap doubletap", handleHammer);
  1343. $(canvas).hammer({ drag_lock_to_axis: false }).on("drag dragstart dragend dragup dragdown dragleft dragright", handleHammer);
  1344. $(canvas).hammer({ drag_lock_to_axis: false }).on("swipe swipeup swipedown swipeleft,swiperight", handleHammer);
  1345. $(canvas).hammer({ drag_lock_to_axis: false }).on("transform transformstart transformend", handleHammer);
  1346. $(canvas).hammer({ drag_lock_to_axis: false }).on("rotate", handleHammer);
  1347. $(canvas).hammer({ drag_lock_to_axis: false }).on("pinch pinchin pinchout", handleHammer);
  1348. function handleHammer( ev ) {
  1349. // disable browser scrolling
  1350. ev.gesture.preventDefault();
  1351. var eData = getTouchEventData( ev, false );
  1352. touchID = touchPick ? getPickObjectID.call( sceneView, touchPick.object, false ) : sceneID;
  1353. switch(ev.type) {
  1354. case 'hold':
  1355. sceneView.kernel.dispatchEvent( touchID, "touchHold", eData.eventData, eData.eventNodeData );
  1356. break;
  1357. case 'tap':
  1358. sceneView.kernel.dispatchEvent( touchID, "touchTap", eData.eventData, eData.eventNodeData );
  1359. // Emulate pointer events
  1360. eData.eventData[0].button = "left";
  1361. sceneView.kernel.dispatchEvent( touchID, "pointerClick", eData.eventData, eData.eventNodeData );
  1362. sceneView.kernel.dispatchEvent( touchID, "pointerDown", eData.eventData, eData.eventNodeData );
  1363. sceneView.kernel.dispatchEvent( touchID, "pointerUp", eData.eventData, eData.eventNodeData );
  1364. break;
  1365. case 'doubletap':
  1366. sceneView.kernel.dispatchEvent( touchID, "touchDoubleTap", eData.eventData, eData.eventNodeData );
  1367. break;
  1368. case 'drag':
  1369. // Fly or Orbit Navigation Behavior
  1370. if ( touchmode != "none") {
  1371. if ( prevGesture == "drag" || prevGesture == "dragleft" || prevGesture == "dragright") {
  1372. self.handleTouchNavigation( eData.eventData );
  1373. }
  1374. }
  1375. sceneView.kernel.dispatchEvent( touchID, "touchDrag", eData.eventData, eData.eventNodeData );
  1376. break;
  1377. case 'dragstart':
  1378. sceneView.kernel.dispatchEvent( touchID, "touchDragStart", eData.eventData, eData.eventNodeData );
  1379. break;
  1380. case 'dragend':
  1381. sceneView.kernel.dispatchEvent( touchID, "touchDragEnd", eData.eventData, eData.eventNodeData );
  1382. break;
  1383. case 'dragup':
  1384. sceneView.kernel.dispatchEvent( touchID, "touchDragUp", eData.eventData, eData.eventNodeData );
  1385. break;
  1386. case 'dragdown':
  1387. sceneView.kernel.dispatchEvent( touchID, "touchDragDown", eData.eventData, eData.eventNodeData );
  1388. break;
  1389. case 'dragleft':
  1390. sceneView.kernel.dispatchEvent( touchID, "touchDragLeft", eData.eventData, eData.eventNodeData );
  1391. break;
  1392. case 'dragright':
  1393. sceneView.kernel.dispatchEvent( touchID, "touchDragRight", eData.eventData, eData.eventNodeData );
  1394. break;
  1395. case 'swipe':
  1396. sceneView.kernel.dispatchEvent( touchID, "touchSwipe", eData.eventData, eData.eventNodeData );
  1397. break;
  1398. case 'swipeup':
  1399. sceneView.kernel.dispatchEvent( touchID, "touchSwipeUp", eData.eventData, eData.eventNodeData );
  1400. break;
  1401. case 'swipedown':
  1402. sceneView.kernel.dispatchEvent( touchID, "touchSwipeDown", eData.eventData, eData.eventNodeData );
  1403. break;
  1404. case 'swipeleft':
  1405. sceneView.kernel.dispatchEvent( touchID, "touchSwipeLeft", eData.eventData, eData.eventNodeData );
  1406. break;
  1407. case 'swiperight':
  1408. sceneView.kernel.dispatchEvent( touchID, "touchSwipeRight", eData.eventData, eData.eventNodeData );
  1409. break;
  1410. case 'transform':
  1411. sceneView.kernel.dispatchEvent( touchID, "touchTransform", eData.eventData, eData.eventNodeData );
  1412. break;
  1413. case 'transformstart':
  1414. sceneView.kernel.dispatchEvent( touchID, "touchTransformStart", eData.eventData, eData.eventNodeData );
  1415. break;
  1416. case 'transformend':
  1417. sceneView.kernel.dispatchEvent( touchID, "touchTransformEnd", eData.eventData, eData.eventNodeData );
  1418. break;
  1419. case 'rotate':
  1420. sceneView.kernel.dispatchEvent( touchID, "touchRotate", eData.eventData, eData.eventNodeData );
  1421. break;
  1422. case 'pinch':
  1423. sceneView.kernel.dispatchEvent( touchID, "touchPinch", eData.eventData, eData.eventNodeData );
  1424. break;
  1425. case 'pinchin':
  1426. // Zoom Out
  1427. if ( touchmode != "none" ) {
  1428. inputHandleScroll( ev.gesture.scale, eData.eventNodeData[ "" ][ 0 ].distance );
  1429. }
  1430. sceneView.kernel.dispatchEvent( touchID, "touchPinchIn", eData.eventData, eData.eventNodeData );
  1431. break;
  1432. case 'pinchout':
  1433. // Zoom In
  1434. if ( touchmode != "none" ) {
  1435. inputHandleScroll( -1 * ev.gesture.scale, eData.eventNodeData[ "" ][ 0 ].distance );
  1436. }
  1437. sceneView.kernel.dispatchEvent( touchID, "touchPinchOut", eData.eventData, eData.eventNodeData );
  1438. break;
  1439. case 'touch':
  1440. touchGesture = true;
  1441. sceneView.kernel.dispatchEvent( touchID, "touchStart", eData.eventData, eData.eventNodeData );
  1442. break;
  1443. case 'release':
  1444. touchGesture = false;
  1445. sceneView.kernel.dispatchEvent( touchID, "touchRelease", eData.eventData, eData.eventNodeData );
  1446. break;
  1447. }
  1448. // Set previous gesture (only perform drag if the previous is not a pinch gesture - causes jumpiness)
  1449. prevGesture = ev.type;
  1450. }
  1451. canvas.onmousedown = function( e ) {
  1452. // Set appropriate button / key states
  1453. // Shift+click of any button is treated as the middle button to accomodate mice that
  1454. // don't have a middle button
  1455. var shiftDown = e.shiftKey;
  1456. switch( e.button ) {
  1457. case 0: // Left button
  1458. if ( shiftDown ) {
  1459. mouseDown.middle = true;
  1460. } else {
  1461. mouseDown.left = true;
  1462. }
  1463. break;
  1464. case 1: // Middle button
  1465. mouseDown.middle = true;
  1466. break;
  1467. case 2: // Right button
  1468. if ( shiftDown ) {
  1469. mouseDown.middle = true;
  1470. } else {
  1471. mouseDown.right = true;
  1472. }
  1473. break;
  1474. };
  1475. // Set pointerLock if appropriate
  1476. var event = getEventData( e, false );
  1477. if ( pointerLockImplemented && self.appRequestsPointerLock( navmode, mouseDown ) ) {
  1478. // HACK: This is to deal with an issue with webkitMovementX in Chrome:
  1479. nextMouseMoveIsErroneous = true;
  1480. nextTwoMouseMovesAreErroneous = true;
  1481. // END HACK
  1482. canvas.requestPointerLock();
  1483. positionUnderMouseClick = event && event.eventNodeData[ "" ][ 0 ].globalPosition;
  1484. }
  1485. // Process mouse down event
  1486. if ( event ) {
  1487. pointerDownID = pointerPickID ? pointerPickID : sceneID;
  1488. sceneView.kernel.dispatchEvent( pointerDownID, "pointerDown", event.eventData, event.eventNodeData );
  1489. // TODO: Navigation - see main "TODO: Navigation" comment for explanation
  1490. startMousePosition = event.eventData[ 0 ].position;
  1491. // END TODO
  1492. }
  1493. e.preventDefault();
  1494. }
  1495. // Listen for onmouseup from the document (instead of the canvas like all the other mouse events)
  1496. // because it will catch mouseup events that occur outside the window, whereas canvas.onmouseup does
  1497. // not.
  1498. document.onmouseup = function( e ) {
  1499. // Set appropriate button / key states
  1500. var ctrlDown = e.ctrlKey;
  1501. var atlDown = e.altKey;
  1502. var ctrlAndAltDown = ctrlDown && atlDown;
  1503. // Shift+click w/ any button is considered a middle click to accomodate mice w/o a
  1504. // middle mouse button. Therefore, if the left or right mouse button is released,
  1505. // but the system did not record that it was down, it must be a Shift+click for the
  1506. // middle mouse button that was released
  1507. switch( e.button ) {
  1508. case 0: // Left button
  1509. if ( mouseDown.left ) {
  1510. mouseDown.left = false;
  1511. } else {
  1512. mouseDown.middle = false;
  1513. }
  1514. break;
  1515. case 1: // Middle button
  1516. mouseDown.middle = false;
  1517. break;
  1518. case 2: // Right button
  1519. if ( mouseDown.right ) {
  1520. mouseDown.right = false;
  1521. } else {
  1522. mouseDown.middle = false;
  1523. }
  1524. break;
  1525. };
  1526. // Release pointerLock if appropriate
  1527. if ( pointerLockImplemented && !self.appRequestsPointerLock( navmode, mouseDown ) ) {
  1528. document.exitPointerLock();
  1529. }
  1530. // Process mouse up event
  1531. var eData = getEventData( e, ctrlAndAltDown );
  1532. if ( eData !== undefined ) {
  1533. var mouseUpObjectID = pointerPickID;
  1534. if ( mouseUpObjectID && pointerDownID && mouseUpObjectID == pointerDownID ) {
  1535. sceneView.kernel.dispatchEvent( mouseUpObjectID, "pointerClick", eData.eventData, eData.eventNodeData );
  1536. // TODO: hierarchy output, helpful for setting up applications
  1537. var objNode = sceneView.state.nodes[mouseUpObjectID];
  1538. var obj3js = objNode.threeObject;
  1539. if ( obj3js ) {
  1540. if ( atlDown && !ctrlDown ) {
  1541. var colladaParent = obj3js;
  1542. while ( colladaParent.parent ) {
  1543. if ( colladaParent.loadedColladaNode ) {
  1544. break;
  1545. } else {
  1546. colladaParent = colladaParent.parent;
  1547. }
  1548. }
  1549. if ( colladaParent === undefined ) {
  1550. colladaParent = obj3js;
  1551. }
  1552. console.info( "===== YAML ===== START" );
  1553. recurseObject3D.call( sceneView, colladaParent, "", 0 );
  1554. console.info( "===== YAML ===== END" );
  1555. console.info( "===== JSON ===== START" );
  1556. recurseJsonObject3D.call( sceneView, colladaParent, "", 0 );
  1557. console.info( "===== JSON ===== END" );
  1558. console.info( "===== THREEJS ===== START" );
  1559. consoleScene.call( this, sceneNode.threeScene, 0 );
  1560. console.info( "===== THREEJS ===== END" );
  1561. }
  1562. }
  1563. } else {
  1564. if ( atlDown && !ctrlDown ) {
  1565. recurseObject3D.call( sceneView, sceneNode.threeScene, "", 0 );
  1566. consoleScene.call( this, sceneNode.threeScene, 0 );
  1567. }
  1568. }
  1569. if ( pointerDownID ) {
  1570. sceneView.kernel.dispatchEvent( pointerDownID, "pointerUp", eData.eventData,
  1571. eData.eventNodeData );
  1572. }
  1573. }
  1574. if ( !( mouseDown.left || mouseDown.right || mouseDown.middle ) ) {
  1575. pointerDownID = undefined;
  1576. // TODO: Navigation - see main "TODO: Navigation" comment for explanation
  1577. startMousePosition = undefined;
  1578. // END TODO
  1579. }
  1580. e.preventDefault();
  1581. }
  1582. canvas.onmouseover = function( e ) {
  1583. if ( !self.mouseOverCanvas ) {
  1584. self.mouseJustEnteredCanvas = true;
  1585. self.mouseOverCanvas = true;
  1586. }
  1587. var eData = getEventData( e, false );
  1588. if ( eData ) {
  1589. pointerOverID = pointerPickID ? pointerPickID : sceneID;
  1590. sceneView.kernel.dispatchEvent( pointerOverID, "pointerOver", eData.eventData, eData.eventNodeData );
  1591. }
  1592. e.preventDefault();
  1593. }
  1594. canvas.onmousemove = function( e ) {
  1595. // HACK: This is to deal with an issue with webkitMovementX in Chrome:
  1596. if ( nextMouseMoveIsErroneous ) {
  1597. if ( nextTwoMouseMovesAreErroneous ) {
  1598. nextTwoMouseMovesAreErroneous = false;
  1599. } else {
  1600. nextMouseMoveIsErroneous = false;
  1601. }
  1602. return;
  1603. }
  1604. // END HACK
  1605. var eData = getEventData( e, false );
  1606. if ( eData ) {
  1607. if ( mouseDown.left || mouseDown.right || mouseDown.middle ) {
  1608. // TODO: Navigation - see main "TODO: Navigation" comment for explanation
  1609. if ( navmode != "none" ) {
  1610. if ( cameraNode ) {
  1611. if ( !cameraNode.lookatval ) {
  1612. inputHandleMouseNavigation( eData.eventData );
  1613. }
  1614. } else {
  1615. self.logger.warnx( "canvas.onmousemove: camera does not exist" );
  1616. }
  1617. }
  1618. // END TODO
  1619. sceneView.kernel.dispatchEvent( pointerDownID, "pointerMove", eData.eventData, eData.eventNodeData );
  1620. } else {
  1621. if ( pointerPickID ) {
  1622. if ( pointerOverID ) {
  1623. if ( pointerPickID != pointerOverID ) {
  1624. sceneView.kernel.dispatchEvent( pointerOverID, "pointerOut", eData.eventData, eData.eventNodeData );
  1625. pointerOverID = pointerPickID;
  1626. sceneView.kernel.dispatchEvent( pointerOverID, "pointerOver", eData.eventData, eData.eventNodeData );
  1627. }
  1628. } else {
  1629. pointerOverID = pointerPickID;
  1630. sceneView.kernel.dispatchEvent( pointerOverID, "pointerOver", eData.eventData, eData.eventNodeData );
  1631. }
  1632. } else {
  1633. if ( pointerOverID ) {
  1634. sceneView.kernel.dispatchEvent( pointerOverID, "pointerOut", eData.eventData, eData.eventNodeData );
  1635. pointerOverID = undefined;
  1636. }
  1637. }
  1638. }
  1639. }
  1640. e.preventDefault();
  1641. }
  1642. canvas.onmouseout = function( e ) {
  1643. if ( pointerOverID ) {
  1644. sceneView.kernel.dispatchEvent( pointerOverID, "pointerOut" );
  1645. pointerOverID = undefined;
  1646. }
  1647. self.mouseOverCanvas = false;
  1648. e.preventDefault();
  1649. }
  1650. canvas.setAttribute( "onmousewheel", '' );
  1651. window.onkeydown = function (event) {
  1652. var key = undefined;
  1653. var validKey = false;
  1654. var keyAlreadyDown = false;
  1655. switch ( event.keyCode ) {
  1656. case 17:
  1657. case 16:
  1658. case 18:
  1659. case 19:
  1660. case 20:
  1661. break;
  1662. default:
  1663. key = getKeyValue.call( sceneView, event.keyCode);
  1664. keyAlreadyDown = !!sceneView.keyStates.keysDown[key.key];
  1665. sceneView.keyStates.keysDown[key.key] = key;
  1666. validKey = true;
  1667. // TODO: Navigation - see main "TODO: Navigation" comment for explanation
  1668. handleKeyNavigation( event.keyCode, true );
  1669. // END TODO
  1670. break;
  1671. }
  1672. if (!sceneView.keyStates.mods) sceneView.keyStates.mods = {};
  1673. sceneView.keyStates.mods.alt = event.altKey;
  1674. sceneView.keyStates.mods.shift = event.shiftKey;
  1675. sceneView.keyStates.mods.ctrl = event.ctrlKey;
  1676. sceneView.keyStates.mods.meta = event.metaKey;
  1677. var sceneNode = sceneView.state.scenes[ sceneView.kernel.application() ];
  1678. if (validKey && sceneNode && !keyAlreadyDown /*&& Object.keys( sceneView.keyStates.keysDown ).length > 0*/) {
  1679. //var params = JSON.stringify( sceneView.keyStates );
  1680. sceneView.kernel.dispatchEvent(sceneNode.ID, "keyDown", [sceneView.keyStates]);
  1681. }
  1682. };
  1683. window.onkeyup = function (event) {
  1684. var key = undefined;
  1685. var validKey = false;
  1686. switch (event.keyCode) {
  1687. case 16:
  1688. case 17:
  1689. case 18:
  1690. case 19:
  1691. case 20:
  1692. break;
  1693. default:
  1694. key = getKeyValue.call( sceneView, event.keyCode);
  1695. delete sceneView.keyStates.keysDown[key.key];
  1696. sceneView.keyStates.keysUp[key.key] = key;
  1697. validKey = true;
  1698. // TODO: Navigation - see main "TODO: Navigation" comment for explanation
  1699. handleKeyNavigation( event.keyCode, false );
  1700. // END TODO
  1701. break;
  1702. }
  1703. sceneView.keyStates.mods.alt = event.altKey;
  1704. sceneView.keyStates.mods.shift = event.shiftKey;
  1705. sceneView.keyStates.mods.ctrl = event.ctrlKey;
  1706. sceneView.keyStates.mods.meta = event.metaKey;
  1707. var sceneNode = sceneView.state.scenes[ sceneView.kernel.application() ];
  1708. if (validKey && sceneNode) {
  1709. //var params = JSON.stringify( sceneView.keyStates );
  1710. sceneView.kernel.dispatchEvent(sceneNode.ID, "keyUp", [sceneView.keyStates]);
  1711. delete sceneView.keyStates.keysUp[key.key];
  1712. }
  1713. };
  1714. window.oncontextmenu = function() {
  1715. if ( navmode == "none" )
  1716. return true;
  1717. else
  1718. return false;
  1719. }
  1720. window.onblur = function() {
  1721. // Stop all key movement when window goes out of focus since key events are now going to a
  1722. // different window
  1723. movingForward = false;
  1724. movingBack = false;
  1725. movingLeft = false;
  1726. movingRight = false;
  1727. rotatingLeft = false;
  1728. rotatingRight = false;
  1729. }
  1730. // As of this writing, Chrome and Opera Next use canvas.onmousewheel
  1731. // Firefox uses canvas.onwheel
  1732. if ( canvas.onmousewheel !== undefined ) {
  1733. canvas.removeAttribute("onmousewheel");
  1734. canvas.onmousewheel = function( e ) {
  1735. var eData = getEventData( e, false );
  1736. if ( eData ) {
  1737. var eventNodeData = eData.eventNodeData[ "" ][ 0 ];
  1738. eventNodeData.wheel = {
  1739. delta: e.wheelDelta / -40,
  1740. deltaX: e.wheelDeltaX / -40,
  1741. deltaY: e.wheelDeltaY / -40,
  1742. };
  1743. var id = sceneID;
  1744. if ( pointerDownID && mouseDown.right || mouseDown.left || mouseDown.middle )
  1745. id = pointerDownID;
  1746. else if ( pointerOverID )
  1747. id = pointerOverID;
  1748. sceneView.kernel.dispatchEvent( id, "pointerWheel", eData.eventData, eData.eventNodeData );
  1749. inputHandleScroll( eventNodeData.wheel.delta, eventNodeData.distance );
  1750. }
  1751. };
  1752. } else if ( canvas.onwheel !== undefined ) {
  1753. canvas.removeAttribute("onmousewheel");
  1754. canvas.onwheel = function( e ) {
  1755. var eData = getEventData( e, false );
  1756. if ( eData ) {
  1757. if ( e.deltaMode != 1 ) {
  1758. self.logger.warnx( "canvas.onwheel: This browser uses an unsupported deltaMode: " +
  1759. e.deltaMode );
  1760. }
  1761. var eventNodeData = eData.eventNodeData[ "" ][ 0 ];
  1762. eventNodeData.wheel = {
  1763. delta: e.deltaY,
  1764. deltaX: e.deltaX,
  1765. deltaY: e.deltaY,
  1766. };
  1767. var id = sceneID;
  1768. if ( pointerDownID && mouseDown.right || mouseDown.left || mouseDown.middle )
  1769. id = pointerDownID;
  1770. else if ( pointerOverID )
  1771. id = pointerOverID;
  1772. sceneView.kernel.dispatchEvent( id, "pointerWheel", eData.eventData, eData.eventNodeData );
  1773. inputHandleScroll( eventNodeData.wheel.delta, eventNodeData.distance );
  1774. }
  1775. };
  1776. } else {
  1777. this.logger.warnx( "initInputEvents: Neither onmousewheel nor onwheel are supported in this " +
  1778. "browser so mouse scrolling is not supported - request that the VWF team " +
  1779. "support the DOMMouseScroll event to support your browser" );
  1780. }
  1781. // TODO: Navigation - This section should become a view component as soon as that system is available
  1782. // When altering this, search for other sections that say "TODO: Navigation"
  1783. var onPointerLockChange = function() {
  1784. if ( document.pointerLockElement === canvas ||
  1785. document.mozPointerLockElement === canvas ||
  1786. document.webkitPointerLockElement === canvas ) {
  1787. pointerLocked = true;
  1788. } else {
  1789. pointerLocked = false;
  1790. }
  1791. }
  1792. document.addEventListener( "pointerlockchange", onPointerLockChange, false);
  1793. document.addEventListener( "mozpointerlockchange", onPointerLockChange, false);
  1794. document.addEventListener( "webkitpointerlockchange", onPointerLockChange, false);
  1795. this.moveNavObject = function( x, y, navObj, navMode, rotationSpeed, translationSpeed, msSinceLastFrame ) {
  1796. var navThreeObject = navObj.threeObject;
  1797. // Compute the distance traveled in the elapsed time
  1798. // Constrain the time to be less than 0.5 seconds, so that if a user has a very low frame rate,
  1799. // one key press doesn't send them off in space
  1800. var dist = translationSpeed * Math.min( msSinceLastFrame * 0.001, 0.5 );
  1801. var dir = [ 0, 0, 0 ];
  1802. var camera = self.state.cameraInUse;
  1803. var cameraWorldTransformArray = camera.matrixWorld.elements;
  1804. var orbiting = ( navMode == "fly" ) && mouseDown.middle && positionUnderMouseClick;
  1805. if ( orbiting ) {
  1806. if ( y ) {
  1807. dir = [ positionUnderMouseClick[ 0 ] - cameraWorldTransformArray[ 12 ],
  1808. positionUnderMouseClick[ 1 ] - cameraWorldTransformArray[ 13 ],
  1809. positionUnderMouseClick[ 2 ] - cameraWorldTransformArray[ 14 ] ];
  1810. if ( y > 0 ) {
  1811. var distToOrbitTarget = Math.sqrt( dir[ 0 ] * dir[ 0 ] + dir[ 1 ] * dir[ 1 ] + dir[ 2 ] * dir[ 2 ] );
  1812. var epsilon = 0.01;
  1813. var almostDistToOrbit = distToOrbitTarget - epsilon;
  1814. if ( dist > almostDistToOrbit ) {
  1815. dist = almostDistToOrbit;
  1816. }
  1817. if ( dist < epsilon ) {
  1818. dir = [ 0, 0, 0 ];
  1819. }
  1820. } else {
  1821. dir = [ -dir[ 0 ], -dir[ 1 ], -dir[ 2 ] ];
  1822. }
  1823. }
  1824. if ( x ) {
  1825. var pitchRadians = 0;
  1826. var yawRadians = x * ( rotationSpeed * degreesToRadians ) *
  1827. Math.min( msSinceLastFrame * 0.001, 0.5 );
  1828. orbit( pitchRadians, yawRadians );
  1829. }
  1830. } else {
  1831. // Get the camera's rotation matrix in the world's frame of reference
  1832. // (remove its translation component so it is just a rotation matrix)
  1833. var camWorldRotMat = goog.vec.Mat4.createFromArray( cameraWorldTransformArray );
  1834. camWorldRotMat[ 12 ] = 0;
  1835. camWorldRotMat[ 13 ] = 0;
  1836. camWorldRotMat[ 14 ] = 0;
  1837. // Calculate a unit direction vector in the camera's parent's frame of reference
  1838. var moveVectorInCameraFrame = goog.vec.Vec4.createFromValues( x, 0, -y, 1 ); // Accounts for z-up (VWF) to y-up (three.js) change
  1839. moveVectorInCameraFrame = goog.vec.Vec4.createFromValues( x, 0, -y, 1 ); // Accounts for z-up (VWF) to y-up (three.js) change
  1840. dir = goog.vec.Mat4.multVec4( camWorldRotMat, moveVectorInCameraFrame, goog.vec.Vec3.create() );
  1841. }
  1842. // If user is walking, constrain movement to the horizontal plane
  1843. if ( navMode == "walk") {
  1844. dir[ 2 ] = 0;
  1845. }
  1846. var length = Math.sqrt( dir[ 0 ] * dir[ 0 ] + dir[ 1 ] * dir[ 1 ] + dir[ 2 ] * dir[ 2 ] );
  1847. if ( length ) {
  1848. goog.vec.Vec3.normalize( dir, dir );
  1849. // Extract the navObject world position so we can add to it
  1850. var navObjectWorldTransformMatrixArray = navThreeObject.matrixWorld.elements;
  1851. var navObjectWorldPos = [ navObjectWorldTransformMatrixArray[ 12 ],
  1852. navObjectWorldTransformMatrixArray[ 13 ],
  1853. navObjectWorldTransformMatrixArray[ 14 ] ];
  1854. // Take the direction and apply a calculated magnitude
  1855. // to that direction to compute the displacement vector
  1856. var deltaTranslation = goog.vec.Vec3.scale( dir, dist, goog.vec.Vec3.create() );
  1857. // Add the displacement to the current navObject position
  1858. goog.vec.Vec3.add( navObjectWorldPos, deltaTranslation, navObjectWorldPos );
  1859. if ( boundingBox != undefined ) {
  1860. if ( navObjectWorldPos[ 0 ] < boundingBox[ 0 ][ 0 ] ) {
  1861. navObjectWorldPos[ 0 ] = boundingBox[ 0 ][ 0 ];
  1862. }
  1863. else if ( navObjectWorldPos[ 0 ] > boundingBox[ 0 ][ 1 ] ) {
  1864. navObjectWorldPos[ 0 ] = boundingBox[ 0 ][ 1 ];
  1865. }
  1866. if ( navObjectWorldPos[ 1 ] < boundingBox[ 1 ][ 0 ] ) {
  1867. navObjectWorldPos[ 1 ] = boundingBox[ 1 ][ 0 ];
  1868. }
  1869. else if ( navObjectWorldPos[ 1 ] > boundingBox[ 1 ][ 1 ] ) {
  1870. navObjectWorldPos[ 1 ] = boundingBox[ 1 ][ 1 ];
  1871. }
  1872. if ( navObjectWorldPos[ 2 ] < boundingBox[ 2 ][ 0 ] ) {
  1873. navObjectWorldPos[ 2 ] = boundingBox[ 2 ][ 0 ];
  1874. }
  1875. else if ( navObjectWorldPos[ 2 ] > boundingBox[ 2 ][ 1 ] ) {
  1876. navObjectWorldPos[ 2 ] = boundingBox[ 2 ][ 1 ];
  1877. }
  1878. }
  1879. // We are about to use the translationMatrix, so let's update it
  1880. extractRotationAndTranslation( navObject.threeObject );
  1881. // Insert the new navObject position into the translation array
  1882. var translationArray = translationMatrix.elements;
  1883. translationArray[ 12 ] = navObjectWorldPos [ 0 ];
  1884. translationArray[ 13 ] = navObjectWorldPos [ 1 ];
  1885. translationArray[ 14 ] = navObjectWorldPos [ 2 ];
  1886. // Since this translation already accounts for pitch and yaw, insert it directly into the navObject
  1887. // transform
  1888. navObjectWorldTransformMatrixArray[ 12 ] = navObjectWorldPos [ 0 ];
  1889. navObjectWorldTransformMatrixArray[ 13 ] = navObjectWorldPos [ 1 ];
  1890. navObjectWorldTransformMatrixArray[ 14 ] = navObjectWorldPos [ 2 ];
  1891. }
  1892. }
  1893. this.rotateNavObjectByKey = function( direction, navObj, navMode, rotationSpeed, translationSpeed, msSinceLastFrame ) {
  1894. var navThreeObject = navObj.threeObject;
  1895. // Compute the distance rotated in the elapsed time
  1896. // Constrain the time to be less than 0.5 seconds, so that if a user has a very low frame rate,
  1897. // one key press doesn't send them off in space
  1898. var theta = direction * ( rotationSpeed * degreesToRadians ) *
  1899. Math.min( msSinceLastFrame * 0.001, 0.5 );
  1900. var orbiting = ( navMode == "fly" ) && mouseDown.middle && positionUnderMouseClick;
  1901. if ( orbiting ) {
  1902. var pitchRadians = 0;
  1903. var yawRadians = -theta;
  1904. orbit( pitchRadians, yawRadians );
  1905. } else {
  1906. // We will soon want to use the yawMatrix and pitchMatrix,
  1907. // so let's update them
  1908. extractRotationAndTranslation( navObject.threeObject );
  1909. var cos = Math.cos( theta );
  1910. var sin = Math.sin( theta );
  1911. var rotation = [ cos, sin, 0, 0,
  1912. -sin, cos, 0, 0,
  1913. 0, 0, 1, 0,
  1914. 0, 0, 0, 1 ];
  1915. // Left multiply the current transform matrix by the rotation transform
  1916. // and assign the result back to the navObject's transform
  1917. var yawArray = yawMatrix.elements;
  1918. // Perform the rotation
  1919. goog.vec.Mat4.multMat( rotation, yawArray, yawArray );
  1920. // Construct the new transform from pitch, yaw, and translation
  1921. var navObjectWorldTransform = navThreeObject.matrixWorld;
  1922. navObjectWorldTransform.multiplyMatrices( yawMatrix, pitchMatrix );
  1923. navObjectWorldTransform.multiplyMatrices( translationMatrix, navObjectWorldTransform );
  1924. if ( navThreeObject instanceof THREE.Camera ) {
  1925. var navObjectWorldTransformArray = navObjectWorldTransform.elements;
  1926. navObjectWorldTransform.elements = convertCameraTransformFromVWFtoThreejs( navObjectWorldTransformArray );
  1927. }
  1928. }
  1929. }
  1930. var handleKeyNavigation = function( keyCode, keyIsDown ) {
  1931. var key = getKeyValue( keyCode ).key;
  1932. key = key && key.toLowerCase();
  1933. switch ( self.navigationKeyMapping[ key ] ) {
  1934. case "forward":
  1935. movingForward = keyIsDown;
  1936. break;
  1937. case "back":
  1938. movingBack = keyIsDown;
  1939. break;
  1940. case "left":
  1941. movingLeft = keyIsDown;
  1942. break;
  1943. case "right":
  1944. movingRight = keyIsDown;
  1945. break;
  1946. case "rotateLeft":
  1947. rotatingLeft = keyIsDown;
  1948. break;
  1949. case "rotateRight":
  1950. rotatingRight = keyIsDown;
  1951. break;
  1952. }
  1953. }
  1954. // END TODO
  1955. // == Draggable Content ========================================================================
  1956. // canvas.addEventListener( "dragenter", function( e ) {
  1957. // e.stopPropagation();
  1958. // e.preventDefault();
  1959. // }, false );
  1960. // canvas.addEventListener( "dragexit", function( e ) {
  1961. // e.stopPropagation();
  1962. // e.preventDefault();
  1963. // }, false );
  1964. // -- dragOver ---------------------------------------------------------------------------------
  1965. canvas.ondragover = function( e ) {
  1966. self.mouseOverCanvas = true;
  1967. sceneCanvas.mouseX=e.clientX;
  1968. sceneCanvas.mouseY=e.clientY;
  1969. var eData = getEventData( e, false );
  1970. if ( eData ) {
  1971. e.dataTransfer.dropEffect = "copy";
  1972. }
  1973. e.preventDefault();
  1974. };
  1975. // -- drop ---------------------------------------------------------------------------------
  1976. canvas.ondrop = function( e ) {
  1977. e.preventDefault();
  1978. var eData = getEventData( e, false );
  1979. if ( eData ) {
  1980. var fileData, fileName, fileUrl, rotation, scale, translation, match, object;
  1981. try {
  1982. fileData = JSON.parse( e.dataTransfer.getData('text/plain') );
  1983. fileName = decodeURIComponent(fileData.fileName);
  1984. fileUrl = decodeURIComponent(fileData.fileUrl);
  1985. rotation = decodeURIComponent(fileData.rotation);
  1986. rotation = rotation ? JSON.parse(rotation) : undefined;
  1987. scale = decodeURIComponent(fileData.scale);
  1988. scale = scale ? JSON.parse(scale) : [1, 1, 1];
  1989. translation = decodeURIComponent(fileData.translation);
  1990. translation = translation ? JSON.parse(translation) : [0, 0, 0];
  1991. if($.isArray(translation) && translation.length == 3) {
  1992. translation[0] += eData.eventNodeData[""][0].globalPosition[0];
  1993. translation[1] += eData.eventNodeData[""][0].globalPosition[1];
  1994. translation[2] += eData.eventNodeData[""][0].globalPosition[2];
  1995. }
  1996. else {
  1997. translation = eData.eventNodeData[""][0].globalPosition;
  1998. }
  1999. if ( match = /* assignment! */ fileUrl.match( /(.*\.vwf)\.(json|yaml)$/i ) ) {
  2000. object = {
  2001. extends: match[1],
  2002. properties: {
  2003. translation: translation,
  2004. rotation : rotation,
  2005. scale: scale,
  2006. },
  2007. };
  2008. fileName = fileName.replace( /\.(json|yaml)$/i, "" );
  2009. } else if ( match = /* assignment! */ fileUrl.match( /\.dae$/i ) ) {
  2010. object = {
  2011. extends: "http://vwf.example.com/node3.vwf",
  2012. source: fileUrl,
  2013. type: "model/vnd.collada+xml",
  2014. properties: {
  2015. translation: translation,
  2016. rotation : rotation,
  2017. scale: scale,
  2018. },
  2019. };
  2020. }
  2021. if ( object ) {
  2022. sceneView.kernel.createChild( sceneView.kernel.application(), fileName, object );
  2023. }
  2024. } catch ( e ) {
  2025. // TODO: invalid JSON
  2026. }
  2027. }
  2028. };
  2029. };
  2030. // TODO: is this function needed?
  2031. // seems to be an exact copy of the ThreeJSPick
  2032. // should be tested and removed if this is not needed
  2033. function ThreeJSTouchPick ( canvas, sceneNode, mousepos )
  2034. {
  2035. if(!this.lastEventData) return;
  2036. var threeCam = this.state.cameraInUse;
  2037. if ( !threeCam ) {
  2038. this.logger.errorx( "Cannot perform pick because there is no camera to pick from" );
  2039. return;
  2040. }
  2041. var intersects = undefined;
  2042. var mousepos = {
  2043. "x": this.lastEventData.eventData[0].position[0],
  2044. "y": this.lastEventData.eventData[0].position[1]
  2045. }; // window coordinates
  2046. var x = ( mousepos.x ) * 2 - 1;
  2047. var y = -( mousepos.y ) * 2 + 1;
  2048. pickDirection.set( x, y, 0.5 );
  2049. var camPos = new THREE.Vector3(
  2050. threeCam.matrixWorld.elements[ 12 ],
  2051. threeCam.matrixWorld.elements[ 13 ],
  2052. threeCam.matrixWorld.elements[ 14 ]
  2053. );
  2054. pickDirection.unproject( threeCam );
  2055. raycaster.ray.set( camPos, pickDirection.sub( camPos ).normalize() );
  2056. intersects = raycaster.intersectObjects( sceneNode.threeScene.children, true );
  2057. // Cycle through the list of intersected objects and return the first visible one
  2058. for ( var i = 0; i < intersects.length; i++ ) {
  2059. if ( intersects[ i ].object.visible ) {
  2060. if ( getPickObjectID( intersects[ i ].object ) !== null ) {
  2061. return intersects[ i ];
  2062. }
  2063. }
  2064. }
  2065. return null;
  2066. }
  2067. function ThreeJSPick( canvas, sceneNode, debug )
  2068. {
  2069. if(!this.lastEventData) return;
  2070. var threeCam = this.state.cameraInUse;
  2071. if ( !threeCam ) {
  2072. this.logger.errorx( "Cannot perform pick because there is no camera to pick from" );
  2073. return;
  2074. }
  2075. var intersects = undefined;
  2076. var target = undefined;
  2077. var mousepos = {
  2078. "x": this.lastEventData.eventData[0].position[0],
  2079. "y": this.lastEventData.eventData[0].position[1]
  2080. }; // window coordinates
  2081. var x = ( mousepos.x ) * 2 - 1;
  2082. var y = -( mousepos.y ) * 2 + 1;
  2083. pickDirection.set( x, y, 0.5 );
  2084. var camPos = new THREE.Vector3(
  2085. threeCam.matrixWorld.elements[ 12 ],
  2086. threeCam.matrixWorld.elements[ 13 ],
  2087. threeCam.matrixWorld.elements[ 14 ]
  2088. );
  2089. pickDirection.unproject( threeCam );
  2090. raycaster.ray.set( camPos, pickDirection.sub( camPos ).normalize() );
  2091. intersects = raycaster.intersectObjects( sceneNode.threeScene.children, true );
  2092. // Cycle through the list of intersected objects and return the first visible one
  2093. for ( var i = 0; i < intersects.length && target === undefined; i++ ) {
  2094. if ( debug ) {
  2095. for ( var j = 0; j < intersects.length; j++ ) {
  2096. console.info( j + ". " + getPickObjectID( intersects[ j ].object ) );
  2097. }
  2098. }
  2099. if ( intersects[ i ].object.visible ) {
  2100. if ( getPickObjectID( intersects[ i ].object ) !== null ) {
  2101. target = intersects[ i ];
  2102. }
  2103. }
  2104. }
  2105. return target;
  2106. }
  2107. function getPickObjectID(threeObject)
  2108. {
  2109. if(threeObject.vwfID)
  2110. return threeObject.vwfID;
  2111. else if(threeObject.parent)
  2112. return getPickObjectID(threeObject.parent);
  2113. return null;
  2114. }
  2115. function vec3ToArray( vec ) {
  2116. return [ vec.x, vec.y, vec.z ];
  2117. }
  2118. function indentStr() {
  2119. return " ";
  2120. }
  2121. function indent(iIndent) {
  2122. var sOut = "";
  2123. for ( var j = 0; j < iIndent; j++ ) {
  2124. sOut = sOut + indentStr.call( this );
  2125. }
  2126. return sOut;
  2127. }
  2128. function indent2(iIndent) {
  2129. var sOut = "";
  2130. var idt = indentStr.call( this )
  2131. for ( var j = 0; j < iIndent; j++ ) {
  2132. sOut = sOut + idt + idt;
  2133. }
  2134. return sOut;
  2135. }
  2136. function getObjectType( object3 ) {
  2137. var type = "object3D";
  2138. if ( object3 instanceof THREE.Camera ) {
  2139. type = "camera"
  2140. } else if ( object3 instanceof THREE.Light ) {
  2141. type = "light"
  2142. } else if ( object3 instanceof THREE.Mesh ) {
  2143. type = "mesh"
  2144. } else if ( object3 instanceof THREE.Scene ) {
  2145. type = "scene";
  2146. }
  2147. return type;
  2148. }
  2149. function getExtendType( object3 ) {
  2150. var exts = "extends: http://vwf.example.com/node3.vwf";
  2151. if ( object3 instanceof THREE.Camera ) {
  2152. exts = "extends: http://vwf.example.com/camera.vwf"
  2153. } else if ( object3 instanceof THREE.Light ) {
  2154. exts = "extends: http://vwf.example.com/light.vwf"
  2155. }
  2156. return exts;
  2157. }
  2158. function consoleOut( msg ) {
  2159. console.info( msg );
  2160. //this.logger.info( msg );
  2161. }
  2162. function getBindableCount( object3 ) {
  2163. var count = 0, tp ;
  2164. if ( object3 instanceof THREE.Mesh ){
  2165. count++;
  2166. }
  2167. for ( var i = 0; i < object3.children.length; i++ ) {
  2168. tp = getObjectType.call( this, object3.children[i] );
  2169. if ( object3.children[i].name != "" ) {
  2170. count++;
  2171. }
  2172. }
  2173. //consoleOut.call( this, count + " = getBindableCount( "+object3.name+" )");
  2174. return count;
  2175. }
  2176. function recurseJsonObject3D( object3, parentName, depth ) {
  2177. var tp = getObjectType.call( this, object3 );
  2178. if ( object3 && object3.name != "" ) {
  2179. var sOut = indent.call( this, depth );
  2180. var sIndent = indent.call( this, depth+1 );
  2181. var bindCount = ( object3.children !== undefined ) ? getBindableCount.call( this, object3 ) : 0;
  2182. consoleOut.call( this, sOut + object3.name + ": {");
  2183. consoleOut.call( this, sIndent + getExtendType.call( this, object3 ) );
  2184. if ( bindCount > 0 ) {
  2185. var recursedCount = 0;
  2186. consoleOut.call( this, sIndent + "children: {" );
  2187. for ( var i = 0; i < object3.children.length; i++ ) {
  2188. depth++;
  2189. recurseJsonObject3D.call( this, object3.children[i], object3.name, depth + 1 );
  2190. depth--;
  2191. recursedCount++;
  2192. }
  2193. if ( tp == "mesh" ) {
  2194. outputJsonMaterial.call( this, depth+2, 0 );
  2195. }
  2196. consoleOut.call( this, sIndent + "}," );
  2197. }
  2198. consoleOut.call( this, sOut + "}," );
  2199. }
  2200. }
  2201. function outputJsonMaterial( iIndent, index ) {
  2202. var sOut = indent.call( this, iIndent + 1 );
  2203. consoleOut.call( this, indent.call( this, iIndent) + "material" + ( index > 0 ? index : "" ) + ": {" );
  2204. consoleOut.call( this, sOut + "extends: http://vwf.example.com/material.vwf" );
  2205. consoleOut.call( this, indent.call( this, iIndent) + "}," );
  2206. }
  2207. function outputObject3D( object3, parentName, iIndent ) {
  2208. var sOut = indent.call( this, iIndent + 1);
  2209. var tp = getObjectType.call( this, object3 );
  2210. var bindCount = ( object3.children !== undefined ) ? getBindableCount.call( this, object3 ) : 0;
  2211. if ( object3.name != "" ) {
  2212. consoleOut.call( this, indent.call( this, iIndent ) + object3.name + ":");
  2213. consoleOut.call( this, sOut + getExtendType.call( this, object3 ) );
  2214. if ( bindCount > 0 ) {
  2215. consoleOut.call( this, sOut + "children: " );
  2216. if ( tp == "mesh" ) {
  2217. // need to check the multimaterial list here
  2218. outputMaterial.call( this, iIndent + 2, 0 );
  2219. }
  2220. }
  2221. }
  2222. }
  2223. function recurseObject3D( object3, parentName, depth ) {
  2224. var tp = getObjectType.call( this, object3 );
  2225. if ( object3 ) {
  2226. var sOut = indent.call( this, depth );
  2227. outputObject3D.call( this, object3, parentName, depth );
  2228. if ( getBindableCount.call( this, object3 ) > 0 ) {
  2229. for ( var i = 0; i < object3.children.length; i++ ) {
  2230. depth++;
  2231. recurseObject3D.call( this, object3.children[i], object3.name, depth + 1 );
  2232. depth--;
  2233. }
  2234. }
  2235. }
  2236. }
  2237. function getWorldTransform( node ) {
  2238. var parent = self.state.nodes[ node.parentID ];
  2239. if ( parent ) {
  2240. var worldTransform = new THREE.Matrix4();
  2241. if ( node.transform === undefined ) {
  2242. node.transform = new THREE.Matrix4();
  2243. }
  2244. return worldTransform.multiplyMatrices( getWorldTransform( parent ), node.transform );
  2245. } else {
  2246. return node.transform;
  2247. }
  2248. }
  2249. function setWorldTransform( node, worldTransform ) {
  2250. if ( node.parent ) {
  2251. var parentInverse = goog.vec.Mat4.create();
  2252. if ( goog.vec.Mat4.invert( getWorldTransform( node.parent ), parentInverse ) ) {
  2253. node.transform = goog.vec.Mat4.multMat( parentInverse, worldTransform,
  2254. goog.vec.Mat4.create() );
  2255. } else {
  2256. self.logger.errorx( "Parent world transform is not invertible - did not set world transform " +
  2257. "on node '" + node.id + "'" );
  2258. }
  2259. } else {
  2260. node.transform = worldTransform;
  2261. }
  2262. }
  2263. function outputMaterial( iIndent, index ) {
  2264. var sOut = indent.call( this, iIndent + 1 );
  2265. consoleOut.call( this, indent.call( this, iIndent) + "material" + ( index > 0 ? index : "" ) + ":" );
  2266. consoleOut.call( this, sOut + "extends: http://vwf.example.com/material.vwf" );
  2267. }
  2268. function consoleObject( object3, depth ) {
  2269. consoleOut.call( this, indent2.call( this, depth ) + object3.name + " -> " + " type = " + getObjectType.call( this, object3 ) );
  2270. }
  2271. function consoleScene( parent, depth ) {
  2272. consoleObject.call( this, parent, depth );
  2273. for ( var i = 0; i < parent.children.length; i++ ) {
  2274. consoleScene.call( this, parent.children[i], depth+1 );
  2275. }
  2276. }
  2277. function getKeyValue( keyCode ) {
  2278. var key = { key: undefined, code: keyCode, char: undefined };
  2279. switch ( keyCode ) {
  2280. case 8:
  2281. key.key = "backspace";
  2282. break;
  2283. case 9:
  2284. key.key = "tab";
  2285. break;
  2286. case 13:
  2287. key.key = "enter";
  2288. break;
  2289. case 16:
  2290. key.key = "shift";
  2291. break;
  2292. case 17:
  2293. key.key = "ctrl";
  2294. break;
  2295. case 18:
  2296. key = "alt";
  2297. break;
  2298. case 19:
  2299. key.key = "pausebreak";
  2300. break;
  2301. case 20:
  2302. key.key = "capslock";
  2303. break;
  2304. case 27:
  2305. key.key = "escape";
  2306. break;
  2307. case 33:
  2308. key.key = "pageup";
  2309. break;
  2310. case 34:
  2311. key.key = "pagedown";
  2312. break;
  2313. case 35:
  2314. key.key = "end";
  2315. break;
  2316. case 36:
  2317. key.key = "home";
  2318. break;
  2319. case 37:
  2320. key.key = "leftarrow";
  2321. break;
  2322. case 38:
  2323. key.key = "uparrow";
  2324. break;
  2325. case 39:
  2326. key.key = "rightarrow";
  2327. break;
  2328. case 40:
  2329. key.key = "downarrow";
  2330. break;
  2331. case 45:
  2332. key.key = "insert";
  2333. break;
  2334. case 46:
  2335. key.key = "delete";
  2336. break;
  2337. case 48:
  2338. key.key = "0";
  2339. key.char = "0";
  2340. break;
  2341. case 49:
  2342. key.key = "1";
  2343. key.char = "1";
  2344. break;
  2345. case 50:
  2346. key.key = "2";
  2347. key.char = "2";
  2348. break;
  2349. case 51:
  2350. key.key = "3";
  2351. key.char = "3";
  2352. break;
  2353. case 52:
  2354. key.key = "4";
  2355. key.char = "4";
  2356. break;
  2357. case 53:
  2358. key.key = "5";
  2359. key.char = "5";
  2360. break;
  2361. case 54:
  2362. key.key = "6";
  2363. key.char = "6";
  2364. break;
  2365. case 55:
  2366. key.key = "7";
  2367. key.char = "7";
  2368. break;
  2369. case 56:
  2370. key.key = "8";
  2371. key.char = "8";
  2372. break;
  2373. case 57:
  2374. key.key = "9";
  2375. key.char = "9";
  2376. break;
  2377. case 65:
  2378. key.key = "A";
  2379. key.char = "A";
  2380. break;
  2381. case 66:
  2382. key.key = "B";
  2383. key.char = "B";
  2384. break;
  2385. case 67:
  2386. key.key = "C";
  2387. key.char = "C";
  2388. break;
  2389. case 68:
  2390. key.key = "D";
  2391. key.char = "D";
  2392. break;
  2393. case 69:
  2394. key.key = "E";
  2395. key.char = "E";
  2396. break;
  2397. case 70:
  2398. key.key = "F";
  2399. key.char = "F";
  2400. break;
  2401. case 71:
  2402. key.key = "G";
  2403. key.char = "G";
  2404. break;
  2405. case 72:
  2406. key.key = "H";
  2407. key.char = "H";
  2408. break;
  2409. case 73:
  2410. key.key = "I";
  2411. key.char = "I";
  2412. break;
  2413. case 74:
  2414. key.key = "J";
  2415. key.char = "J";
  2416. break;
  2417. case 75:
  2418. key.key = "K";
  2419. key.char = "K";
  2420. break;
  2421. case 76:
  2422. key.key = "L";
  2423. key.char = "L";
  2424. break;
  2425. case 77:
  2426. key.key = "M";
  2427. key.char = "M";
  2428. break;
  2429. case 78:
  2430. key.key = "N";
  2431. key.char = "N";
  2432. break;
  2433. case 79:
  2434. key.key = "O";
  2435. key.char = "O";
  2436. break;
  2437. case 80:
  2438. key.key = "P";
  2439. key.char = "P";
  2440. break;
  2441. case 81:
  2442. key.key = "Q";
  2443. key.char = "Q";
  2444. break;
  2445. case 82:
  2446. key.key = "R";
  2447. key.char = "R";
  2448. break;
  2449. case 83:
  2450. key.key = "S";
  2451. key.char = "S";
  2452. break;
  2453. case 84:
  2454. key.key = "T";
  2455. key.char = "T";
  2456. break;
  2457. case 85:
  2458. key.key = "U";
  2459. key.char = "U";
  2460. break;
  2461. case 86:
  2462. key.key = "V";
  2463. key.char = "V";
  2464. break;
  2465. case 87:
  2466. key.key = "W";
  2467. key.char = "W";
  2468. break;
  2469. case 88:
  2470. key.key = "X";
  2471. key.char = "X";
  2472. break;
  2473. case 89:
  2474. key.key = "Y";
  2475. key.char = "Y";
  2476. break;
  2477. case 90:
  2478. key.key = "Z";
  2479. key.char = "Z";
  2480. break;
  2481. case 91:
  2482. key.key = "leftwindow";
  2483. break;
  2484. case 92:
  2485. key.key = "rightwindow";
  2486. break;
  2487. case 93:
  2488. key.key = "select";
  2489. break;
  2490. case 96:
  2491. key.key = "numpad0";
  2492. key.char = "0";
  2493. break;
  2494. case 97:
  2495. key.key = "numpad1";
  2496. key.char = "1";
  2497. break;
  2498. case 98:
  2499. key.key = "numpad2";
  2500. key.char = "2";
  2501. break;
  2502. case 99:
  2503. key.key = "numpad3";
  2504. key.char = "3";
  2505. break;
  2506. case 100:
  2507. key.key = "numpad4";
  2508. key.char = "4";
  2509. break;
  2510. case 101:
  2511. key.key = "numpad5";
  2512. key.char = "5";
  2513. break;
  2514. case 102:
  2515. key.key = "numpad6";
  2516. key.char = "6";
  2517. break;
  2518. case 103:
  2519. key.key = "numpad7";
  2520. key.char = "7";
  2521. break;
  2522. case 104:
  2523. key.key = "numpad8";
  2524. key.char = "8";
  2525. break;
  2526. case 105:
  2527. key.key = "numpad9";
  2528. key.char = "9";
  2529. break;
  2530. case 106:
  2531. key.key = "multiply";
  2532. key.char = "*";
  2533. break;
  2534. case 107:
  2535. key.key = "add";
  2536. key.char = "+";
  2537. break;
  2538. case 109:
  2539. key.key = "subtract";
  2540. key.char = "-";
  2541. break;
  2542. case 110:
  2543. key.key = "decimalpoint";
  2544. key.char = ".";
  2545. break;
  2546. case 111:
  2547. key.key = "divide";
  2548. key.char = "/";
  2549. break;
  2550. case 112:
  2551. key.key = "f1";
  2552. break;
  2553. case 113:
  2554. key.key = "f2";
  2555. break;
  2556. case 114:
  2557. key.key = "f3";
  2558. break;
  2559. case 115:
  2560. key.key = "f4";
  2561. break;
  2562. case 116:
  2563. key.key = "f5";
  2564. break;
  2565. case 117:
  2566. key.key = "f6";
  2567. break;
  2568. case 118:
  2569. key.key = "f7";
  2570. break;
  2571. case 119:
  2572. key.key = "f8";
  2573. break;
  2574. case 120:
  2575. key.key = "f9";
  2576. break;
  2577. case 121:
  2578. key.key = "f10";
  2579. break;
  2580. case 122:
  2581. key.key = "f11";
  2582. break;
  2583. case 123:
  2584. key.key = "f12";
  2585. break;
  2586. case 144:
  2587. key.key = "numlock";
  2588. break;
  2589. case 145:
  2590. key.key = "scrolllock";
  2591. break;
  2592. case 186:
  2593. key.key = "semicolon";
  2594. key.char = ";";
  2595. break;
  2596. case 187:
  2597. key.key = "equal";
  2598. key.char = "=";
  2599. break;
  2600. case 188:
  2601. key.key = "comma";
  2602. key.char = ",";
  2603. break;
  2604. case 189:
  2605. key.key = "dash";
  2606. key.char = "-";
  2607. break;
  2608. case 190:
  2609. key.key = "period";
  2610. key.char = ".";
  2611. break;
  2612. case 191:
  2613. key.key = "forwardslash";
  2614. key.char = "/";
  2615. break;
  2616. case 192:
  2617. key.key = "graveaccent";
  2618. break;
  2619. case 219:
  2620. key.key = "openbracket";
  2621. key.char = "{";
  2622. break;
  2623. case 220:
  2624. key.key = "backslash";
  2625. key.char = "\\";
  2626. break;
  2627. case 221:
  2628. key.key = "closebracket";
  2629. key.char = "}";
  2630. break;
  2631. case 222:
  2632. key.key = "singlequote";
  2633. key.char = "'";
  2634. break;
  2635. case 32:
  2636. key.key = "space";
  2637. key.char = " ";
  2638. break;
  2639. }
  2640. return key;
  2641. }
  2642. function controlNavObject( node ) {
  2643. if ( !node ) {
  2644. self.logger.error( "Attempted to control non-existent navigation object" );
  2645. return;
  2646. }
  2647. // If there is already a navObject, make that object opaque if we had made it transparent
  2648. if ( navObject && !makeOwnAvatarVisible ) {
  2649. setVisibleRecursively( navObject.threeObject, true );
  2650. }
  2651. // Set the new navigation object
  2652. navObject = node;
  2653. // Set the 3D model transparent if requested
  2654. if ( !makeOwnAvatarVisible ) {
  2655. setVisibleRecursively( navObject.threeObject, false );
  2656. }
  2657. // TODO: The model should keep track of a shared navObject, not just the shared camera that it tracks now. See Redmine #3145.
  2658. if( !usersShareView ) {
  2659. // Search for a camera in the navigation object and if it exists, make it active
  2660. var cameraIds = self.kernel.find( navObject.ID,
  2661. "descendant-or-self::element(*,'http://vwf.example.com/camera.vwf')" );
  2662. if ( cameraIds.length ) {
  2663. // Set the view's active camera
  2664. var rendererState = self.state;
  2665. var cameraId = cameraIds[ 0 ];
  2666. cameraNode = rendererState.nodes[ cameraId ];
  2667. rendererState.cameraInUse = cameraNode.threeObject;
  2668. }
  2669. }
  2670. // Request properties from the navigation object
  2671. vwf_view.kernel.getProperty( navObject.ID, "navmode" );
  2672. vwf_view.kernel.getProperty( navObject.ID, "touchmode" );
  2673. vwf_view.kernel.getProperty( navObject.ID, "translationSpeed" );
  2674. vwf_view.kernel.getProperty( navObject.ID, "rotationSpeed" );
  2675. }
  2676. function findNavObject() {
  2677. // Find the navigable objects in the scene
  2678. var sceneRootID = self.kernel.application();
  2679. var navObjectIds = self.kernel.find( sceneRootID,
  2680. ".//element(*,'http://vwf.example.com/navigable.vwf')" );
  2681. numNavCandidates = navObjectIds.length;
  2682. // If there are navigation objects in the scene, get their owner property values (The rest of the logic
  2683. // of choosing the correct navigation object is in the gotProperty call for owner)
  2684. // Else, retrieve the userObject property so we may create a navigation object from it for this user
  2685. if ( numNavCandidates ) {
  2686. for ( var i = 0; i < numNavCandidates; i++ ) {
  2687. vwf_view.kernel.getProperty( navObjectIds[ i ], "owner" );
  2688. }
  2689. } else {
  2690. vwf_view.kernel.getProperty( sceneRootID, "makeOwnAvatarVisible" );
  2691. vwf_view.kernel.getProperty( sceneRootID, "boundingBox" );
  2692. vwf_view.kernel.getProperty( sceneRootID, "userObject" );
  2693. userObjectRequested = true;
  2694. }
  2695. }
  2696. function inputHandleMouseNavigation( mouseEventData ) {
  2697. var deltaX = 0;
  2698. var deltaY = 0;
  2699. if ( pointerLocked ) {
  2700. deltaX = mouseEventData.movementX / self.width;
  2701. deltaY = mouseEventData.movementY / self.height;
  2702. } else if ( startMousePosition ) {
  2703. var currentMousePosition = mouseEventData[ 0 ].position;
  2704. deltaX = currentMousePosition[ 0 ] - startMousePosition [ 0 ];
  2705. deltaY = currentMousePosition[ 1 ] - startMousePosition [ 1 ];
  2706. }
  2707. if ( deltaX || deltaY ) {
  2708. if ( navObject ) {
  2709. var navThreeObject = navObject.threeObject;
  2710. var originalTransform = goog.vec.Mat4.clone( navThreeObject.matrix.elements );
  2711. self.handleMouseNavigation( deltaX, deltaY, navObject, navmode,
  2712. rotationSpeed, translationSpeed, mouseDown, mouseEventData );
  2713. setTransformFromWorldTransform( navThreeObject );
  2714. callModelTransformBy( navObject, originalTransform, navThreeObject.matrix.elements );
  2715. } else {
  2716. self.logger.warnx( "handleMouseNavigation: There is no navigation object to move" );
  2717. }
  2718. startMousePosition = currentMousePosition;
  2719. }
  2720. }
  2721. function inputHandleScroll( wheelDelta, distanceToTarget ) {
  2722. if ( navObject && navObject.threeObject ) {
  2723. var navThreeObject = navObject.threeObject;
  2724. var originalTransform = goog.vec.Mat4.clone( navThreeObject.matrix.elements );
  2725. self.handleScroll( wheelDelta, navObject, navmode, rotationSpeed, translationSpeed, distanceToTarget );
  2726. setTransformFromWorldTransform( navThreeObject );
  2727. callModelTransformBy( navObject, originalTransform, navThreeObject.matrix.elements );
  2728. }
  2729. }
  2730. function inputMoveNavObject( msSinceLastFrame ) {
  2731. var x = 0;
  2732. var y = 0;
  2733. // Calculate the movement increments
  2734. if ( movingForward )
  2735. y += 1;
  2736. if ( movingBack )
  2737. y -= 1;
  2738. if ( movingLeft )
  2739. x -= 1;
  2740. if ( movingRight )
  2741. x += 1;
  2742. // If there is no movement since last frame, return
  2743. if ( ! ( x || y ) )
  2744. return;
  2745. if ( navObject ) {
  2746. var navThreeObject = navObject.threeObject;
  2747. var originalTransform = goog.vec.Mat4.clone( navThreeObject.matrix.elements );
  2748. self.moveNavObject( x, y, navObject, navmode, rotationSpeed, translationSpeed, msSinceLastFrame );
  2749. // Update the navigation object's local transform from its new world transform
  2750. setTransformFromWorldTransform( navThreeObject );
  2751. callModelTransformBy( navObject, originalTransform, navThreeObject.matrix.elements );
  2752. } else {
  2753. self.logger.warnx( "moveNavObject: There is no navigation object to move" );
  2754. }
  2755. }
  2756. function inputRotateNavObjectByKey( msSinceLastFrame ) {
  2757. var direction = 0;
  2758. // Calculate movement increment
  2759. if ( rotatingLeft )
  2760. direction += 1;
  2761. if ( rotatingRight )
  2762. direction -= 1;
  2763. // If there is no rotation this frame, return
  2764. if ( !direction )
  2765. return;
  2766. if ( navObject ) {
  2767. var navThreeObject = navObject.threeObject;
  2768. var originalTransform = goog.vec.Mat4.clone( navThreeObject.matrix.elements );
  2769. self.rotateNavObjectByKey( direction, navObject, navmode,
  2770. rotationSpeed, translationSpeed, msSinceLastFrame );
  2771. // Force the navObject's world transform to update from its local transform
  2772. setTransformFromWorldTransform( navThreeObject );
  2773. callModelTransformBy( navObject, originalTransform, navThreeObject.matrix.elements );
  2774. } else {
  2775. self.logger.warnx( "rotateNavObjectByKey: There is no navigation object to move" );
  2776. }
  2777. }
  2778. // Receive Model Transform Changes algorithm
  2779. // 1.0 If (own view changes) then IGNORE (only if no external changes have occurred since the user’s view
  2780. // requested this change – otherwise, will need to treat like 1.1 or 1.2)
  2781. // 1.1 Elseif (other external changes and no outstanding own view changes) then ADOPT
  2782. // 1.2 Else Interpolate to the model’s transform (conflict b/w own view and external sourced model changes)
  2783. function receiveModelTransformChanges( nodeID, transformMatrix ) {
  2784. var node = self.state.nodes[ nodeID ];
  2785. // If the node does not exist in the state's list of nodes, then this update is from a prototype and we
  2786. // should ignore it
  2787. if ( !node ) {
  2788. return;
  2789. }
  2790. // If the transform property was initially updated by this view....
  2791. if ( node.ignoreNextTransformUpdate ) {
  2792. node.outstandingTransformRequests.shift();
  2793. node.ignoreNextTransformUpdate = false;
  2794. } else { // this transform change request is not from me
  2795. adoptTransform( node, transformMatrix );
  2796. if ( node.outstandingTransformRequests ) {
  2797. var threeObject = node.threeObject;
  2798. for ( var i = 0; i < node.outstandingTransformRequests.length; i++ ) {
  2799. goog.vec.Mat4.multMat( node.outstandingTransformRequests[ i ],
  2800. threeObject.matrix.elements,
  2801. threeObject.matrix.elements );
  2802. }
  2803. updateRenderObjectTransform( threeObject );
  2804. }
  2805. }
  2806. }
  2807. function adoptTransform ( node, transform ) {
  2808. var transformMatrix = goog.vec.Mat4.clone( transform );
  2809. var threeObject = node.threeObject;
  2810. if ( threeObject instanceof THREE.Camera ) {
  2811. transformMatrix = convertCameraTransformFromVWFtoThreejs( transformMatrix );
  2812. } else if( threeObject instanceof THREE.PointCloud ) {
  2813. // I don't see where this function is defined. Maybe a copy-paste bug from
  2814. // GLGE driver? - Eric (5/13/13)
  2815. threeObject.updateTransform( transformMatrix );
  2816. }
  2817. threeObject.matrix.elements = transformMatrix;
  2818. updateRenderObjectTransform( threeObject );
  2819. nodeLookAt( node );
  2820. }
  2821. function callModelTransformBy( node, originalViewTransform, goalViewTransform ) {
  2822. var nodeID = node.ID;
  2823. if ( nodeID ) {
  2824. var inverseOriginalViewTransform = goog.vec.Mat4.createFloat32();
  2825. if ( goog.vec.Mat4.invert( originalViewTransform, inverseOriginalViewTransform ) ) {
  2826. var deltaViewTransform = goog.vec.Mat4.multMat( goalViewTransform, inverseOriginalViewTransform,
  2827. goog.vec.Mat4.createFloat32() );
  2828. var deltaModelTransform;
  2829. if ( node.threeObject instanceof THREE.Camera ) {
  2830. var originalModelTransform = convertCameraTransformFromThreejsToVWF( originalViewTransform );
  2831. var goalModelTransform = convertCameraTransformFromThreejsToVWF( goalViewTransform );
  2832. var inverseOriginalModelTransform = goog.vec.Mat4.createFloat32();
  2833. if ( goog.vec.Mat4.invert( originalModelTransform, inverseOriginalModelTransform ) ) {
  2834. deltaModelTransform = goog.vec.Mat4.multMat( goalModelTransform,
  2835. inverseOriginalModelTransform,
  2836. goog.vec.Mat4.createFloat32() );
  2837. } else {
  2838. self.logger.errorx( "callModelTransformBy: Original model transform is not invertible" );
  2839. }
  2840. } else {
  2841. deltaModelTransform = deltaViewTransform;
  2842. }
  2843. vwf_view.kernel.fireEvent( nodeID, "changingTransformFromView");
  2844. vwf_view.kernel.callMethod( nodeID, "transformBy", [ deltaModelTransform ] );
  2845. node.outstandingTransformRequests = node.outstandingTransformRequests || [];
  2846. node.outstandingTransformRequests.push( deltaViewTransform );
  2847. } else {
  2848. self.logger.errorx( "callModelTransformBy: Original view transform is not invertible" );
  2849. }
  2850. } else {
  2851. self.logger.errorx( "callModelTransformBy: Cannot set property on node that does not have a " +
  2852. "valid ID" );
  2853. }
  2854. }
  2855. function setTransformFromWorldTransform( threeObject ) {
  2856. if ( !threeObject ) {
  2857. self.logger.warnx( "setTransformFromWorldTransform: There is no threeObject to update" );
  2858. return;
  2859. }
  2860. var parent = threeObject.parent;
  2861. if ( parent ) {
  2862. var inverseParentWorldMatrix = new THREE.Matrix4();
  2863. inverseParentWorldMatrix.getInverse( parent.matrixWorld );
  2864. threeObject.matrix.multiplyMatrices( inverseParentWorldMatrix, threeObject.matrixWorld );
  2865. } else {
  2866. threeObject.matrix.elements = goog.vec.Mat4.clone( threeObject.matrixWorld.elements );
  2867. }
  2868. updateRenderObjectTransform( threeObject );
  2869. }
  2870. function updateRenderObjectTransform( threeObject ) {
  2871. // Tell three.js not to update the transform matrix from position and rotation values (which are older)
  2872. threeObject.matrixAutoUpdate = false;
  2873. // Update the object's world transform
  2874. threeObject.updateMatrixWorld( true );
  2875. }
  2876. // Function to make the object continuously look at a position or node
  2877. // (for use when setting 'transform' or 'lookAt')
  2878. // An almost identical function is copied in model/threejs.js, so if any modifications are made here, they
  2879. // should be made there, also
  2880. function nodeLookAt( node ) {
  2881. if ( !node ) {
  2882. self.logger.warnx( "nodeLookAt: Node does not exist" );
  2883. return;
  2884. }
  2885. // Function to make the object look at a particular position
  2886. // (For use in the following conditional)
  2887. var lookAtWorldPosition = function( targetWorldPos ) {
  2888. // Get the eye position
  2889. var eye = new THREE.Vector3();
  2890. var threeObject = node.threeObject;
  2891. eye.setFromMatrixPosition( threeObject.matrixWorld );
  2892. var look = new THREE.Vector3();
  2893. look.subVectors( targetWorldPos, eye );
  2894. if ( look.length() > 0 ) {
  2895. look.normalize();
  2896. // Set the up vector to be z
  2897. var roughlyUp = new THREE.Vector3();
  2898. roughlyUp.set( 0, 0, 1 );
  2899. var right = new THREE.Vector3();
  2900. right.crossVectors( look, roughlyUp );
  2901. if ( right.length() == 0 ) {
  2902. look.x += 0.0001;
  2903. right.crossVectors( look, roughlyUp );
  2904. }
  2905. right.normalize();
  2906. var up = new THREE.Vector3();
  2907. up.crossVectors( right, look );
  2908. var worldTransform = threeObject.matrixWorld.elements;
  2909. worldTransform[ 0 ] = right.x; worldTransform[ 4 ] = look.x; worldTransform[ 8 ] = up.x;
  2910. worldTransform[ 1 ] = right.y; worldTransform[ 5 ] = look.y; worldTransform[ 9 ] = up.y;
  2911. worldTransform[ 2 ] = right.z; worldTransform[ 6 ] = look.z; worldTransform[ 10 ] = up.z;
  2912. setTransformFromWorldTransform( threeObject );
  2913. if ( threeObject instanceof THREE.Camera ) {
  2914. var nodeTransformArray = threeObject.matrix.elements;
  2915. threeObject.matrix.elements = convertCameraTransformFromVWFtoThreejs( nodeTransformArray );
  2916. updateRenderObjectTransform( threeObject );
  2917. }
  2918. }
  2919. }
  2920. // The position for the object to look at - to be set in the following conditional
  2921. var targetWorldPos = new THREE.Vector3();
  2922. //Threejs does not currently support auto tracking the lookat,
  2923. //instead, we'll take the position of the node and look at that.
  2924. if ( utility.isString( node.lookatval ) ) {
  2925. var lookatNode = self.state.nodes[ node.lookatval ];
  2926. if ( lookatNode )
  2927. {
  2928. targetWorldPos.setFromMatrixPosition( lookatNode.threeObject.matrixWorld );
  2929. lookAtWorldPosition( targetWorldPos );
  2930. }
  2931. } else if ( node.lookatval instanceof Array ) {
  2932. targetWorldPos.set( node.lookatval[0], node.lookatval[1], node.lookatval[2] );
  2933. lookAtWorldPosition( targetWorldPos );
  2934. }
  2935. }
  2936. function convertCameraTransformFromVWFtoThreejs( transform ) {
  2937. // Rotate 90 degrees around X to convert from VWF Z-up to three.js Y-up.
  2938. var newTransform = goog.vec.Mat4.clone( transform );
  2939. // Get column y and z out of the matrix
  2940. var columny = goog.vec.Vec4.create();
  2941. goog.vec.Mat4.getColumn( newTransform, 1, columny );
  2942. var columnz = goog.vec.Vec4.create();
  2943. goog.vec.Mat4.getColumn( newTransform, 2, columnz );
  2944. // Swap the two columns, negating columny
  2945. goog.vec.Mat4.setColumn( newTransform, 1, columnz );
  2946. goog.vec.Mat4.setColumn( newTransform, 2, goog.vec.Vec4.negate( columny, columny ) );
  2947. return newTransform;
  2948. }
  2949. function convertCameraTransformFromThreejsToVWF( transform ) {
  2950. // Rotate -90 degrees around X to convert from three.js Y-up to VWF Z-up.
  2951. var newTransform = goog.vec.Mat4.clone( transform );
  2952. // Get column y and z out of the matrix
  2953. var columny = goog.vec.Vec4.create();
  2954. goog.vec.Mat4.getColumn( newTransform, 1, columny );
  2955. var columnz = goog.vec.Vec4.create();
  2956. goog.vec.Mat4.getColumn( newTransform, 2, columnz );
  2957. // Swap the two columns, negating columnz
  2958. goog.vec.Mat4.setColumn( newTransform, 1, goog.vec.Vec4.negate( columnz, columnz ) );
  2959. goog.vec.Mat4.setColumn( newTransform, 2, columny );
  2960. return newTransform;
  2961. }
  2962. // TODO: This should be replaced with self.state.setMeshPropertyRecursively
  2963. function setVisibleRecursively( threeObject, visible ) {
  2964. if ( !threeObject ) {
  2965. return;
  2966. }
  2967. threeObject.visible = visible;
  2968. for ( var i = 0; i < threeObject.children.length; i++ ) {
  2969. setVisibleRecursively( threeObject.children[ i ], visible );
  2970. }
  2971. }
  2972. function extractRotationAndTranslation( threeObject ) {
  2973. // Pull the pitch, yaw, and translation out of the transform
  2974. var worldTransformArray = threeObject.matrixWorld.elements;
  2975. var vwfWorldTransformArray;
  2976. // If this threeObject is a camera, it has a 90-degree rotation on it to account for the different
  2977. // coordinate systems of VWF and three.js. We need to undo that rotation before using it as a VWF
  2978. // property.
  2979. // Else, just use the transform as-is
  2980. if ( threeObject instanceof THREE.Camera ) {
  2981. vwfWorldTransformArray = convertCameraTransformFromThreejsToVWF( worldTransformArray );
  2982. } else {
  2983. vwfWorldTransformArray = goog.vec.Mat4.clone( worldTransformArray );
  2984. }
  2985. pitchMatrix = new THREE.Matrix4();
  2986. var pitchArray = pitchMatrix.elements;
  2987. var costheta = vwfWorldTransformArray[ 10 ];
  2988. var sintheta = vwfWorldTransformArray[ 6 ];
  2989. pitchArray[ 5 ] = costheta;
  2990. pitchArray[ 6 ] = sintheta;
  2991. pitchArray[ 9 ] = -sintheta;
  2992. pitchArray[ 10 ] = costheta;
  2993. yawMatrix = new THREE.Matrix4();
  2994. var yawArray = yawMatrix.elements;
  2995. var cosphi = vwfWorldTransformArray[ 0 ];
  2996. var sinphi = vwfWorldTransformArray[ 1 ];
  2997. yawArray[ 0 ] = cosphi;
  2998. yawArray[ 1 ] = sinphi;
  2999. yawArray[ 4 ] = -sinphi;
  3000. yawArray[ 5 ] = cosphi;
  3001. translationMatrix = new THREE.Matrix4();
  3002. var translationArray = translationMatrix.elements;
  3003. translationArray[ 12 ] = vwfWorldTransformArray[ 12 ];
  3004. translationArray[ 13 ] = vwfWorldTransformArray[ 13 ];
  3005. translationArray[ 14 ] = vwfWorldTransformArray[ 14 ];
  3006. }
  3007. function orbit( pitchRadians, yawRadians ) {
  3008. if ( navObject ) {
  3009. // We can only orbit around a point if there is a point to orbit around
  3010. if ( positionUnderMouseClick ) {
  3011. // We will soon want to use the yawMatrix and pitchMatrix,
  3012. // so let's update them
  3013. extractRotationAndTranslation( navObject.threeObject );
  3014. var navThreeObject = navObject.threeObject;
  3015. var originalTransform = goog.vec.Mat4.clone( navThreeObject.matrix.elements );
  3016. var originalPitchMatrix = pitchMatrix.clone();
  3017. // --------------------
  3018. // Calculate new pitch
  3019. // --------------------
  3020. var pitchQuat = new THREE.Quaternion();
  3021. pitchQuat.setFromAxisAngle( new THREE.Vector3( 1, 0, 0 ), pitchRadians );
  3022. var pitchDeltaMatrix = new THREE.Matrix4();
  3023. pitchDeltaMatrix.makeRotationFromQuaternion( pitchQuat );
  3024. pitchMatrix.multiplyMatrices( pitchDeltaMatrix, pitchMatrix );
  3025. // Constrain the camera's pitch to +/- 90 degrees
  3026. // We need to do something if zAxis.z is < 0
  3027. var pitchMatrixElements = pitchMatrix.elements;
  3028. var pitchIsConstrained = false;
  3029. var zenithOrNadirMult = 0;
  3030. if ( pitchMatrixElements[ 10 ] < 0 ) {
  3031. var xAxis = goog.vec.Vec3.create();
  3032. xAxis = goog.vec.Vec3.setFromArray( xAxis, [ pitchMatrixElements[ 0 ],
  3033. pitchMatrixElements[ 1 ],
  3034. pitchMatrixElements[ 2 ] ] );
  3035. var yAxis = goog.vec.Vec3.create();
  3036. // If forward vector is tipped up
  3037. if ( pitchMatrixElements[ 6 ] > 0 ) {
  3038. yAxis = goog.vec.Vec3.setFromArray( yAxis, [ 0, 0, 1 ] );
  3039. } else {
  3040. yAxis = goog.vec.Vec3.setFromArray( yAxis, [ 0, 0, -1 ] );
  3041. }
  3042. // Calculate the zAxis as a crossProduct of x and y
  3043. var zAxis = goog.vec.Vec3.cross( xAxis, yAxis, goog.vec.Vec3.create() );
  3044. // Put these values back in the camera matrix
  3045. pitchMatrixElements[ 4 ] = yAxis[ 0 ];
  3046. pitchMatrixElements[ 5 ] = yAxis[ 1 ];
  3047. pitchMatrixElements[ 6 ] = yAxis[ 2 ];
  3048. pitchMatrixElements[ 8 ] = zAxis[ 0 ];
  3049. pitchMatrixElements[ 9 ] = zAxis[ 1 ];
  3050. pitchMatrixElements[ 10 ] = zAxis[ 2 ];
  3051. pitchIsConstrained = true;
  3052. zenithOrNadirMult = -yAxis[ 2 ];
  3053. }
  3054. // ------------------
  3055. // Calculate new yaw
  3056. // ------------------
  3057. var yawQuat = new THREE.Quaternion();
  3058. yawQuat.setFromAxisAngle( new THREE.Vector3( 0, 0, 1 ), yawRadians );
  3059. var yawDeltaMatrix = new THREE.Matrix4();
  3060. yawDeltaMatrix.makeRotationFromQuaternion( yawQuat );
  3061. yawMatrix.multiplyMatrices( yawDeltaMatrix, yawMatrix );
  3062. // --------------------------
  3063. // Calculate new translation
  3064. // --------------------------
  3065. if ( pitchIsConstrained ) {
  3066. var inverseOriginalPitchMatrix = new THREE.Matrix4();
  3067. inverseOriginalPitchMatrix.getInverse( originalPitchMatrix );
  3068. pitchDeltaMatrix.multiplyMatrices( pitchMatrix, inverseOriginalPitchMatrix );
  3069. }
  3070. var rotatedOrbitFrameInWorld = new THREE.Matrix4();
  3071. //rotatedOrbitFrameInWorld.multiplyMatrices( yawMatrix, pitchMatrix );
  3072. rotatedOrbitFrameInWorld = yawMatrix.clone();
  3073. rotatedOrbitFrameInWorld.setPosition( new THREE.Vector3( positionUnderMouseClick[ 0 ],
  3074. positionUnderMouseClick[ 1 ],
  3075. positionUnderMouseClick[ 2 ] ) );
  3076. var worldToRotatedOrbit = new THREE.Matrix4();
  3077. worldToRotatedOrbit.getInverse( rotatedOrbitFrameInWorld );
  3078. var translationInRotatedOrbitFrame = new THREE.Matrix4();
  3079. translationInRotatedOrbitFrame.multiplyMatrices( worldToRotatedOrbit, translationMatrix );
  3080. // Apply pitch and then yaw
  3081. translationInRotatedOrbitFrame.multiplyMatrices( pitchDeltaMatrix, translationInRotatedOrbitFrame );
  3082. translationInRotatedOrbitFrame.multiplyMatrices( yawDeltaMatrix, translationInRotatedOrbitFrame );
  3083. // Transform back to world
  3084. var newTranslationInWorld = new THREE.Matrix4();
  3085. newTranslationInWorld.multiplyMatrices( rotatedOrbitFrameInWorld, translationInRotatedOrbitFrame );
  3086. var translationArray = translationMatrix.elements;
  3087. var newTranslationInWorldArray = newTranslationInWorld.elements;
  3088. translationArray[ 12 ] = newTranslationInWorldArray[ 12 ];
  3089. translationArray[ 13 ] = newTranslationInWorldArray[ 13 ];
  3090. translationArray[ 14 ] = newTranslationInWorldArray[ 14 ];
  3091. var boundByBoundingBox = false;
  3092. if ( boundingBox != undefined ) {
  3093. if ( translationArray[ 12 ] < boundingBox[ 0 ][ 0 ] ) {
  3094. boundByBoundingBox = true;
  3095. }
  3096. else if ( translationArray[ 12 ] > boundingBox[ 0 ][ 1 ] ) {
  3097. boundByBoundingBox = true;
  3098. }
  3099. if ( translationArray[ 13 ] < boundingBox[ 1 ][ 0 ] ) {
  3100. boundByBoundingBox = true;
  3101. }
  3102. else if ( translationArray[ 13 ] > boundingBox[ 1 ][ 1 ] ) {
  3103. boundByBoundingBox = true;
  3104. }
  3105. if ( translationArray[ 14 ] < boundingBox[ 2 ][ 0 ] ) {
  3106. boundByBoundingBox = true;
  3107. }
  3108. else if ( translationArray[ 14 ] > boundingBox[ 2 ][ 1 ] ) {
  3109. boundByBoundingBox = true;
  3110. }
  3111. }
  3112. // -------------------------------------------------
  3113. // Put all components together and set the new pose
  3114. // -------------------------------------------------
  3115. if ( boundByBoundingBox == false ) {
  3116. var navObjectWorldMatrix = navThreeObject.matrixWorld;
  3117. navObjectWorldMatrix.multiplyMatrices( yawMatrix, pitchMatrix );
  3118. navObjectWorldMatrix.multiplyMatrices( translationMatrix, navObjectWorldMatrix );
  3119. if ( navThreeObject instanceof THREE.Camera ) {
  3120. var navObjWrldTrnsfmArr = navObjectWorldMatrix.elements;
  3121. navObjectWorldMatrix.elements = convertCameraTransformFromVWFtoThreejs( navObjWrldTrnsfmArr );
  3122. }
  3123. }
  3124. setTransformFromWorldTransform( navThreeObject );
  3125. callModelTransformBy( navObject, originalTransform, navThreeObject.matrix.elements );
  3126. }
  3127. } else {
  3128. self.logger.warnx( "orbit: There is no navigation object to move" );
  3129. }
  3130. }
  3131. function setActiveCamera( cameraID ) {
  3132. if ( usersShareView && this.state.nodes[ cameraID ] !== undefined ) {
  3133. var sceneID = this.kernel.application();
  3134. var sceneNode = this.state.scenes[ sceneID ];
  3135. //var cameras = this.kernel.find( sceneID, "./element(*,'http://vwf.example.com/camera.vwf')" );
  3136. cameraNode = this.state.nodes[ cameraID ];
  3137. this.state.cameraInUse = cameraNode.threeObject;
  3138. var canvas = this.canvasQuery[ 0 ];
  3139. this.state.cameraInUse.aspect = canvas.clientWidth / canvas.clientHeight;
  3140. }
  3141. }
  3142. function createControls( camera, element ) {
  3143. var controls;
  3144. if ( !isMobile() ) {
  3145. controls = new THREE.OrbitControls( camera, element );
  3146. //controls.rotateUp(Math.PI / 4);
  3147. controls.target.set(
  3148. camera.position.x + 0.1,
  3149. camera.position.y,
  3150. camera.position.z
  3151. );
  3152. controls.noZoom = true;
  3153. controls.noPan = true;
  3154. controls.autoRotate = false;
  3155. } else {
  3156. controls = new THREE.DeviceOrientationControls( camera, true );
  3157. controls.connect();
  3158. controls.update();
  3159. element.addEventListener( 'click', fullscreen, false );
  3160. }
  3161. return controls;
  3162. }
  3163. function fullscreen() {
  3164. var container = document.getElementById( "container" );
  3165. if ( container ) {
  3166. if ( container.requestFullscreen ) {
  3167. container.requestFullscreen();
  3168. } else if ( container.msRequestFullscreen ) {
  3169. container.msRequestFullscreen();
  3170. } else if ( container.mozRequestFullScreen ) {
  3171. container.mozRequestFullScreen();
  3172. } else if ( container.webkitRequestFullscreen ) {
  3173. container.webkitRequestFullscreen();
  3174. }
  3175. }
  3176. }
  3177. function isMobileAndroid() {
  3178. return navigator.userAgent.match(/Android/i);
  3179. }
  3180. function isMobileBlackBerry() {
  3181. return navigator.userAgent.match(/BlackBerry/i);
  3182. }
  3183. function isMobileiOS() {
  3184. return navigator.userAgent.match(/iPhone|iPad|iPod/i);
  3185. }
  3186. function isMobileOpera() {
  3187. return navigator.userAgent.match(/Opera Mini/i);
  3188. }
  3189. function isMobileWindows() {
  3190. return navigator.userAgent.match(/IEMobile/i);
  3191. }
  3192. function isMobile(){
  3193. return (isMobileAndroid() || isMobileBlackBerry() || isMobileiOS() || isMobileOpera() || isMobileWindows());
  3194. }
  3195. });