pxlNav.cam/Camera.js

  1. ./ pxlNav Camera manager
  2. ./ Written by Kevin Edzenga 2020; 2024
  3. ./ Note : Since item implementation was done rather hastily,
  4. ./ Triggering full screen overlays occures in the Camera class currently.
  5. ./ This include LizardKing, StarField and InfiniteZoom
  6. ./ The Low-Gravity Jump is triggered through the `User` class
  7. ./
  8. ./ TODO : Move item functions from Camera and User to an `Item` class
  9. import {
  10. Vector2,
  11. Vector3,
  12. Quaternion,
  13. Object3D,
  14. Euler
  15. } from "...../libs/three/three.module.min.js";
  16. import { pxlUserSettings } from "...core/Options.js";
  17. import { VERBOSE_LEVEL, COLLIDER_TYPE, CAMERA_EVENT } from "...core/Enums.js";
  18. ./ TODO : Extend this damn monolith of a chunky boy
  19. ./ Camera, Player Controller, Force Influence / Collision
  20. .**
  21. * Camera or pxlCamera class
  22. * @alias pxlCamera
  23. * @class
  24. * @description Camera or pxlCamera class
  25. *.
  26. export class Camera{
  27. constructor(){
  28. ./ -- -- -- -- -- -- -- -- -- -- --
  29. ./ -- Default Camera Settings -- -- --
  30. ./ -- -- -- -- -- -- -- -- -- -- -- -- --
  31. ./ User Height
  32. this.standingHeight=1.75; ./ ~5'9" in meters
  33. ./ Movement Speed Scalar
  34. this.movementScalar=1.0;
  35. ./ Max Movement Settings
  36. this.hasMovementLimit=true;
  37. this.movementMax = 100.0; ./ Meters per second
  38. ./ Jump Height Scalar
  39. this.jumpScalar=1.0;
  40. ./ Scale of the User & Settings
  41. ./ Standing Height, Jumping Height, Walking Speed, etc.
  42. this.userScale=1.0;
  43. ./ Max distance (Meters) up or down, like walking up and down stairs.
  44. this.maxStepHeight=5;
  45. ./ Once click/tap is done, how much does the last velocity ease out
  46. ./ Velocity * this.cameraEasing
  47. this.cameraEasing = [ .55, .45 ]; ./ [ PC, Mobile ]
  48. ./ Touch screen sensitivity settings
  49. this.touchMaxSensitivity = 800;
  50. ./ The jumping impulse per frame
  51. ./this.cameraJumpImpulse= [ 0.045, 0.085 ];// [.035,.075]; // [ Grav, Low Grav ]
  52. this.cameraJumpImpulse= [ .75, 1.5 ];./ [.035,.075]; // [ Grav, Low Grav ]
  53. this.cameraMaxJumpHold=[ 2.85, 2.0 ]; ./ Second; [ Grav, Low Grav ]
  54. this.gravityCount=0;
  55. this.gravityRate=0;
  56. this.gravityMax=15.5; ./ Gravity at scale of 1 / avg human(1.8 meters) * 9.8mms = 5.44; But this is digital here, lower is lighter
  57. this.gravityUPS=[.3,.15]; ./ Units Per Step(); Influence of M/S^2 .. I guess haha; [ Grav, Low Grav ]
  58. ./ -- -- -- //
  59. ./ -- -- -- //
  60. ./ -- -- -- //
  61. this.pxlAudio=null;
  62. this.pxlTimer=null;
  63. this.pxlAutoCam=null;
  64. this.pxlEnv=null;
  65. this.pxlColliders=null;
  66. this.pxlUser=null;
  67. this.pxlUtils=null;
  68. this.pxlDevice=null;
  69. this.pxlGuiDraws=null;
  70. this.pxlQuality=null;
  71. this.pxlOptions=null;
  72. this.socket=null;
  73. this.camera=null;
  74. this.canMove=true;
  75. this.HDRView=false;
  76. ./ Run updateCamera
  77. this.camUpdated=true;
  78. this.cameraBooted=false;
  79. ./ Calculation triggers
  80. this.hasMoved = false; ./ Player has moved camera, WASD or Arrow Keys
  81. this.hasRotated = false; ./ Player clicked and dragged mouse/touch to rotate camera
  82. this.hasJumped = false; ./ Initial Jump Trigger
  83. this.hasJumpLock = false; ./ Held jump upon landing, jump again after delay, this is the delay notification
  84. this.hasGravity = false; ./ Player isn't on the ground; due to jump or running off a ledge
  85. ./ -- -- --
  86. ./ User-Input Movement & Look Scalars
  87. ./ Only set when there is analog / float input; like gamepad thumbstick or touch 'joysticks'
  88. this.userInputMoveScalar = {x:1,y:1};
  89. this.userInputLookScalar = 1;
  90. this.roomStandingHeight = { 'default' : this.standingHeight };
  91. this.standingHeightGravInfluence=0;
  92. this.standingMaxGravityOffset=.5; ./ Usage - ( standingHeight / standingHeightGravInfluence ) * standingMaxGravityOffset
  93. this.walkBounceSeed=230;
  94. this.walkBounceHeight = .3; ./ sin(walkBounce) * walkBounceHeight
  95. this.walkBounce=0;
  96. this.walkBouncePerc=0;
  97. this.walkBounceRate=.025; ./ Bounce rate per frame; walkBounce + walkBounceRate
  98. this.walkBounceEaseIn=.03; ./ Ease in bouncePerc rate up to 1; perc + easeIn
  99. this.walkBounceEaseOut=.95; ./ Ease out bouncePerc scalar; perc * easeOut
  100. this.posRotEasingThreshold=.01; ./ Velocity based calculations with any per frame scalar cut off value; val<posRotEasingThreshold ? 0
  101. this.cameraMovement=[0,0]; ./ Left/Right, Forward/Back, Jump
  102. this.cameraMovementEase=.85; ./ After key up, pre-frame rate to 0
  103. this.cameraMoveLength=0;
  104. this.cameraMoveLengthMult=.1; ./ cameraMoveLength scalar // ## check adding multiplier to keydown movement calculations
  105. this.camPosBlend=.65; ./ Blend to previous position, easing movement
  106. this.camRotXYZ=new Vector3(0,0,0);./new Vector3(0,0,0);
  107. this.camRotPitch=new Vector2(0,0);
  108. this.cameraJumpActive=false;
  109. this.cameraAllowJump=true;
  110. this.cameraJumpHeight=0;
  111. this.cameraJumpVelocity=0;
  112. this.cameraJumpVelocityEaseOut=.90; ./ Ease out Jump Button Influence after Button Released
  113. this.cameraJumpInAir=false;
  114. this.floorColliderInitialHit=false;
  115. this.colliderValidityChecked=true; ./ Value shouldn't matter, but should Room Environments not set colliderValidity, assume checked initially
  116. this.nearestFloorHit=new Vector3(0,0,0);
  117. this.nearestFloorObjName=null;
  118. this.nearestFloorHitPrev=new Vector3(0,0,0);
  119. this.nearestFloorObjNamePrev=null;
  120. this.gravitySourceActive=false;
  121. this.gravityDirection=new Vector3( 0, -1, 0 );
  122. this.gravityEaseOutRate=.50;
  123. this.jump=0;
  124. ./ TODO : Unsure if I'd rather a contant timer for all "allowed" jumps or not
  125. ./ For now, this lock holds that the player should jump again when the timer is up
  126. this.releaseJumpLockTime = 0;
  127. this.releaseJumpLockDelay = .08; ./ Seconds dely between repeated jumping, its less jaring with a slight delay
  128. this.runMain=true;
  129. this.workerActive=false;
  130. this.worker=null;
  131. this.workerTransfers=false;
  132. this.workerMessage=()=>{}; ./ Browser Compatibility
  133. this.workerFunc=()=>{}; ./ Browser Compatibility
  134. this.deviceKey=()=>{}; ./ Browser Compatibility
  135. this.portalList={};
  136. this.roomWarpZone=[];
  137. this.colliderCurObjHit=null;
  138. this.colliderPrevObjHit=null;
  139. this.colliderValid=false;
  140. this.colliderFail=false;
  141. this.warpActive=false;
  142. this.warpType=0;
  143. this.warpObj=null;
  144. this.warpTarget=null;
  145. this.hotKeyTriggered=false;
  146. this.eventCheckStatus=false;
  147. this.proximityScaleTrigger=false;
  148. this.colliderShiftActive=true;
  149. this.colliderAdjustPerc=0;
  150. this.colliderAdjustRate=.020;
  151. ./ ## Move to Device.js
  152. this.gyroGravity=[0,0,0];
  153. this.cameraPose={
  154. alpha:null,
  155. beta:null,
  156. gamma:null,
  157. alphaOffset:0,
  158. betaOffset:0,
  159. gammaOffset:0,
  160. orientation:window.orientation||0,
  161. pos:[0,0,0],
  162. posOffset:[0,0,0],
  163. rx:()=>{return this.beta},
  164. ry:()=>{return this.alpha},
  165. rz:()=>{return this.gamma},
  166. accelZeroed:[0,0,0],
  167. accelCalibration:10,
  168. accelCalDiv:1.10,
  169. accelCalCount:0,
  170. accelTotal:[0,0,0],
  171. accelPrev:null,
  172. accelDelta:[0,0,0],
  173. accelClearDelta:()=>{this.accelDelta=[0,0,0];},
  174. };
  175. this.uniformScalars={
  176. curExp:1.0,
  177. darkBase:.1,
  178. brightBase:0.5,
  179. exposureUniformBase:1.0,
  180. }
  181. ./ TODO : This needs to be moved and integrated into fileIO and RoomClass
  182. this.cameraPosLookAtNames = {
  183. "default":{
  184. pos:"position",
  185. lookAt:"lookat",
  186. },
  187. "mobile":{
  188. pos:"positionmobile",
  189. lookAt:"lookatmobile",
  190. },
  191. "vr":{
  192. pos:"positionvr",
  193. lookAt:"lookatvr",
  194. }
  195. };
  196. this.cameraPos=new Vector3(0,0,0);
  197. this.cameraPrevPos=new Vector3(0,0,0);;
  198. this.cameraPrevLookAt=new Vector3(0,0,0);;
  199. this.cameraAim=new Vector3(0,0,1);
  200. this.cameraAimTarget=new Vector3(0,0,0);;
  201. this.cameraCross=new Vector3(1,0,0); ./ For Audio API faux 3d audio; UNUSED CURRENTLY
  202. this.lookAtTargetActive=new Vector3(0,0,0);;
  203. this.lookAtPerc=new Vector2(1,0);
  204. this.lookAtLockPerc=0;
  205. this.lookAtLockFader=0;
  206. this.lookAtLockFadeRate=.01;
  207. this.prevQuaternion=new Quaternion(); ./ Used in motionBlur shader calculations only
  208. ./this.prevWorldMatrix= new Matrix4(); // Only used if running higher quality motionBlur calculations, not needed
  209. this.pi=3.141592653589793238462643383279;
  210. this.touchSensitivityLimits = this.touchMaxSensitivity * this.pi;
  211. ./ Event callbacks
  212. this.callbacks={};
  213. this.init();
  214. }
  215. .**
  216. * Sets dependencies for the Camera class.
  217. * @param {Object} pxlNav - The navigation object containing dependencies.
  218. * @private
  219. *.
  220. setDependencies( pxlNav ){
  221. this.pxlAudio=pxlNav.pxlAudio;
  222. this.pxlTimer=pxlNav.pxlTimer;
  223. this.pxlAutoCam=pxlNav.pxlAutoCam;
  224. this.pxlEnv=pxlNav.pxlEnv;
  225. this.pxlColliders=pxlNav.pxlColliders;
  226. this.pxlUser=pxlNav.pxlUser;
  227. this.pxlUtils=pxlNav.pxlUtils;
  228. this.pxlDevice=pxlNav.pxlDevice;
  229. this.pxlGuiDraws=pxlNav.pxlGuiDraws;
  230. this.pxlQuality=pxlNav.pxlQuality;
  231. this.pxlOptions=pxlNav.pxlOptions;
  232. this.socket=pxlNav.socket;
  233. }
  234. ./ -- -- --
  235. log( msg ){
  236. if( this.verbose >= VERBOSE_LEVEL.INFO ){
  237. console.log( msg );
  238. }
  239. }
  240. warn( msg ){
  241. if( this.verbose >= VERBOSE_LEVEL.WARN ){
  242. console.warn( msg );
  243. }
  244. }
  245. error( msg ){
  246. if( this.verbose >= VERBOSE_LEVEL.ERROR ){
  247. console.error( msg );
  248. }
  249. }
  250. ./ -- -- --
  251. .**
  252. * Initializes the Camera class and Camera Worker.
  253. *.
  254. ./ TODO : Get worker implemented for whole of camera scripts
  255. init(){
  256. ./ Camera Service Worker - Ray Intersect Collision Detector
  257. ./ TODO : Finish adding workers to monitor the Collider class and raycasting
  258. var worker;
  259. if( false && Worker ){
  260. worker = new Worker("js.pxlBase/webWorkers/CameraWorker.js");
  261. this.worker=worker;
  262. ./this.workerList.push( worker );
  263. let tmpThis=this;
  264. worker.onmessage= (event) => {
  265. tmpThis.workerMessage(event.data);
  266. };
  267. worker.onerror= (event)=>{
  268. tmpThis.workerMessage({type:"err", data:event.data});
  269. };
  270. ./ Transferables Status
  271. let ab= new ArrayBuffer(1);
  272. worker.postMessage(ab, [ab]); ./ ab.byteLength -> If transfer successful
  273. this.workerTransfers=ab.byteLength==0; ./ Can transfer ArrayBuffers directly
  274. ./ Message Functions
  275. this.workerMessage= async ( msg )=>{
  276. if( msg.type == "update" ){
  277. tmpThis.updateMainValues( msg );
  278. }
  279. }
  280. this.workerFunc= async ( type, state=null )=>{
  281. tmpThis.worker.postMessage({type, state})
  282. }
  283. this.deviceKey= async ( key=false, state=false )=>{
  284. if( typeof key == "number"){
  285. }else if( typeof key == "string"){
  286. let type="key";
  287. tmpThis.worker.postMessage({type, key, state})
  288. }
  289. }
  290. this.workerActive=true;
  291. this.runMain=false;
  292. this.workerFunc("init");
  293. }
  294. }
  295. .**
  296. * Updates main values from worker data.
  297. * UNUSED CURRENTLY
  298. * @param {Object} data - The data from the worker.
  299. * @private
  300. *.
  301. updateMainValues( data ){
  302. let {gravityRate, standingHeightGravInfluence, cameraJumpImpulse}=data;
  303. if( gravityRate != null ){
  304. this.gravityRate = gravityRate;
  305. }
  306. if( standingHeightGravInfluence != null ){
  307. this.standingHeightGravInfluence = standingHeightGravInfluence;
  308. }
  309. if( cameraJumpImpulse != null ){
  310. this.cameraJumpVelocity+=cameraJumpImpulse;
  311. }
  312. this.camUpdated=true;
  313. }
  314. .///////////////////////////////////////////
  315. ./ Update settings externally of Camera //
  316. ./////////////////////////////////////////
  317. ./ Allow access to alter the Camera's internal settings
  318. ./ Run these updates during the Room's `init()`, `start()`, or `build()` functions
  319. ./ TODO : Allow per-Room settings to be set in the Room's FBX file or Room Class
  320. ./ TODO : Move these to the User class when updated for pxlNav
  321. ./ User is currently as it was for Antibody.club, so it needs to be updated for the pxlNav module
  322. ./ Default is 1.75
  323. .**
  324. * Sets the user's standing height.
  325. * @method
  326. * @memberof pxlCamera
  327. * @param {number} val - The user's standing height.
  328. * @param {string} [roomName='default'] - The name of the room.
  329. * @example
  330. * ./ Set the user's standing height to 1.75 meters.
  331. * this.pxlCamera.setUserHeight( 1.75 );
  332. *.
  333. setUserHeight( val, roomName="default" ){
  334. val = Math.max( val, 0.01 );
  335. if( !this.roomStandingHeight.hasOwnProperty(roomName) ){
  336. this.roomStandingHeight[roomName]=this.standingHeight;
  337. }
  338. if( roomName=="default" ){
  339. this.standingHeight=val;
  340. }
  341. this.roomStandingHeight[roomName]=val;
  342. }
  343. ./ Default is 5
  344. .**
  345. * Sets the user's maximum step height.
  346. * @method
  347. * @memberof pxlCamera
  348. * @param {number} val - The user's maximum step height.
  349. * @example
  350. * ./ Set the user's maximum step height to 5 meters.
  351. * this.pxlCamera.setMaxStepHeight( 5 );
  352. *.
  353. setMaxStepHeight( val ){
  354. val = Math.max( val, 0.01 );
  355. this.maxStepHeight=val;
  356. }
  357. ./ Default is 1
  358. .**
  359. * Sets the user's scale.
  360. * @method
  361. * @memberof pxlCamera
  362. * @param {number} val - The user's scale.
  363. * @example
  364. * ./ Set the user's scale to 1.
  365. * this.pxlCamera.setUserScale( 1 );
  366. *.
  367. setUserScale( val ){
  368. val = Math.max( val, 0.01 );
  369. this.userScale=val;
  370. }
  371. ./ Default is 1
  372. .**
  373. * Sets the user's movement scalar.
  374. * @method
  375. * @memberof pxlCamera
  376. * @param {number} val - The user's movement scalar.
  377. * @example
  378. * ./ Set the user's movement scalar to 1.
  379. * this.pxlCamera.setMovementScalar( 1 );
  380. *.
  381. setMovementScalar( val ){
  382. val = Math.max( val, 0.01 );
  383. this.movementScalar=val;
  384. }
  385. ./ Default is 10
  386. .**
  387. * Sets the user's maximum movement speed.
  388. * @method
  389. * @memberof pxlCamera
  390. * @param {number} val - The user's maximum movement speed.
  391. * @example
  392. * ./ Set the user's maximum movement speed to 10 meters per second.
  393. * this.pxlCamera.setMovementMax( 10 );
  394. *.
  395. setMovementMax( val ){
  396. val = Math.max( val, 0.01 );
  397. this.movementMax=val;
  398. }
  399. ./ Default is 0.85
  400. .**
  401. * Sets the user's movement easing rate.
  402. * @method
  403. * @memberof pxlCamera
  404. * @param {number} val - The user's movement easing rate.
  405. * @example
  406. * ./ Set the user's movement easing rate to 0.85.
  407. * this.pxlCamera.setMovementEase( 0.85 );
  408. *.
  409. setMovementEase( val ){
  410. val = Math.min( 1, Math.max( val, 0.01 ) );
  411. this.cameraMovementEase=val;
  412. }
  413. ./ Default is 0.75
  414. .**
  415. * Sets the jump scalar.
  416. * @method
  417. * @memberof pxlCamera
  418. * @param {number} val - The jump scalar.
  419. * @example
  420. * ./ Set the jump scalar to 0.75.
  421. * this.pxlCamera.setJumpScalar( 0.75 );
  422. *.
  423. setPositionBlend( val ){
  424. val = Math.min( 1, Math.max( val, 0.01 ) );
  425. this.camPosBlend=val;
  426. }
  427. ./ Default is 0.1
  428. .**
  429. * Sets the user's movement multiplier.
  430. * @method
  431. * @memberof pxlCamera
  432. * @param {number} val - The user's movement multiplier.
  433. * @example
  434. * ./ Set the user's movement multiplier to 0.1.
  435. * this.pxlCamera.setInputMovementMult( 0.1 );
  436. *.
  437. setInputMovementMult( val ){
  438. val = Math.max( val, 0.01 );
  439. this.cameraMoveLengthMult=val;
  440. }
  441. ./ -- -- --
  442. ./ Default is 1
  443. .**
  444. * Sets the user's jump scalar.
  445. * @method
  446. * @memberof pxlCamera
  447. * @param {number} val - The user's jump scalar.
  448. * @example
  449. * ./ Set the user's jump scalar to 1.
  450. * this.pxlCamera.setJumpScalar( 1 );
  451. *.
  452. setJumpScalar( val ){
  453. val = Math.max( val, 0.01 );
  454. this.jumpScalar=val;
  455. }
  456. ./ Default is 0.75
  457. .**
  458. * Sets the user's jump impulse.
  459. * @method
  460. * @memberof pxlCamera
  461. * @param {number} val - The user's jump impulse.
  462. * @example
  463. * ./ Set the user's jump impulse to 0.75.
  464. * this.pxlCamera.setJumpImpulse( 0.75 );
  465. *.
  466. setJumpImpulse( val ){
  467. val = Math.max( val, 0.01 );
  468. this.cameraJumpImpulse[0]=val;
  469. }
  470. ./ Default is 2.85
  471. .**
  472. * Sets the user's maximum jump hold.
  473. * @method
  474. * @memberof pxlCamera
  475. * @param {number} val - The user's maximum jump hold.
  476. * @example
  477. * ./ Set the user's maximum jump hold to 2.85.
  478. * this.pxlCamera.setJumpHoldMax( 2.85 );
  479. *.
  480. setJumpHoldMax( val ){
  481. val = Math.max( val, 0.01 );
  482. this.cameraMaxJumpHold[0]=val;
  483. }
  484. ./ Default is 0.08
  485. .**
  486. * Sets the user's jump repeat delay.
  487. * @method
  488. * @memberof pxlCamera
  489. * @param {number} val - The user's jump repeat delay.
  490. * @example
  491. * ./ Set the user's jump repeat delay to 0.08.
  492. * this.pxlCamera.setJumpRepeatDelay( 0.08 );
  493. *.
  494. setJumpRepeatDelay( val ){
  495. val = Math.max( val, 0.01 );
  496. this.releaseJumpLockDelay=val;
  497. }
  498. ./ -- -- --
  499. ./ Default is 0.85
  500. .**
  501. * Sets the user's camera movement easing.
  502. * @method
  503. * @memberof pxlCamera
  504. * @param {number} val - The user's camera movement easing.
  505. * @example
  506. * ./ Set the user's camera movement easing to 0.85.
  507. * this.pxlCamera.setCameraMoveEasing( 0.85 );
  508. *.
  509. setCameraRotateEasing( val ){
  510. if( !Array.isArray(val) ){
  511. if( typeof val == "number" ){
  512. val=[val,val];
  513. this.warn("Warning : Camera.setCameraEasing() expects an array of two numeric values, [PC Easing 0-1, Mobile Easing 0-1]; Default is [.55,.45]");
  514. }else{
  515. this.error("Error : Camera.setCameraEasing() unexpected values type; expecting an array of two numeric values, [PC Easing 0-1, Mobile Easing 0-1]; Default is [.55,.45]");
  516. }
  517. }
  518. this.cameraEasing=val;
  519. }
  520. ./ Touch Sensitivity should be a pixel-to-device reasonable value
  521. ./ Default is 500, being 500 pixels dragging range to look around
  522. .**
  523. * Sets the user's touch sensitivity.
  524. * @method
  525. * @memberof pxlCamera
  526. * @param {number} val - The user's touch sensitivity.
  527. * @example
  528. * ./ Set the user's touch sensitivity to 500.
  529. * this.pxlCamera.setTouchSensitivity( 500 );
  530. *.
  531. setTouchSensitivity( val ){
  532. if(val<=0){
  533. val=1;
  534. }
  535. this.touchMaxSensitivity=val;
  536. this.touchSensitivityLimits = this.touchMaxSensitivity * this.pi;
  537. }
  538. .**
  539. * Sets the user's gravity rate.
  540. * @method
  541. * @memberof pxlCamera
  542. * @param {number} val - The user's gravity rate.
  543. * @example
  544. * ./ Set the user's gravity rate to 0.3
  545. * this.pxlCamera.setGravityRate( 0.3 );
  546. *.
  547. setGravityRate( val ){
  548. if(val<=0){
  549. val=1;
  550. }
  551. this.gravityRate=val;
  552. }
  553. ./ Assume 1 unit is 1 meter/second^2
  554. ./ But default is 2.5, so it's a bit lighter than Earth's gravity
  555. .**
  556. * Sets the user's gravity rate.
  557. * @method
  558. * @memberof pxlCamera
  559. * @param {number} val - The user's gravity rate.
  560. * @example
  561. * ./ Set the user's gravity rate to 2.5
  562. * this.pxlCamera.setGravityRate( 2.5 );
  563. *.
  564. setGravityMax( val ){
  565. if(val<=0){
  566. val=1;
  567. }
  568. this.gravityMax=val;
  569. }
  570. ./ Set walking bounce settings
  571. ./ Default is 230
  572. .**
  573. * Sets the user's walk bounce seed.
  574. * @method
  575. * @memberof pxlCamera
  576. * @param {number} val - The user's walk bounce seed.
  577. * @example
  578. * ./ Set the user's walk bounce seed to 230.
  579. * this.pxlCamera.setWalkBounceSeed( 230 );
  580. *.
  581. setWalkBounceHeight( val ){
  582. if(val<=0){
  583. val=0;
  584. }
  585. this.walkBounceHeight=val;
  586. }
  587. ./ Default is 0.025
  588. .**
  589. * Sets the user's walk bounce rate.
  590. * @method
  591. * @memberof pxlCamera
  592. * @param {number} val - The user's walk bounce rate.
  593. * @example
  594. * ./ Set the user's walk bounce rate to 0.025.
  595. * this.pxlCamera.setWalkBounceRate( 0.025 );
  596. *.
  597. setWalkBounceRate( val ){
  598. if(val<=0){
  599. val=0.0001;
  600. }
  601. this.walkBounceRate=val;
  602. }
  603. ./ Default is 0.03
  604. .**
  605. * Sets the user's walk bounce ease in.
  606. * @method
  607. * @memberof pxlCamera
  608. * @param {number} val - The user's walk bounce ease in.
  609. * @example
  610. * ./ Set the user's walk bounce ease in to 0.03
  611. * this.pxlCamera.setWalkBounceEaseIn( 0.03 );
  612. *.
  613. setWalkBounceEaseIn( val ){
  614. val = Math.min( 1, Math.max( val, 0.0001 ) );
  615. this.walkBounceEaseIn=val;
  616. }
  617. ./ Default is 0.95
  618. .**
  619. * Sets the user's walk bounce ease out.
  620. * @method
  621. * @memberof pxlCamera
  622. * @param {number} val - The user's walk bounce ease out.
  623. * @example
  624. * ./ Set the user's walk bounce ease out to 0.95
  625. * this.pxlCamera.setWalkBounceEaseOut( 0.95 );
  626. *.
  627. setWalkBounceEaseOut( val ){
  628. val = Math.min( 1, Math.max( val, 0.01 ) );
  629. this.walkBounceEaseOut=val;
  630. }
  631. ./ -- -- --
  632. ./ Set camera values from `pxlUserSettings` structure
  633. ./ Should custom entries have been added, they wont be processed
  634. ./ TODO : This is a bit messy at the moment
  635. ./ TODO : Add a per-room userSettings with lookup into the `pxlUserSettings` structure
  636. ./ Since with this set structure, it doesn't need to be individual variables
  637. .**
  638. * Sets the user's settings.
  639. * @method
  640. * @memberof pxlCamera
  641. * @param {Object} userSettingsObject - The user's settings object.
  642. * @example
  643. * ./ Set the user's settings.
  644. * import { pxlUserSettings } from "...core/Options.js";
  645. * let userSettingsObject = Object.assign({}, pxlUserSettings);
  646. * this.pxlCamera.setUserSettings( userSettingsObject );
  647. *.
  648. setUserSettings( userSettingsObject ){
  649. ./ User & collider step height settings
  650. if( userSettingsObject.hasOwnProperty("height") ){
  651. if( userSettingsObject.height.hasOwnProperty("standing") ){
  652. this.setUserHeight( userSettingsObject.height.standing );
  653. }
  654. if( userSettingsObject.height.hasOwnProperty("stepSize") ){
  655. this.setMaxStepHeight( userSettingsObject.height.stepSize );
  656. }
  657. }
  658. ./ -- -- --
  659. ./ Camera movement settings
  660. if( userSettingsObject.hasOwnProperty("movement") ){
  661. if( userSettingsObject.movement.hasOwnProperty("scalar") ){
  662. this.setMovementScalar( userSettingsObject.movement.scalar );
  663. }
  664. if( userSettingsObject.movement.hasOwnProperty("max") ){
  665. this.setMovementMax( userSettingsObject.movement.max );
  666. }
  667. if( userSettingsObject.movement.hasOwnProperty("easing") ){
  668. this.setMovementEase( userSettingsObject.movement.easing );
  669. }
  670. }
  671. ./ -- -- --
  672. ./ Head bounce settings
  673. if( userSettingsObject.hasOwnProperty("headBounce") ){
  674. if( userSettingsObject.headBounce.hasOwnProperty("height") ){
  675. this.setWalkBounceHeight( userSettingsObject.headBounce.height );
  676. }
  677. if( userSettingsObject.headBounce.hasOwnProperty("rate") ){
  678. this.setWalkBounceRate( userSettingsObject.headBounce.rate );
  679. }
  680. if( userSettingsObject.headBounce.hasOwnProperty("easeIn") ){
  681. this.setWalkBounceEaseIn( userSettingsObject.headBounce.easeIn );
  682. }
  683. if( userSettingsObject.headBounce.hasOwnProperty("easeOut") ){
  684. this.setWalkBounceEaseOut( userSettingsObject.headBounce.easeOut );
  685. }
  686. }
  687. ./ -- -- --
  688. ./ Jump settings
  689. if( userSettingsObject.hasOwnProperty("jump") ){
  690. if( userSettingsObject.jump.hasOwnProperty("impulse") ){
  691. this.setJumpScalar( userSettingsObject.jump.impulse );
  692. }
  693. if( userSettingsObject.jump.hasOwnProperty("holdMax") ){
  694. this.setJumpHoldMax( userSettingsObject.jump.holdMax );
  695. }
  696. if( userSettingsObject.jump.hasOwnProperty("repeatDelay") ){
  697. this.setJumpRepeatDelay( userSettingsObject.jump.repeatDelay );
  698. }
  699. }
  700. ./ -- -- --
  701. ./ Gravity settings
  702. ./ From a Jump or running off a ledge
  703. if( userSettingsObject.hasOwnProperty("gravity") ){
  704. if( userSettingsObject.gravity.hasOwnProperty("ups") ){
  705. this.setGravityRate( userSettingsObject.gravity.ups );
  706. }
  707. if( userSettingsObject.gravity.hasOwnProperty("max") ){
  708. this.setGravityMax( userSettingsObject.gravity.max );
  709. }
  710. }
  711. }
  712. .////////////////////////
  713. ./ Main runtime loop //
  714. .//////////////////////
  715. .**
  716. * Main step function to update camera state.
  717. * @private
  718. *.
  719. step(){
  720. ./ Update camera position with out gravity, jump, or collider influences.
  721. ./ Simply apply initial frame movement vectors
  722. if(this.pxlDevice.directionKeyDown){
  723. this.updateMovement(this.pxlTimer.prevMS);
  724. }
  725. if( this.runMain ){
  726. if( this.hasJumpLock && this.pxlTimer.runtime > this.releaseJumpLockTime ){
  727. this.hasJumpLock = false;
  728. this.hasGravity = false;
  729. this.cameraAllowJump = true;
  730. this.camInitJump();
  731. }
  732. if( this.hasGravity && this.cameraJumpActive ){
  733. this.camJump(this.pxlTimer.prevMS);
  734. }else if(this.cameraJumpVelocity>0 ){
  735. this.killJumpImpulse();
  736. }
  737. }
  738. ./ Check if camera calculations should be ran
  739. this.camUpdated= this.camUpdated || this.hasMoved || this.hasRotated || this.hasGravity || this.cameraMovement[0]!=0 || this.cameraMovement[1]!=0 ;./ || this.pxlDevice.cursorLockActive;
  740. this.updateCamera();
  741. ./ Update Shaders to Camera Position / Rotation changes
  742. this.lowQualityUpdates();
  743. this.midQualityUpdates();
  744. this.eventCheck();
  745. }
  746. .**
  747. * Checks for events based on environment triggers.
  748. * Currently only checking for Ground Collider, Room Warps, and Portals
  749. * @private
  750. *.
  751. eventCheck(){
  752. if( this.colliderValid && this.eventCheckStatus){
  753. ./ Camera Translate Events don't need further calculatons; Room Warps and Portals
  754. if( this.eventTrigger(this.nearestFloorObjName) ){
  755. this.warpEventTriggered(1, this.nearestFloorObjName);
  756. ./return;
  757. }
  758. }
  759. }
  760. .**
  761. * Updates device values based on velocity easing magnitude.
  762. * @param {number} velEaseMag - The current velocity magnitude.
  763. * @private
  764. *.
  765. updateDeviceValues( velEaseMag ){
  766. if(!this.pxlQuality.settings.leftRight){
  767. let tankRotate=-this.cameraMovement[0];
  768. if(!this.pxlDevice.touchMouseData.active){
  769. this.pxlDevice.touchMouseData.velocity.x+=tankRotate;
  770. ./this.pxlDevice.touchMouseData.velocityEase.x+=tankRotate;
  771. }
  772. this.pxlDevice.touchMouseData.netDistance.x+=tankRotate*4.0;
  773. }
  774. ./let stillMoving=false;
  775. ./ PC Mouse Movement
  776. if(this.pxlDevice.touchMouseData.velocity!=null && this.pxlDevice.mobile==0){
  777. if(velEaseMag<this.posRotEasingThreshold){
  778. this.pxlDevice.touchMouseData.velocity.multiplyScalar(0);
  779. ./this.pxlDevice.touchMouseData.velocityEase.multiplyScalar(0);
  780. }else{
  781. let curEasing = this.cameraEasing[ (this.pxlDevice.mobile?1:0) ];
  782. this.pxlDevice.touchMouseData.velocity.multiplyScalar( curEasing );
  783. ./this.pxlDevice.touchMouseData.velocityEase.multiplyScalar( curEasing );
  784. }
  785. this.pxlDevice.touchMouseData.netDistance.add( this.pxlDevice.touchMouseData.velocity.clone().multiply( this.pxlDevice.touchMouseData.moveMult ) );
  786. }
  787. }
  788. ./////////////////////////////////////////
  789. ./ Gyroscope Enabled Device Functions //
  790. .///////////////////////////////////////
  791. .**
  792. * Builds device-pose monitors for gyroscope-enabled devices.
  793. * CURRENTLY UNWORKING
  794. * NOTE : Development in-progress through 'Device.js'
  795. * @private
  796. *.
  797. buildDeviceMonitors(){
  798. let camObject=this;
  799. ./window.addEventListener('deviceorientation', (e)=>{camObject.devicePoseData(camObject,e)} );
  800. ./window.addEventListener('orientationchange', (e)=>{camObject.deviceOrientationData(camObject,e)} );
  801. ./window.addEventListener('devicemotion', (e)=>{camObject.deviceMotionData(camObject,e)} );
  802. ./%=
  803. ./gyroscope=new Gyroscope({frequency:10});
  804. ./gyroscope.addEventListener('reading',gyroPoseData);
  805. ./gyroscope.start();
  806. ./window.addEventListener('devicemotion', deviceMotionData);
  807. ./window.addEventListener('orientationchange', devicePoseChange); // Don't know when this ever fires, docs seem like it should run tho
  808. ./ Based around w3.org's Accelerometer builder
  809. .* navigator.permissions.query({ name: 'accelerometer' }).then(result => {
  810. if (result.state === 'denied') {
  811. console.log('Permission to use accelerometer sensor is denied.');
  812. return;
  813. }
  814. let acl = new Accelerometer({frequency: 10});
  815. let max_magnitude = 0;
  816. acl.addEventListener('activate', () => console.log('Ready to measure.'));
  817. acl.addEventListener('error', error => console.log(`Error: ${error.name}`));
  818. acl.addEventListener('reading', () => {
  819. let magnitude = Math.hypot(acl.x, acl.y, acl.z);
  820. if (magnitude > max_magnitude) {
  821. max_magnitude = magnitude;
  822. console.log(`Max magnitude: ${max_magnitude} m.s2`);
  823. }
  824. });
  825. acl.start();
  826. });*.
  827. ./%
  828. }
  829. .*
  830. gyroPoseData(camObj,e){
  831. let x=gyroscope.x;
  832. let y=gyroscope.y;
  833. let z=gyroscope.z;
  834. let prevGyro=[...this.pxlDevice.gyroGravity];
  835. this.pxlDevice.gyroGravity=[x,y,z];
  836. accumGravity=[accumGravity[0]+x-prevGyro[0],accumGravity[1]+y-prevGyro[1],accumGravity[2]+z-prevGyro[2]];
  837. }
  838. deviceMotionData(camObj,e){
  839. let acc=e.acceleration;./IncludingGravity;
  840. ./let ag=e.accelerationIncludingGravity;
  841. if(camObj.cameraPose.accelCalCount<camObj.cameraPose.accelCalibration){
  842. camObj.cameraPose.accelCalCount+=1;
  843. camObj.cameraPose.accelZeroed[0]+=acc.x;
  844. camObj.cameraPose.accelZeroed[1]+=acc.y;
  845. camObj.cameraPose.accelZeroed[2]+=acc.z;
  846. if(camObj.cameraPose.accelCalCount==camObj.cameraPose.accelCalibration){
  847. camObj.cameraPose.accelZeroed[0]*=camObj.cameraPose.accelCalDiv;
  848. camObj.cameraPose.accelZeroed[1]*=camObj.cameraPose.accelCalDiv;
  849. camObj.cameraPose.accelZeroed[2]*=camObj.cameraPose.accelCalDiv;
  850. }
  851. }
  852. camObj.cameraPose.accelDelta[0]=acc.x-camObj.cameraPose.accelZeroed[0];
  853. camObj.cameraPose.accelDelta[1]=acc.y-camObj.cameraPose.accelZeroed[1];
  854. camObj.cameraPose.accelDelta[2]=acc.z-camObj.cameraPose.accelZeroed[2];
  855. camObj.cameraPose.accelTotal[0]+=acc.x-camObj.cameraPose.accelZeroed[0];
  856. camObj.cameraPose.accelTotal[1]+=acc.y-camObj.cameraPose.accelZeroed[1];
  857. camObj.cameraPose.accelTotal[2]+=acc.z-camObj.cameraPose.accelZeroed[2];
  858. }
  859. devicePoseData(camObj,e){
  860. if(e.alpha!==null){
  861. camObj.abs=e.absolute;
  862. camObj.cameraPose.gamma=e.gamma; ./ Yaw
  863. camObj.cameraPose.beta=e.beta; ./ Pitch
  864. camObj.cameraPose.alpha=e.alpha; ./ Roll
  865. camObj.cameraPose.alphaOffset=e.alphaOffset||0; ./ Roll
  866. }
  867. }
  868. deviceOrientationData(camObj,e){
  869. camObj.cameraPose.orientation=window.orientation||0;
  870. }
  871. *.
  872. .////////////////////////////////////////////////////////////////////
  873. ./ Set Camera Position / Look At Functions; Room Warps & Portals //
  874. .//////////////////////////////////////////////////////////////////
  875. .**
  876. * Updates camera matrices.
  877. * Updates - Projection Matrix, Matrix World, & World Matrix
  878. * @example
  879. * import { Object3D } from "three";
  880. * let emptyObject=new Object3D();
  881. *
  882. * emptyObject.position.set(10,10,0);
  883. *
  884. * this.pxlCamera.setTransform(emptyObject.position);
  885. *
  886. * ./ Update camera matrices.
  887. * this.pxlCamera.updateCameraMatrices();
  888. *.
  889. updateCameraMatrices(){
  890. this.camera.updateProjectionMatrix();
  891. this.camera.updateMatrixWorld();
  892. this.camera.updateWorldMatrix();
  893. }
  894. .**
  895. * Resets camera calculations to a Vector3.
  896. * @param {Vector3} newPosition - The new position for the camera.
  897. * @example
  898. * import { Vector3 } from "three";
  899. * let newPos=new Vector3(20,5,15);
  900. * this.pxlCamera.resetCameraCalculations(newPos);
  901. *.
  902. resetCameraCalculations( newPosition ){
  903. this.cameraMovement[0] = 0;
  904. this.cameraMovement[1] = 0;
  905. this.pxlDevice.touchMouseData.curFadeOut.multiplyScalar(0);
  906. this.pxlDevice.touchMouseData.velocity.multiplyScalar(0);
  907. this.pxlDevice.touchMouseData.netDistance.set(0,0);
  908. this.camera.position.copy( newPosition );
  909. this.updateCameraMatrices();
  910. this.cameraPos.copy( newPosition );
  911. this.cameraPrevPos.copy( newPosition );
  912. ./ Force collision detection
  913. this.colliderCurObjHit=null;
  914. this.colliderPrevObjHit=null;
  915. this.camUpdated=true; ./ Forces next frame update
  916. this.hasMoved=true; ./ Forces next frame update
  917. }
  918. .**
  919. * Sets the field-of-view for the camera.
  920. * @param {number} fov - The field-of-view.
  921. * @example
  922. * ./ Set the field-of-view to 75.
  923. * this.pxlCamera.setFOV( 75 );
  924. *.
  925. setFOV( fov ){
  926. this.camera.fov=fov;
  927. this.camera.updateProjectionMatrix();
  928. this.camUpdated=true;
  929. }
  930. .**
  931. * Sets camera stats including FOV, aspect ratio, near and far clipping planes.
  932. * Used when changing Rooms
  933. * @param {number} fov - The field of view.
  934. * @param {number} aspect - The aspect ratio.
  935. * @param {number} near - The near clipping plane.
  936. * @param {number} far - The far clipping plane.
  937. * @example
  938. * ./ Set the camera stats.
  939. * ./ FOV: 75, Aspect: 1.33, Near: 0.1, Far: 1000
  940. * this.pxlCamera.setStats( 75, 1.33, 0.1, 1000 );
  941. *.
  942. setStats( fov, aspect, near, far ){
  943. ./ TODO : Aspect is weird, I need to work out better calculations for this
  944. ./this.camera.aspect=aspect;
  945. this.camera.near=near;
  946. this.camera.far=far;
  947. this.setFOV(fov);
  948. }
  949. setAspect( aspect ){
  950. this.camera.aspect=aspect;
  951. this.camera.updateProjectionMatrix();
  952. ./this.camUpdated=true;
  953. }
  954. .**
  955. * Sets the camera transform to a specific position and lookAt target.
  956. * For camera position changes, portals, and room warps
  957. * @param {Vector3} pos - The position to set the camera.
  958. * @param {Vector3} [lookAt=null] - The lookAt target.
  959. * @example
  960. * import { Vector3 } from "three";
  961. * let pos=new Vector3(10,15,15);
  962. * let lookAt=new Vector3(0,5,0);
  963. *
  964. * this.pxlCamera.setTransform(pos, lookAt);
  965. *.
  966. setTransform(pos, lookAt=null){ ./ Vector3, Vector3
  967. this.resetCameraCalculations(pos); ./ Reinitiates Camera; Forces collision detection, Kills user inputs
  968. if(lookAt){
  969. this.cameraAimTarget.position.copy( lookAt );
  970. this.camera.lookAt(lookAt);
  971. this.cameraPrevLookAt.copy( lookAt );
  972. this.updateCameraMatrices();
  973. this.pxlDevice.touchMouseData.initialQuat=this.camera.quaternion.clone();
  974. }
  975. this.resetGravity();
  976. this.camUpdated=true; ./ Forces next frame update
  977. }
  978. .**
  979. * Best used without a `lookAt` target to allow the object's rotation to be adopted.
  980. * If you just want to move the camera without rotational changes -or- to an object with a lookAt, it's better to use `setTransform`
  981. * @param {Object3D} obj - The object to set the camera to.
  982. * @param {Object3D|string} [lookAt=null] - The lookAt Camera Position Name or target object.
  983. * If a string is provided, it checks for the camera position if it exists in the Room,
  984. * Ussuall set in your FBX file.
  985. * @example
  986. * import { Object3D } from "three";
  987. * let obj=new Object3D();
  988. * obj.position.set(10,15,15);
  989. *
  990. * this.pxlCamera.setToObj(obj);
  991. *.
  992. setToObj(obj, lookAt=null){ ./ Object3D, Object3D
  993. this.resetCameraCalculations( obj.position ); ./ Reinitiates Camera; Forces collision detection, Kills user inputs
  994. ./ If no lookat, adopt Object rotation
  995. if(lookAt){
  996. let toLookAt=lookAt.position.clone();
  997. this.cameraAimTarget.position.copy( toLookAt );
  998. this.camera.lookAt(toLookAt);
  999. this.cameraPrevLookAt.copy( toLookAt );
  1000. this.updateCameraMatrices();
  1001. this.pxlDevice.touchMouseData.initialQuat=this.camera.quaternion.clone();
  1002. }else{
  1003. this.pxlDevice.touchMouseData.initialQuat=obj.quaternion.clone();
  1004. this.camera.setRotationFromQuaternion(this.pxlDevice.touchMouseData.initialQuat);
  1005. this.updateCameraMatrices();
  1006. }
  1007. this.resetGravity();
  1008. this.camUpdated=true; ./ Forces next frame update
  1009. this.hasMoved=true;
  1010. this.hasRotated=true;
  1011. this.colliderCheck( obj.position );
  1012. this.nearestFloorObjName=null;
  1013. }
  1014. .**
  1015. * Warps the camera to a specific room.
  1016. * @param {string} roomName - The name of the room to warp to.
  1017. * @param {boolean} [start=false] - Whether to run the room's `start()` function.
  1018. * @param {Object3D} [objTarget=null] - The target object in the room.
  1019. * @example
  1020. * ./ Warp the camera to the default location in "OutletEnvironment" room.
  1021. * this.pxlCamera.warpToRoom( "OutletEnvironment", true );
  1022. *
  1023. * ./ Warp to the 'aboutMe' camera position in "CampfireEnvironment" room.
  1024. * ./ Note, camera positions are set in your FBX by the camera's group name.
  1025. * this.pxlCamera.warpToRoom( "CampfireEnvironment", true, "aboutMe" );
  1026. *.
  1027. warpToRoom(roomName, start=false, objTarget=null){
  1028. let roomKeys = Object.keys(this.pxlEnv.roomSceneList);
  1029. let roomEnv = this.pxlEnv.roomSceneList[this.pxlEnv.currentRoom]
  1030. objTarget = objTarget || roomEnv.defaultCamLocation || "";
  1031. objTarget = objTarget.toLowerCase()
  1032. let camLocName = roomName.toLowerCase();
  1033. let hasCurrentRoom = this.pxlEnv.roomSceneList.hasOwnProperty( this.pxlEnv.currentRoom );
  1034. if( roomEnv && !roomKeys.includes(roomName) && roomEnv.camLocation.hasOwnProperty(camLocName) ){
  1035. objTarget = camLocName;
  1036. roomName = this.pxlEnv.currentRoom;
  1037. }else{
  1038. if( hasCurrentRoom){
  1039. this.pxlEnv.roomSceneList[this.pxlEnv.currentRoom].stop();
  1040. }
  1041. roomEnv = this.pxlEnv.roomSceneList[roomName];
  1042. };
  1043. let prevRoom=this.pxlEnv.currentRoom;
  1044. let holdCamera=false;
  1045. if( hasCurrentRoom && this.pxlEnv.roomSceneList[this.pxlEnv.currentRoom].hasOwnProperty( "camHoldWarpPos" )){
  1046. holdCamera = this.pxlEnv.roomSceneList[this.pxlEnv.currentRoom].camHoldWarpPos;
  1047. }
  1048. this.pxlEnv.currentRoom=roomName;
  1049. this.pxlAutoCam.curRoom=roomName;
  1050. let isMainRoom=roomName==this.pxlEnv.mainRoom;
  1051. ./this.pxlEnv.delayPass.uniforms.roomComposer.value= isMainRoom ? 0 : 1;
  1052. if( this.pxlUser.iZoom ){
  1053. let tDiff= isMainRoom ? this.pxlEnv.roomComposer : this.pxlEnv.mapComposer;
  1054. let tDiffPrev= isMainRoom ? this.pxlEnv.mapComposer: this.pxlEnv.roomComposer;
  1055. this.pxlEnv.delayPass.uniforms.tDiffusePrev.value= tDiff.renderTarget1.texture;
  1056. this.pxlEnv.delayPass.uniforms.tDiffusePrevRoom.value= tDiffPrev.renderTarget1.texture;
  1057. setTimeout( ()=>{
  1058. if( prevRoom != roomName ){
  1059. if( isMainRoom ){
  1060. this.pxlEnv.roomComposer.reset();
  1061. }else{
  1062. this.pxlEnv.mapComposer.reset();
  1063. }
  1064. }
  1065. setTimeout( ()=>{
  1066. this.pxlEnv.mapComposerWarpPass.needsSwap=false;
  1067. },500);
  1068. },100);
  1069. }
  1070. ./this.pxlEnv.delayPass.uniforms.tDiffusePrev.value= roomName==this.pxlEnv.mainRoom ? this.pxlEnv.mapComposer.renderTarget1.texture : this.pxlEnv.roomComposer.renderTarget1.texture;
  1071. ./if(roomName!=this.pxlEnv.mainRoom || start){
  1072. if( start ){
  1073. if( !roomEnv ){
  1074. this.warn("pxlCamera.warpToRoom(); pxlRoom not found - "+this.pxlEnv.currentRoom);
  1075. return;
  1076. }
  1077. if( roomName != prevRoom ){
  1078. roomEnv.start();
  1079. }
  1080. this.pxlEnv.roomRenderPass.scene=roomEnv.scene;
  1081. let camLocName = objTarget.toLowerCase();
  1082. if( roomEnv.camLocation.hasOwnProperty( camLocName ) ){
  1083. let posName = this.cameraPosLookAtNames["default"].pos;
  1084. let lookAtName = this.cameraPosLookAtNames["default"].lookAt;
  1085. if( this.pxlDevice.mobile ){
  1086. if( roomEnv.camLocation[ camLocName ].hasOwnProperty( this.cameraPosLookAtNames["mobile"].pos ) ){
  1087. posName=this.cameraPosLookAtNames["mobile"].pos;
  1088. }
  1089. if( roomEnv.camLocation[ camLocName ].hasOwnProperty( this.cameraPosLookAtNames["mobile"].lookAt ) ){
  1090. lookAtName=this.cameraPosLookAtNames["mobile"].lookAt;
  1091. }
  1092. }
  1093. let toPos = roomEnv.camLocation[ camLocName ][ posName ];
  1094. let toLookAt = roomEnv.camLocation[ camLocName ][ lookAtName ];
  1095. this.setTransform( toPos, toLookAt );
  1096. }else if( roomEnv.camInitPos && roomEnv.camInitLookAt && ( !holdCamera || !this.pxlEnv.postIntro || this.hotKeyTriggered ) ){
  1097. this.setTransform( roomEnv.camInitPos, roomEnv.camInitLookAt );
  1098. this.hotKeyTriggered=false;
  1099. }
  1100. }else{
  1101. if( !holdCamera || !this.pxlEnv.postIntro || this.hotKeyTriggered ){
  1102. if(objTarget!=null){
  1103. this.setToObj(objTarget);
  1104. }else{
  1105. this.setTransform( roomEnv.camReturnPos, roomEnv.camReturnLookAt );
  1106. }
  1107. this.hotKeyTriggered=false;
  1108. }
  1109. }
  1110. this.pxlGuiDraws.prepArtistInfo( roomEnv.getArtistInfo() );
  1111. this.camUpdated=true;
  1112. .*this.pxlEnv.mapComposerWarpPass.enabled=!this.pxlEnv.mapComposerWarpPass.enabled;
  1113. this.pxlEnv.mapComposer.render();
  1114. this.pxlEnv.mapComposerWarpPass.enabled=!this.pxlEnv.mapComposerWarpPass.enabled;
  1115. this.pxlEnv.mapComposer.render();*.
  1116. let curFOV = roomEnv.pxlCamFOV[ this.pxlDevice.mobile ? 'MOBILE' : 'PC' ];
  1117. this.camera.fov=curFOV;
  1118. this.camera.zoom=roomEnv.pxlCamZoom;
  1119. this.camera.aspect=roomEnv.pxlCamAspect;
  1120. this.camera.near=roomEnv.pxlCamNearClipping;
  1121. this.camera.far=roomEnv.pxlCamFarClipping;
  1122. this.camera.updateProjectionMatrix();
  1123. let standingHeight=this.getUserHeight();
  1124. this.emitCameraTransforms( this.camera.position.clone(), standingHeight, true );
  1125. ./ Camera assumes the player is warping to safe ground
  1126. ./ So it will treat the position as the nearest floor hit
  1127. ./ This was causing initial jumping and movement to use the camera position as the floor
  1128. ./ This will drop the player from any height to the nearest floor using gravity when warping to a room
  1129. ./ TODO : Move this to be Room specific
  1130. if( this.canMove ){
  1131. this.colliderValid=false;
  1132. this.hasGravity=true;
  1133. }
  1134. this.pxlAutoCam.checkStatus();
  1135. }
  1136. .**
  1137. * Get a snapshot of the room to use as the diffuse texture for the warp effect.
  1138. * @private
  1139. *
  1140. *.
  1141. warpToRoomSnapshot(roomName){
  1142. this.pxlEnv.currentRoom=roomName;
  1143. let roomEnv=this.pxlEnv.roomSceneList[this.pxlEnv.currentRoom];
  1144. let curFOV = roomEnv.pxlCamFOV[ this.pxlDevice.mobile ? 'MOBILE' : 'PC' ];
  1145. this.camera.fov=curFOV;
  1146. this.camera.zoom=roomEnv.pxlCamZoom;
  1147. this.camera.aspect=roomEnv.pxlCamAspect;
  1148. this.camera.near=roomEnv.pxlCamNearClipping;
  1149. this.camera.far=roomEnv.pxlCamFarClipping;
  1150. this.camera.updateProjectionMatrix();
  1151. this.setTransform( roomEnv.camThumbPos, roomEnv.camThumbLookAt );
  1152. let standingHeight=this.getUserHeight();
  1153. this.emitCameraTransforms( this.camera.position.clone(), standingHeight, true );
  1154. }
  1155. ./ -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
  1156. ./
  1157. .**
  1158. * Initiates fast travel to a specific location.
  1159. * This begins the warp effect process,
  1160. * It doesn't affect camera upon triggering, just queuing the warp event
  1161. *
  1162. * *Implementation has been disabled until further development*
  1163. *
  1164. * @param {number} [hotkey=0] - The hotkey for the fast travel location.
  1165. *
  1166. *.
  1167. fastTravel(hotkey=0){
  1168. if( this.pxlAutoCam.enabled ){
  1169. return;
  1170. }
  1171. if( this.pxlAutoCam.active || this.pxlAutoCam.autoCamActive ){
  1172. this.pxlAutoCam.preAutoCamToggle();
  1173. }
  1174. this.hotKeyTriggered=true;
  1175. if( hotkey == 0 ){ ./ Lobby
  1176. ./this.warpEventTriggered( 1, this.pxlEnv.mainRoom, 'init' );
  1177. this.warpEventTriggered( 1, this.pxlEnv.currentRoom, 'init' );
  1178. }
  1179. ./ Hotkeys are set for a specific scene, make 3d scene file dependant
  1180. return;
  1181. if( hotkey == 1 ){ ./ Canyon
  1182. ./this.warpEventTriggered( 1, this.pxlEnv.mainRoom, this.portalList['Portal_8'] );
  1183. }else if( hotkey == 2 ){ ./ Dance Hall
  1184. ./this.warpEventTriggered( 1, "ShadowPlanet", 'init' );
  1185. }else if( hotkey == 3 ){ ./ Sunflower Room
  1186. ./this.warpEventTriggered( 1, this.pxlEnv.mainRoom, this.portalList['Portal_0'] );
  1187. }
  1188. }
  1189. .///////////////////////////////////
  1190. ./ Camera Jumping Functions //
  1191. ./////////////////////////////////
  1192. .**
  1193. * Handles the camera jump key press or release.
  1194. * @param {boolean} [jumpKeyIsDown=false] - Whether the jump (Default : Space) key is pressed.
  1195. * @example
  1196. * ./ Handle the camera jump key press or release.
  1197. * ./ Player has pressed the jump key
  1198. * this.pxlCamera.camJumpKey( true );
  1199. *
  1200. * ./ Player has released the jump key
  1201. * this.pxlCamera.camJumpKey( false );
  1202. *.
  1203. camJumpKey( jumpKeyIsDown=false ){
  1204. if( jumpKeyIsDown ){ ./ Space is down
  1205. this.camInitJump();
  1206. }else{ ./ Space is up
  1207. if(this.cameraJumpActive){ ./ Space
  1208. this.cameraJumpActive=false;
  1209. }
  1210. this.cameraAllowJump=true;
  1211. this.hasJumpLock=false; ./ Prevent repeated jumping if Space held down after landing
  1212. }
  1213. }
  1214. .**
  1215. * Initializes the jump values for the camera.
  1216. * @private
  1217. *.
  1218. camInitJump(){
  1219. ./ Link static camera to prevent jumping as well
  1220. if( !this.canMove ){return;}
  1221. if( !this.hasGravity && this.cameraAllowJump ){
  1222. this.pxlDevice.keyDownCount[2]=this.pxlTimer.prevMS;
  1223. this.cameraAllowJump=false; ./ Prevent jump spamming up stacked colliders; this may be desired for ladders
  1224. this.cameraJumpActive=true;
  1225. this.cameraJumpInAir=true;
  1226. this.hasGravity=true;
  1227. this.gravityRate=0;
  1228. this.cameraJumpVelocity=this.cameraJumpImpulse[this.pxlUser.lowGrav] * this.userScale;
  1229. if( this.hasJumpLock ){
  1230. this.hasJumpLock=false;
  1231. this.nearestFloorHit=this.nearestFloorHitPrev;
  1232. }
  1233. }
  1234. }
  1235. .**
  1236. * Handles the camera jump step.
  1237. * Step the jump while impulse isn't 0.
  1238. * @param {number} curTime - The current time.
  1239. * @private
  1240. *.
  1241. camJump(curTime){
  1242. let timeDelta= (curTime-this.pxlDevice.keyDownCount[2]) ;
  1243. let fpsRateAdjust=1;./Math.min(1, 1/(20*this.pxlTimer.msRunner.y));
  1244. ./ let jumpPerc=Math.min(1, timeDelta/(this.cameraMaxJumpHold[this.pxlUser.lowGrav]*fpsRateAdjust) );
  1245. let jumpPerc=Math.min(1, timeDelta . (this.cameraMaxJumpHold[this.pxlUser.lowGrav] ) ) ;
  1246. if(this.cameraJumpActive){
  1247. let jumpRate=jumpPerc ;
  1248. if(jumpRate==1){
  1249. this.cameraJumpActive=false;
  1250. }else{
  1251. jumpRate=(1-jumpRate)*(1-jumpRate);
  1252. jumpRate=jumpRate* ( jumpRate*.5+.5);
  1253. }
  1254. this.cameraJumpVelocity+=Math.max(0, jumpRate) * this.cameraJumpImpulse[this.pxlUser.lowGrav] * this.jumpScalar * this.pxlTimer.deltaTime;
  1255. }
  1256. this.cameraJumpVelocity*=(1-jumpPerc);./*.5+.5;
  1257. if( jumpPerc==1 ){
  1258. this.cameraJumpActive=false;
  1259. }
  1260. }
  1261. .**
  1262. * Kills the jump impulse.
  1263. * Space released before max jump
  1264. * @example
  1265. * ./ Kill the jump impulse.
  1266. * this.pxlCamera.killJumpImpulse();
  1267. *.
  1268. killJumpImpulse( scalar=1){
  1269. let toImpulse=this.cameraJumpVelocity * scalar * this.cameraJumpVelocityEaseOut;
  1270. this.cameraJumpVelocity= toImpulse>.1 ? toImpulse : 0;
  1271. this.workerFunc( "killJumpImpulse" );
  1272. }
  1273. .////////////////////////
  1274. ./ Gravity Functions //
  1275. .//////////////////////
  1276. .**
  1277. * Updates the gravity for the camera.
  1278. * Gravity is updated and offset landing height with an ease back to standing upright
  1279. * Ran internally during the main loop, but revealed for external access
  1280. * @param {number} deltaTime - The time since the last frame.
  1281. * @example
  1282. * ./ Update the gravity for the camera.
  1283. * this.pxlCamera.updateGravity( this.pxlTimer.deltaTime );
  1284. *.
  1285. updateGravity( deltaTime ){
  1286. if( this.runMain ){
  1287. this.gravityRate = Math.max(0, this.gravityRate-this.cameraJumpVelocity );
  1288. let gravityRate = this.gravityUPS[ this.pxlUser.lowGrav ];
  1289. if( this.hasGravity ){
  1290. this.gravityCount += (gravityRate * 2.5) * deltaTime;
  1291. ./this.gravityRate = Math.min( 1, ((this.gravityRate+this.gravityMax)*deltaTime)) * gravityRate;
  1292. this.gravityRate = Math.min( 1, ((this.gravityRate+this.gravityMax))) * this.gravityCount;
  1293. ./this.gravityRate += this.gravityRate * this.gravityCount;
  1294. }
  1295. if( this.gravityRate != 0 ){
  1296. ./ gMult not used, testing for need
  1297. let gMult=1;
  1298. if( !this.hasGravity ){
  1299. this.gravityRate=this.gravityRate>.01 ? this.gravityRate*this.gravityEaseOutRate*gravityRate * deltaTime : 0;
  1300. gMult= this.gravityRate;
  1301. }else{
  1302. ./gMult=this.gravityRate*.08*gravityRate;
  1303. gMult=this.gravityRate;
  1304. }
  1305. gMult=Math.min(1, gMult);
  1306. this.standingHeightGravInfluence = Math.min(1, this.gravityRate . this.gravityMax ) * this.standingMaxGravityOffset;
  1307. ./this.standingHeightGravInfluence = Math.min(1, gMult / this.gravityMax ) * this.standingMaxGravityOffset;
  1308. ./this.standingHeightGravInfluence = Math.min(this.gravityMax, this.gravityRate * deltaTime ) * this.standingMaxGravityOffset;
  1309. ./this.standingHeightGravInfluence = Math.min(this.gravityMax, this.gravityRate * deltaTime ) * this.standingMaxGravityOffset;
  1310. }
  1311. }
  1312. }
  1313. .**
  1314. * Resets the gravity for the camera.
  1315. * Ran during Jump Landing, Room & Portal Warps currently
  1316. * @example
  1317. * ./ Reset the gravity for the camera.
  1318. * this.pxlCamera.resetGravity();
  1319. *.
  1320. resetGravity(){
  1321. this.gravityCount=0;
  1322. this.gravityRate=0;
  1323. this.workerFunc( "resetGravity" );
  1324. this.jumpLanding( false ); ./ resetGravity runs jumpLanding on Worker
  1325. }
  1326. .**
  1327. * Handles the jump landing, stopping the camera jump.
  1328. * @param {boolean} [send=true] - Whether to send the landing event.
  1329. * @example
  1330. * ./ Force the camera to "land" from a jump
  1331. * ./ Player will still be in the air, but gravity will be active
  1332. * this.pxlCamera.jumpLanding();
  1333. *.
  1334. jumpLanding( send=true ){
  1335. ./ Is space held down?
  1336. ./ Trigger short delay before triggering a jump again
  1337. if( this.cameraJumpActive ){
  1338. this.hasJumpLock=true;
  1339. this.releaseJumpLockTime = this.pxlTimer.runtime + this.releaseJumpLockDelay;
  1340. }
  1341. this.gravityCount=0;
  1342. this.hasGravity=false; ./ Should probably name it cameraInAir
  1343. this.cameraJumpVelocity=0;
  1344. this.cameraJumpInAir=false; ./ hasGravity should indicate this value; residual from logic rework
  1345. this.cameraJumpActive=false; ./ Stop running camJump function
  1346. ./ Leave outside if for external access
  1347. if( send ){
  1348. this.workerFunc( "jumpLanding" );
  1349. }
  1350. }
  1351. ./////////////////////////////////////////////
  1352. ./ Collision Objects and Ground Functions //
  1353. .///////////////////////////////////////////
  1354. .**
  1355. * Checks for main collider interactions.
  1356. * Ground plane, obstacles, and no terrain (If there are colliders in scene)
  1357. * @param {Vector3} curCamPos - The current camera position.
  1358. * @returns {Vector3} - The updated camera position.
  1359. * @example
  1360. * ./ Check for main collider interactions.
  1361. * let curPos=this.pxlCamera.colliderCheck( this.camera.position );
  1362. * ./ Update the camera position.
  1363. * this.setTransform( curPos );
  1364. *.
  1365. ./ TODO : gravitySource should probably originate from Room's object list, but for now...
  1366. ./ TODO : Collision check shouldn't run if no cam movement length aside from gravity, store floor name and collision position from prior run
  1367. colliderCheck( curCamPos ){
  1368. ./ Check if camera is in Roam or Static mode
  1369. if( !this.canMove ){
  1370. return curCamPos;
  1371. }
  1372. ./ Floor Collider
  1373. ./ geoList["floorCollider"] Collision Detection
  1374. let objHit=null;
  1375. this.movementBlocked=false;
  1376. if( (this.cameraMoveLength>0 || this.colliderPrevObjHit==null || this.nearestFloorObjName==null) &&
  1377. this.cameraBooted && this.pxlEnv.roomSceneList[this.pxlEnv.currentRoom].collidersExist
  1378. ){
  1379. this.colliderValidityChecked=true; ./ Prevent doublechecking object validity post collision detection
  1380. let curRoomObj = this.pxlEnv.roomSceneList[ this.pxlEnv.currentRoom ];
  1381. let castDir=new Vector3(0,-1,0);
  1382. let castPos=curCamPos.clone();./.add(new Vector3(0,100,0));
  1383. let castHeight= 150*this.maxStepHeight ;
  1384. castPos.y += castHeight + this.maxStepHeight;
  1385. let resetKeyDown=false;
  1386. var rayHits=[];
  1387. let curQuadrant= ( ~~(castPos.x>0)+"" ) + ( ~~(castPos.z>0)+"" );
  1388. if( curRoomObj.hasColliderType( COLLIDER_TYPE.WALL ) ){
  1389. rayHits = this.pxlColliders.castRay( this.pxlEnv.currentRoom, castPos, castDir, COLLIDER_TYPE.WALL );
  1390. }
  1391. if(rayHits.length > 0){ ./ Hit a wall, check if you are standing on the wall
  1392. ./ this.floorColliderInitialHit=true;
  1393. curRoomObj.hitColliders( rayHits, COLLIDER_TYPE.WALL );
  1394. if(curRoomObj.hasColliderType( COLLIDER_TYPE.WALL_TOP )){
  1395. let rayTopHits=this.pxlColliders.castRay( this.pxlEnv.currentRoom, castPos, castDir, COLLIDER_TYPE.WALL_TOP );
  1396. if( rayTopHits.length > 0 ){
  1397. curRoomObj.hitColliders( rayTopHits, COLLIDER_TYPE.WALL_TOP );
  1398. }
  1399. let closestDist=-99999;
  1400. let yPrevRef=curCamPos.y;
  1401. let curName;
  1402. let curCollisionPos=this.nearestFloorHit;
  1403. let validHitCheck=false;
  1404. for(var x=0; x<rayTopHits.length;++x){
  1405. var obj=rayTopHits[x];
  1406. curName=obj.object.name;
  1407. let curHit=obj.pos;
  1408. let curDist=obj.dist;
  1409. let camDist=curHit.y;./- curCamPos.y; // ## Why??
  1410. let validHit=camDist < this.maxStepHeight;
  1411. validHitCheck = validHitCheck ? validHitCheck : validHit;
  1412. if( (curDist<closestDist && valid) || objHit==null){
  1413. objHit=curName;
  1414. closestDist=curDist;
  1415. curCollisionPos=curHit;
  1416. }
  1417. }
  1418. ./ Check if camera is on top of wall
  1419. let pullBack;
  1420. if( !validHitCheck || ((curCamPos.y) < curCollisionPos.y && (this.nearestFloorHitPrev.y-curCollisionPos.y > (this.maxStepHeight+this.getStandingHeight()) && !this.hasGravity) && ( (curCamPos.y+this.maxStepHeight+this.getStandingHeight()) < curCollisionPos.y && this.hasGravity) ) ){
  1421. ./objHit=this.nearestFloorObjName;
  1422. if(this.cameraMovement[0] != 0 || this.cameraMovement[1] != 0 ){
  1423. validHitCheck=true;
  1424. this.hasGravity=false;
  1425. this.hasJumpLock=true;
  1426. }
  1427. pullBack=this.cameraPos.clone();
  1428. pullBack.y=Math.min(curCamPos.y,pullBack.y);./+this.cameraJumpVelocity;
  1429. curCamPos=pullBack;
  1430. curCollisionPos=curCamPos;
  1431. if(this.hasGravity){
  1432. curCollisionPos.y=this.nearestFloorHitPrev.y;
  1433. }else{
  1434. curCollisionPos.y=this.cameraPos.y;
  1435. }
  1436. this.cameraJumpActive=false;
  1437. this.cameraAllowJump=true;
  1438. this.cameraJumpInAir=false;
  1439. this.cameraMovement[0] = 0;
  1440. this.cameraMovement[1] = 0;
  1441. this.pxlDevice.touchMouseData.curFadeOut.multiplyScalar(0);
  1442. this.pxlDevice.touchMouseData.velocity.multiplyScalar(0);
  1443. ./this.pxlDevice.touchMouseData.velocityEase.multiplyScalar(0);
  1444. }
  1445. ./this.colliderValid=validHitCheck;
  1446. if( validHitCheck ){
  1447. if( objHit == null ){
  1448. this.nearestFloorHit = this.nearestFloorHitPrev;
  1449. this.nearestFloorObjName = this.nearestFloorObjNamePrev;
  1450. if( Math.abs(curCamPos.y-this.nearestFloorHit.y) > (this.maxStepHeight+this.getStandingHeight()) ){
  1451. this.colliderValid = false;
  1452. this.hasGravity = true;
  1453. }
  1454. }else{
  1455. this.nearestFloorHitPrev = this.nearestFloorHit;
  1456. this.nearestFloorObjNamePrev = this.nearestFloorObjName;
  1457. this.nearestFloorHit = curCollisionPos;
  1458. this.nearestFloorObjName = objHit;
  1459. }
  1460. }
  1461. }else{
  1462. this.colliderFail=true;
  1463. this.movementBlocked=true;
  1464. }
  1465. }else{
  1466. ./ ## Find orientation to gravitational source if any exist in Room Environment
  1467. let stepUpDist=this.maxStepHeight;./+this.cameraJumpVelocity;
  1468. let validDistRange=stepUpDist+this.getStandingHeight();
  1469. ./castPos.y=curCamPos.y+stepUpDist;
  1470. castPos=curCamPos.clone(); ./.add(new Vector3(0,100,0));
  1471. castPos.y += castHeight + this.maxStepHeight;
  1472. if( this.pxlEnv.roomSceneList[this.pxlEnv.currentRoom].hasColliders() ){
  1473. rayHits=this.pxlColliders.castRay( this.pxlEnv.currentRoom, castPos, castDir, COLLIDER_TYPE.FLOOR );
  1474. }else{
  1475. return curCamPos; ./ No colliders
  1476. }
  1477. if(rayHits.length > 0){
  1478. if(rayHits.length > 1){
  1479. let firstDist = Math.abs( rayHits[0].pos.y-curCamPos.y );
  1480. if( firstDist<stepUpDist ){
  1481. rayHits = [ rayHits[0] ];
  1482. }else if( firstDist>stepUpDist && rayHits[0].pos.y > rayHits[1].pos.y && rayHits[1].pos.y > curCamPos.y-this.maxStepHeight ){
  1483. return curCamPos;
  1484. }
  1485. }
  1486. this.floorColliderInitialHit=true;
  1487. let closestDist=-99999;
  1488. let curName;
  1489. let curCollisionPos=this.nearestFloorHit;
  1490. for(let x=0; x<rayHits.length;++x){
  1491. let obj=rayHits[x];
  1492. let curHit=obj.pos;
  1493. ./let curHit=castPos.y-obj.dist;
  1494. ./let curDist=obj.dist;
  1495. let validHit=false;
  1496. curName=obj.object.name;
  1497. let camDist=curHit.distanceTo( curCamPos );
  1498. validHit=camDist<this.maxStepHeight;
  1499. ./console.log(stepUpDist,camDist, obj.dist, validHit);
  1500. if( ( this.portalList[curName] || this.roomWarpZone.includes(curName) ) && validHit){
  1501. objHit=curName;
  1502. curCollisionPos=curHit;
  1503. break;
  1504. }else if( !this.itemCheck(curName, validHit) ){
  1505. if(camDist<closestDist || objHit==null){
  1506. objHit=curName;
  1507. closestDist=camDist;
  1508. curCollisionPos=curHit;
  1509. }
  1510. }
  1511. }
  1512. if( this.nearestFloorObjName==null && objHit!=null){
  1513. this.nearestFloorHitPrev=curCollisionPos;
  1514. this.nearestFloorObjNamePrev=objHit;
  1515. this.nearestFloorHit=curCollisionPos;
  1516. this.nearestFloorObjName=objHit;
  1517. }
  1518. ./console.log(this.nearestFloorHitPrev.y-curCollisionPos.y, this.maxStepHeight+this.getStandingHeight() );
  1519. if( (this.nearestFloorHitPrev.y-curCollisionPos.y) > (this.maxStepHeight+this.getStandingHeight()) && !this.hasGravity){
  1520. this.nearestFloorHit=this.nearestFloorHitPrev;
  1521. this.nearestFloorObjName=this.nearestFloorObjNamePrev;
  1522. ./this.cameraMovement[0] = Math.abs(this.cameraMovement[0])<this.posRotEasingThreshold ? 0 : this.cameraMovement[0]*this.cameraMovementEase;
  1523. ./this.cameraMovement[1] = Math.abs(this.cameraMovement[1])<this.posRotEasingThreshold ? 0 : this.cameraMovement[1]*this.cameraMovementEase;
  1524. if( !objHit ){
  1525. curCamPos=this.cameraPrevPos.clone();
  1526. }else{
  1527. curCamPos=this.cameraPos.clone();
  1528. }
  1529. objHit=this.nearestFloorObjName;
  1530. this.cameraMovement[0] = 0;
  1531. this.cameraMovement[1] = 0;
  1532. this.pxlDevice.touchMouseData.curFadeOut.multiplyScalar(0);
  1533. this.pxlDevice.touchMouseData.velocity.multiplyScalar(0);
  1534. ./this.pxlDevice.touchMouseData.velocityEase.multiplyScalar(0);
  1535. }else{ ./ Player is jumping or falling
  1536. this.nearestFloorHitPrev=this.nearestFloorHit;
  1537. this.nearestFloorObjNamePrev=this.nearestFloorObjName;
  1538. this.nearestFloorHit=curCollisionPos;
  1539. this.nearestFloorObjName=objHit;
  1540. if( objHit == null ){
  1541. this.colliderValid=false;
  1542. this.hasGravity=true;
  1543. }
  1544. }
  1545. }else{
  1546. ./ If this runs, means the user ran off the edge of the collider and there is no floor below them.
  1547. this.colliderFail=true;
  1548. this.movementBlocked=true;
  1549. this.colliderValidityChecked=false;
  1550. curCamPos=this.cameraPos.clone();
  1551. }
  1552. }
  1553. }else{
  1554. ./ User didn't move, may be falling from gravity
  1555. ./ Call valid object check within distance from camera location
  1556. this.colliderValidityChecked=false;
  1557. }
  1558. this.colliderValidityChecked=false;
  1559. return curCamPos;
  1560. }
  1561. .//////////////////////////////
  1562. ./ Collider Event Triggers //
  1563. .////////////////////////////
  1564. ./ Should only run when this.colliderValid==false
  1565. ./ ##
  1566. .**
  1567. * Checks if the collider is valid.
  1568. * For GravitySource, make sure to get vector delta magnitude, not just Y delta
  1569. * @param {Vector3} curCamPos - The current camera position.
  1570. * @returns {number} - The distance to the collider.
  1571. * @example
  1572. * ./ Check if the collider is valid, within the max step height.
  1573. * let dist=this.pxlCamera.checkColliderValid( this.camera.position );
  1574. * ./ Update the camera position.
  1575. *.
  1576. checkColliderValid( curCamPos ){
  1577. this.colliderValidityChecked=true;
  1578. let validDist=this.maxStepHeight+this.gravityRate;
  1579. let curDist = curCamPos.distanceTo( this.nearestFloorHit );
  1580. let checkValid= curDist < validDist;./ && (curCamPos.y+validDist)<this.nearestFloorHit.y;
  1581. this.colliderValid=checkValid;
  1582. return curDist;
  1583. ./return checkValid; // Currently not needed, pre-emptive
  1584. }
  1585. .**
  1586. * Triggers an event based on the collider object.
  1587. * Currently supports inta-room portals, extra-room warp zones, and only 2 audio triggers.
  1588. * Custom triggers haven't been implemented yet,
  1589. * For any custom Collider Events, use the `castray()`
  1590. * @param {string} [checkObject=null] - The collider object to check.
  1591. * @returns {boolean} - Whether an event was triggered.
  1592. * @example
  1593. * ./ Trigger an event based on the collider object.
  1594. * this.pxlCamera.eventTrigger( "AudioTrigger_1" );
  1595. * ./ Update the camera position.
  1596. * this.setTransform( curPos );
  1597. *.
  1598. eventTrigger( checkObject=null ){
  1599. ./ Check if camera is in Roam or Static mode
  1600. if( !this.canMove ){ return false; }
  1601. ./ No collider, might be in a Room Environment with no colliders
  1602. if(!checkObject){ return false; }
  1603. ./ -- Check for Portals -- //
  1604. ./ -- Within Room Environment position/rotation updates -- //
  1605. if(this.portalList[checkObject]){
  1606. this.warpEventTriggered( 0, this.portalList[checkObject]);
  1607. this.eventCheckStatus=false;
  1608. return true;
  1609. }
  1610. ./ -- Check for between Room Warp Zones -- //
  1611. ./ -- Separate ThreeJS Scene position/rotation updates -- //
  1612. if( this.roomWarpZone.includes(checkObject) ){ ./ ## Should live in the Room Environment
  1613. this.warpEventTriggered( 1, checkObject );
  1614. this.eventCheckStatus=false;
  1615. return true; ./ Room warp, kill camera calculations
  1616. }
  1617. ./ -- Check for change between different Collider objects -- //
  1618. ./ -- Changes may trigger Audio and Screen Visual Effects -- //
  1619. this.colliderShiftActive=this.colliderCurObjHit!=checkObject || this.colliderShiftActive ;
  1620. this.colliderPrevObjHit= this.colliderCurObjHit;
  1621. this.colliderCurObjHit=checkObject;
  1622. ./ If the volume adjust is mid transition, keep the fade rolling
  1623. this.colliderShiftActive=this.colliderShiftActive || !(this.colliderAdjustPerc==1 || this.colliderAdjustPerc==0);
  1624. ./ Fade Up/Down Audio/Video effects per room names
  1625. ./ ## These should be handled by the Room Environment itself
  1626. if(this.colliderShiftActive && this.colliderCurObjHit){
  1627. let volMult= 1;
  1628. let audioTriggerCur = this.colliderCurObjHit.includes("AudioTrigger")
  1629. ./let audioTriggerPrev = (this.colliderPrevObjHit || "").includes("AudioTrigger")
  1630. ./let audioTrigger= ( audioTriggerCur || audioTriggerPrev ) && ( audioTriggerCur != audioTriggerPrev);
  1631. if( audioTriggerCur ){
  1632. volMult=-1;
  1633. }
  1634. this.colliderAdjustPerc=Math.min(1, Math.max(0, this.colliderAdjustPerc + this.colliderAdjustRate * volMult ));
  1635. let curExposure=(1-this.colliderAdjustPerc);
  1636. let curExp=1.0;
  1637. ./ ## Convert to function dictionary per Room Environment's Collider[objName]
  1638. if(this.colliderCurObjHit == "AudioTrigger_1"){ ./ Sunflower Room
  1639. this.pxlEnv.currentAudioZone=1;
  1640. curExp= curExp - curExposure*this.uniformScalars.darkBase; ./ ## Don't do it this way... blend, don't add offset
  1641. this.uniformScalars.exposureUniformBase=curExp;
  1642. }else if(this.colliderCurObjHit == "AudioTrigger_2"){ ./ Lobby
  1643. this.pxlEnv.currentAudioZone=2;
  1644. let animPerc=1;
  1645. curExp= this.uniformScalars.curExp + curExposure*this.uniformScalars.brightBase*animPerc; ./ ## Don't do it this way... blend, don't add offset
  1646. this.uniformScalars.exposureUniformBase=curExp;
  1647. this.proximityScaleTrigger=true; ./ Fade Out proximity range
  1648. this.pxlAudio.setFadeActive(-1);
  1649. ./}else if(){ } // TODO : Add camera location warp pad check
  1650. }else{
  1651. this.pxlEnv.currentAudioZone=0;
  1652. curExp= curExp*(1-curExposure) + this.uniformScalars.exposureUniformBase*curExposure; ./ ## Don't do it this way... blend, don't add offset
  1653. }
  1654. ./ Transition has completed when True
  1655. this.colliderShiftActive=!(this.colliderAdjustPerc==1 || this.colliderAdjustPerc==0);
  1656. ./ If Lobby geomtry is visible, but no longer in the Lobby, toggle visiblity
  1657. ./ Runs once, at the moment of collider change
  1658. if( this.colliderPrevObjHit=="AudioTrigger_2" && this.colliderCurObjHit!=this.colliderPrevObjHit){
  1659. this.proximityScaleTrigger=true; ./ Fade In proximity range
  1660. this.pxlAudio.setFadeActive(1);
  1661. }
  1662. if( this.pxlDevice.mobile ){
  1663. curExp=this.colliderAdjustPerc;
  1664. }
  1665. ./ Set scene exposure on post-process composer passes
  1666. this.pxlEnv.updateCompUniforms(curExp);
  1667. ./ Scale proximity visual
  1668. if(this.proximityScaleTrigger && !this.pxlDevice.mobile && !this.pxlAutoCam.enabled ){
  1669. let proxMult=this.colliderAdjustPerc;
  1670. proxMult=1-(1-proxMult)*(1-proxMult);
  1671. this.pxlEnv.fogMult.x = proxMult;
  1672. if( !this.colliderShiftActive ){
  1673. this.proximityScaleTrigger=false;
  1674. }
  1675. }
  1676. this.eventCheckStatus=this.colliderShiftActive;
  1677. }
  1678. }
  1679. .**
  1680. * Checks if an item was triggered.
  1681. * Currently only used as - if( !this.itemCheck(validHit) ){}
  1682. * Preventing standing on the item collider plane
  1683. * @param {string} curNameBase - The base name of the item.
  1684. * @param {boolean} validHit - Whether the hit was valid.
  1685. * @returns {boolean} - Whether the item was triggered.
  1686. * @private
  1687. *.
  1688. itemCheck(curNameBase, validHit){
  1689. if(!validHit){ return false; }
  1690. let curName=curNameBase.split("_").shift();
  1691. if(this.pxlUser.itemListNames.includes(curNameBase)){
  1692. let itemPickup=this.pxlUser.checkItemPickUp(curName);
  1693. if(itemPickup){
  1694. return this.itemActive( curName, curNameBase );
  1695. }
  1696. }
  1697. return false; ./ Allowed to stand on object
  1698. }
  1699. .**
  1700. * Triggers a newly picked-up item.
  1701. * If no item is picked up, a random overlay-effect item is selected.
  1702. *.
  1703. itemTrigger(){
  1704. if( this.pxlUser.itemActiveTimer.length>0 ){
  1705. this.pxlUser.itemActiveTimer[0]=this.pxlTimer.curMS;
  1706. }else{
  1707. if( this.pxlUser.mPick.length==0){
  1708. this.pxlUser.mPick=this.pxlUtils.randomizeArray( ['LizardKing', 'StarField', 'InfinityZoom'] );
  1709. }
  1710. ./this.pxlUser.mPick="LizardKing";
  1711. let setItem= this.pxlUser.mPick.pop();
  1712. this.pxlUser.checkItemPickUp(setItem);
  1713. this.itemActive( setItem );
  1714. }
  1715. }
  1716. .**
  1717. * Activates an item.
  1718. * @param {string} [curName=null] - The name of the item.
  1719. * @param {string} [curNameBase=null] - The base name of the item.
  1720. * @returns {boolean} - Whether the item was activated.
  1721. *.
  1722. itemActive( curName=null, curNameBase=null ){
  1723. if( curName==null ){
  1724. return false;
  1725. }
  1726. let timer=this.pxlTimer.prevMS+this.pxlUser.itemRunTime;
  1727. let finCmd="";
  1728. let text="";
  1729. if(curName=="LowGravity"){
  1730. text="Low Gravity";
  1731. finCmd="this.lowGrav=0;this.itemGroupList['"+curNameBase+"'].visible=true;";
  1732. timer=this.pxlTimer.prevMS+this.pxlUser.itemRunTime;
  1733. }else if(curName=="LizardKing"){
  1734. text="I am the Lizard King";
  1735. finCmd="this.lKing=0;this.lKingWarp.set(...this.lKingInactive);this.lizardKingPass.enabled=false;"+(!this.pxlDevice.mobile && "this.itemGroupList['"+curNameBase+"'].visible=true;");
  1736. timer=this.pxlTimer.prevMS+this.pxlUser.itemRunTime;
  1737. }else if(curName=="StarField"){
  1738. text="Major Tom";
  1739. finCmd="this.sField=0;this.starFieldPass.enabled=false;"+(!this.pxlDevice.mobile && "this.itemGroupList['"+curNameBase+"'].visible=true;");
  1740. timer=this.pxlTimer.prevMS+this.pxlUser.itemRunTime;
  1741. }else if(curName=="InfinityZoom"){
  1742. text="Fractal Substrate";
  1743. finCmd="this.iZoom=0;this.crystallinePass.enabled=false;"+(!this.pxlDevice.mobile && "this.itemGroupList['"+curNameBase+"'].visible=true;this.pxlEnv.mapComposerWarpPass.needsSwap=true;this.pxlEnv.mapComposerWarpPass.enabled=false;");
  1744. timer=this.pxlTimer.prevMS+this.pxlUser.itemRunTime;
  1745. ./this.pxlEnv.mapComposerWarpPass.needsSwap=false;
  1746. this.pxlEnv.mapComposerWarpPass.needsSwap=true;
  1747. setTimeout(()=>{
  1748. .*if( this.pxlEnv.currentRoom==this.pxlEnv.mainRoom ){
  1749. this.pxlEnv.roomComposer.reset();
  1750. this.pxlEnv.mapComposer.render();
  1751. }else{
  1752. this.pxlEnv.mapComposer.reset();
  1753. this.pxlEnv.roomComposer.render();
  1754. }*.
  1755. this.pxlEnv.mapComposer.render();
  1756. this.pxlEnv.roomComposer.render();
  1757. setTimeout(()=>{
  1758. this.pxlEnv.mapComposerWarpPass.needsSwap=false;
  1759. this.pxlEnv.mapComposerWarpPass.enabled=true;
  1760. .*if( this.pxlEnv.currentRoom==this.pxlEnv.mainRoom ){
  1761. this.pxlEnv.roomComposer.reset();
  1762. }else{
  1763. this.pxlEnv.mapComposer.reset();
  1764. }*.
  1765. },500);
  1766. },500);
  1767. .*this.pxlEnv.mapComposer.render();
  1768. this.pxlEnv.mapComposerWarpPass.enabled=!this.pxlEnv.mapComposerWarpPass.enabled;
  1769. this.pxlEnv.mapComposer.render();*.
  1770. ./this.pxlEnv.roomComposer.render();
  1771. ./this.pxlEnv.mapComposer.render();
  1772. }else{
  1773. return false;
  1774. }
  1775. this.pxlGuiDraws.buildItemHud(curName,text);
  1776. if( !this.pxlDevice.mobile ){
  1777. this.pxlUser.itemGroupList[curNameBase].visible=false;
  1778. }
  1779. this.pxlUser.itemInactiveCmd.push( finCmd );
  1780. this.pxlUser.itemActiveTimer.push(timer);
  1781. this.pxlUser.itemActiveList.push(text);
  1782. return true; ./ Don't stand upon item collision object
  1783. }
  1784. .///////////////////////////////////
  1785. ./ Camera Positional Functions //
  1786. ./////////////////////////////////
  1787. ./ Flip between free roaming or static camera
  1788. ./ Run this through pxlNav's trigger system, trigger --
  1789. ./ pxlNav.trigger("Camera","Roam")
  1790. ./ -or-
  1791. ./ pxlNav.trigger("Camera","Static")
  1792. .**
  1793. * Toggles the camera movement.
  1794. * @param {boolean} [canMoveVal=null] - Whether the camera can move.
  1795. * @example
  1796. * ./ Toggle the camera to `Static` mode.
  1797. * this.pxlCamera.toggleMovement( false );
  1798. *
  1799. * ./ Toggle the camera to `Roam` mode.
  1800. * this.pxlCamera.toggleMovement( true );
  1801. *.
  1802. toggleMovement( canMoveVal=null ){
  1803. if(canMoveVal == null){
  1804. canMoveVal=!this.canMove;
  1805. }
  1806. this.canMove = canMoveVal;
  1807. this.hasGravity = canMoveVal;
  1808. }
  1809. .**
  1810. * Updates the movement based on the current time.
  1811. * Appling down directional key values to camera movement array
  1812. *
  1813. * Ran internally during the main loop, but revealed for external access
  1814. *
  1815. * @param {number} curTime - The current time.
  1816. * @example
  1817. * ./ Update the movement based on the current time.
  1818. * this.pxlCamera.updateMovement( this.pxlTimer.deltaTime );
  1819. *
  1820. * ./ Log the current camera movement direction in X and Z.
  1821. * console.log( this.pxlCamera.cameraMovement );
  1822. *.
  1823. updateMovement(curTime){
  1824. ./ Check if camera is in Roam or Static mode
  1825. if( !this.canMove ){ return; }
  1826. if( this.pxlDevice.mobile ){
  1827. return;
  1828. }
  1829. let rate=[0,0];./
  1830. let dir=[...this.pxlDevice.directionKeysPressed];
  1831. let strafe=0;
  1832. let dolly=0;
  1833. ./ Get millisecond time differences so camera movement is independant of FPS
  1834. let deltas=[ (curTime-this.pxlDevice.keyDownCount[0]), (curTime-this.pxlDevice.keyDownCount[1]) ]; ./ 1.000 seconds
  1835. ./console.log( dir );
  1836. ./ Array entry
  1837. let easingMode = this.mobile ? 1 : 0;
  1838. ./ Check if either Left or Right direction keys are pressed
  1839. ./ Default: rate[0] is strafing movement
  1840. ./ If tank controls are enabled, rate[0] drives rotation rate
  1841. ./ this.pxlQuality.settings.leftRight == False : Tank Controls; True : Strafing
  1842. ./ TODO : Yeah, I know. There is a TODO in QualityController.js to decouple movement settings
  1843. if((dir[0]+dir[2])==1){
  1844. strafe=dir[2]-dir[0];
  1845. ./ Subtract forward/back from strafing movement to reduce diagonal super-speed
  1846. let turnRate=this.pxlQuality.settings.leftRight ? this.cameraEasing[ easingMode ] : ( 1 - Math.min(1, Math.abs(this.cameraMovement[1]*.3)) ) *.5 ;
  1847. rate[0]=( (this.pxlQuality.settings.leftRight ? 1.0 : 6.0) + (deltas[0]*(deltas[0])) * .1 ) * turnRate;
  1848. rate[0]= Math.min( this.pxlUser.moveSpeed, rate[0] ) * this.movementScalar * this.userInputMoveScalar.x;
  1849. }else{
  1850. ./ Bother Left AND Right direction keys are pressed, cancel movement
  1851. this.pxlDevice.keyDownCount[0]=curTime;
  1852. }
  1853. ./ Check if either Up or Down direction keys are pressed
  1854. if((dir[1]+dir[3])==1){
  1855. dolly=dir[3]-dir[1];
  1856. ./ Subtract strafing movement from dolly movement to reduce diagonal super-speed
  1857. let dollyRate=(1- Math.min(1, Math.abs(this.cameraMovement[0]*.07))) * this.cameraEasing[ easingMode ];
  1858. rate[1]=( ((deltas[1]*(deltas[1]*3+2+this.pxlUser.moveSpeed))*.5) ) * dollyRate;
  1859. rate[1]= Math.min( this.pxlUser.moveSpeed, rate[1] ) * this.movementScalar * this.userInputMoveScalar.y;
  1860. }else{
  1861. ./ Both Up AND Down direction keys are pressed, cancel movement
  1862. this.pxlDevice.keyDownCount[1]=curTime;
  1863. }
  1864. let moveSpeed = ( rate[0]**2 + rate[1]**2 ) ** 0.5;
  1865. this.hasMovementLimit=true;
  1866. this.movementMax = 10.0; ./ Meters per second
  1867. this.cameraMovement[0]+=strafe*rate[0];
  1868. this.cameraMovement[1]+=dolly*rate[1];
  1869. }
  1870. .**
  1871. * Initializes the starting camera position per-frame.
  1872. * This is ran in `updateCamera()`
  1873. * @returns {Vector3} - The initial camera position.
  1874. * @private
  1875. *.
  1876. initFrameCamPosition(){
  1877. let curCamPos=this.cameraPos.clone();
  1878. if(!this.cameraBooted){ ./ These should be set from Scene File, if not, initial values
  1879. this.cameraAimTarget.position.set(0, 0, 0);./.add(new Vector3(0,0,0));
  1880. this.cameraPrevPos = new Vector3(curCamPos.clone());
  1881. this.cameraPrevLookAt = new Vector3(0,0,1);
  1882. this.hasMoved = true;
  1883. this.hasRotated = true;
  1884. }else{
  1885. let userMovement;
  1886. .*if(this.pxlDevice.mobile){ // ## When Mobile is implemented, convert to this.cameraMovement
  1887. userMovement=new Vector3(-this.pxlDevice.touchMouseData.curDistance.x*.01,0,-this.pxlDevice.touchMouseData.curDistance.y*.01);
  1888. this.cameraMoveLength=userMovement.length();
  1889. }else{*.
  1890. ./userMovement=new Vector3(this.cameraMovement[0],0,this.cameraMovement[1]);
  1891. userMovement=new Vector3((this.pxlQuality.settings.leftRight?this.cameraMovement[0]*.5:0),0,this.cameraMovement[1]);
  1892. this.cameraMoveLength=userMovement.length();
  1893. ./}
  1894. userMovement.applyQuaternion(this.camera.quaternion);
  1895. let moveScalar = this.cameraMoveLength*this.cameraMoveLengthMult;
  1896. ./ Give some base movement to the camera
  1897. ./ This way it doesn't ramp up from 0, but from the minimum movement speed
  1898. if( moveScalar!=0 ){
  1899. let minimumMoveSpeed=0.1;
  1900. moveScalar = moveScalar>0 ? Math.max(minimumMoveSpeed,moveScalar) : Math.min(-minimumMoveSpeed,moveScalar);
  1901. userMovement.normalize().multiply(new Vector3(1,0,1)).multiplyScalar(moveScalar);
  1902. curCamPos.add(userMovement);
  1903. this.cameraMovement[0] = Math.abs(this.cameraMovement[0])<this.posRotEasingThreshold ? 0 : this.cameraMovement[0]*this.cameraMovementEase;
  1904. this.cameraMovement[1] = Math.abs(this.cameraMovement[1])<this.posRotEasingThreshold ? 0 : this.cameraMovement[1]*this.cameraMovementEase;
  1905. if( this.cameraMovement[0] == 0 ){
  1906. this.userInputMoveScalar.x=1;
  1907. }
  1908. if( this.cameraMovement[1] == 0 ){
  1909. this.userInputMoveScalar.y=1;
  1910. }
  1911. this.hasMoved=true;
  1912. }
  1913. ./curCamPos=curCamPos.clone().multiplyScalar(this.camPosBlend).add(this.cameraPrevPos.clone().multiplyScalar(1-this.camPosBlend));
  1914. ./ ## When GravitySource exists, apply cameraMovement offset
  1915. ./ Cam movement to Vector3( cm[0], 0, cm[1] ), rotated by Quaternion from Euler Normalize Vector (camPos - collider hit)
  1916. ./ DO NOT USE CAMERA QUATERNION, movement doesn't align to camera orientation
  1917. curCamPos.y=this.cameraPos.y + this.cameraJumpVelocity;
  1918. if( this.workerActive ){
  1919. this.cameraJumpVelocity=0; ./ Additive from the worker thread
  1920. }
  1921. }
  1922. this.cameraCross=new Vector3(1,0,0).applyQuaternion( this.camera.quaternion );
  1923. return curCamPos;
  1924. }
  1925. .**
  1926. * Updates the camera position based on gravity and collisions.
  1927. * Delta ( camPos + gravity direction * gravity rate ) > ( Distance camPos to Collider Hit )
  1928. *
  1929. * Handled internally during the main loop, but revealed for external access
  1930. *
  1931. * @param {Vector3} curCamPos - The current camera position.
  1932. * @returns {Vector3} - The updated camera position.
  1933. * @example
  1934. * ./ Update the camera position based on gravity and collisions.
  1935. * let curPos=this.pxlCamera.updateCamera( this.camera.position );
  1936. * curPos = this.pxlCamera.applyGravity( curPos );
  1937. *
  1938. * ./ Check if the collider is valid, within the max step height.
  1939. * let dist=this.pxlCamera.checkColliderValid( curPos );
  1940. *
  1941. * if( this.colliderValid ){
  1942. * ./ Update the camera position.
  1943. * this.setTransform( curPos );
  1944. * }
  1945. *.
  1946. applyGravity( curCamPos ){
  1947. if( this.canMove && this.hasGravity ){
  1948. ./curCamPos=this.checkColliderFail( curCamPos );
  1949. let validDist=this.maxStepHeight+this.gravityRate;
  1950. let jumpUpState=(curCamPos.y)<this.nearestFloorHit.y;
  1951. .*if( jumpUpState || this.colliderFail ){
  1952. ./curCamPos.x = nPrev.x;
  1953. ./curCamPos.y = Math.max( nPrev.y, curCamPos.y-this.gravityRate );
  1954. curCamPos.y = Math.max( 100, curCamPos.y-this.gravityRate );
  1955. ./curCamPos.z = nPrev.z;
  1956. if( curCamPos.y == 100 ){./nPrev.y){
  1957. let nPrev=this.nearestFloorHitPrev;
  1958. curCamPos=nPrev.clone();
  1959. this.resetGravity();
  1960. }
  1961. }*.
  1962. if( jumpUpState ){
  1963. let nPrev=this.nearestFloorHitPrev;./.nearestFloorHit;//this.nearestFloorHitPrev;
  1964. ./curCamPos.x = nPrev.x;
  1965. curCamPos.y = Math.max( nPrev.y, curCamPos.y);./-this.gravityRate );
  1966. ./curCamPos.z = nPrev.z;
  1967. if( curCamPos.y < 0 ){./nPrev.y){
  1968. curCamPos.x=nPrev.x;./clone();
  1969. curCamPos.z=nPrev.z;./clone();
  1970. ./this.resetGravity();
  1971. ./this.jumpLanding();
  1972. }
  1973. }else{
  1974. curCamPos.y = Math.max( this.nearestFloorHit.y, curCamPos.y - this.gravityRate );
  1975. if( curCamPos.y == this.nearestFloorHit.y && curCamPos.y<this.cameraPrevPos.y ){
  1976. this.jumpLanding();
  1977. }
  1978. }
  1979. }else{
  1980. let distToFloor=curCamPos.distanceTo( this.nearestFloorHit );
  1981. if( distToFloor < this.maxStepHeight){
  1982. curCamPos.y = this.nearestFloorHit.y;
  1983. }else{
  1984. curCamPos=this.cameraPos.clone();
  1985. let fallStatus=curCamPos.y > this.nearestFloorHit.y;
  1986. this.hasGravity=fallStatus;
  1987. this.hasMoved = this.hasMoved || fallStatus;
  1988. this.colliderFail= !fallStatus;
  1989. this.workerFunc("jumpLanding");
  1990. ./curCamPos=this.checkColliderFail( curCamPos );
  1991. }
  1992. }
  1993. return curCamPos;
  1994. }
  1995. .**
  1996. * Gets the standing height of the user.
  1997. * Head to Foot only - No landing, gravity, or walk-bounce
  1998. * @returns {number} - The standing height.
  1999. * @example
  2000. * ./ Get the standing height of the user.
  2001. * let standingHeight=this.pxlCamera.getStandingHeight();
  2002. * console.log( "getStandingHeight()", standingHeight );
  2003. *.
  2004. getStandingHeight(){
  2005. let retHeight = this.standingHeight;
  2006. if( this.roomStandingHeight.hasOwnProperty(this.pxlEnv.currentRoom) ){
  2007. retHeight = this.roomStandingHeight[this.pxlEnv.currentRoom];
  2008. }
  2009. return retHeight * this.userScale;
  2010. }
  2011. .**
  2012. * Gets the user height including jump and walking-bounce offsets.
  2013. * @returns {number} - The user height.
  2014. * @example
  2015. * ./ Get the user height including jump and walking-bounce offsets.
  2016. * let userHeight=this.pxlCamera.getUserHeight();
  2017. * console.log( "getUserHeight()", userHeight );
  2018. *.
  2019. getUserHeight(){
  2020. ./ Add bob to movement to appear as taking steps
  2021. let walkBounceAdd=Math.min(1, Math.abs(this.cameraMovement[1]));
  2022. this.walkBouncePerc=this.walkBouncePerc>=1?1:this.walkBouncePerc + this.walkBounceEaseIn * walkBounceAdd;
  2023. this.walkBounce+=walkBounceAdd * this.walkBounceRate;
  2024. this.walkBouncePerc=this.walkBouncePerc * this.walkBounceEaseOut + walkBounceAdd;
  2025. if(this.walkBouncePerc<.03){
  2026. this.walkBouncePerc=0;
  2027. this.walkBounce=0;
  2028. this.walkBounceSeed=Math.random()*2351.3256;
  2029. }
  2030. ./let walkBounceOffset=Math.sin(this.walkBounce*.4+this.walkBounceSeed+this.cameraMovement[1]*.2)*this.walkBouncePerc*.3;
  2031. let walkBounceOffset=Math.sin(this.walkBounce+this.walkBounceSeed) * this.walkBouncePerc * this.walkBounceHeight;
  2032. let curStandingHeight=this.getStandingHeight() - this.standingHeightGravInfluence + walkBounceOffset;
  2033. return curStandingHeight;
  2034. }
  2035. .///////////////////////////////////
  2036. ./ Camera Rotational Functions //
  2037. ./////////////////////////////////
  2038. .**
  2039. * Applies mobile rotation to the camera.
  2040. * Mobile currently doesn't support movement in Rooms
  2041. *
  2042. * Currently unused; awaiting mobile-gyroscope implementation
  2043. *.
  2044. camApplyMobileRotation(){
  2045. if(this.cameraPose.alpha!=null){
  2046. let dtor=0.017453292519943278; ./ PI/180
  2047. let halfSqrt=2.23606797749979; ./ Sqrt(5)
  2048. let camPoseQuat=new Quaternion();
  2049. let a=this.cameraPose.alpha*dtor+this.cameraPose.alphaOffset+2.1;
  2050. let b=this.cameraPose.beta*dtor;
  2051. let g=this.cameraPose.gamma*dtor;
  2052. let viewNormal=new Vector3(0,0,1);
  2053. let poseQuat=new Quaternion();
  2054. let initPoseQuat=new Quaternion(-halfSqrt,0,0,halfSqrt);
  2055. let euler=new Euler();
  2056. euler.set(b,a,-g,'YXZ'); ./ Device returns YXZ for deviceOrientation
  2057. camPoseQuat.setFromEuler(euler);
  2058. camPoseQuat.multiply(initPoseQuat);
  2059. camPoseQuat.multiply(poseQuat.setFromAxisAngle(viewNormal,-this.cameraPose.orientation));
  2060. camPoseQuat.normalize();
  2061. let smoothedQuat=new Quaternion();
  2062. Quaternion.slerp(this.camera.quaternion,camPoseQuat,smoothedQuat,0.35);
  2063. let cameraLimit = new Euler().setFromQuaternion(smoothedQuat);
  2064. cameraLimit.x = Math.max(-0.95 * Math.PI . 2, Math.min(0.95 * Math.PI / 2, cameraLimit.x));
  2065. smoothedQuat.setFromEuler(cameraLimit);
  2066. this.camera.setRotationFromQuaternion(smoothedQuat);
  2067. this.hasRotated=true;
  2068. }
  2069. }
  2070. .**
  2071. * Updates the camera rotation; Look At(Aim) Target
  2072. *
  2073. * Note: Known bug, static camera rotation is based on the current camera rotation
  2074. * This causes an inate rotation when the camera is moved to a new position
  2075. *.
  2076. updateRoamRotation(){
  2077. if(this.cameraPose.alpha==null){ ./ ## Should gyro exist, don't run. But need to allow controlled look around on mobile
  2078. let xGrav=this.pxlDevice.gyroGravity[2];./*this.gravityRate;//*PI;
  2079. let viewNormal=new Vector3(0,0,1);
  2080. let poseQuat=new Quaternion();
  2081. ./ ## Theres a better place for this....
  2082. this.pxlDevice.touchMouseData.velocity.y=Math.min(this.touchSensitivityLimits, Math.max(-this.touchSensitivityLimits, this.pxlDevice.touchMouseData.velocity.y));
  2083. let euler=new Euler();
  2084. let camPoseQuat;
  2085. if( this.pxlDevice.mobile ){
  2086. let invertLookBool = this.pxlOptions.userSettings.look.mobile.invert || false;
  2087. let invertLook = invertLookBool ? -1 : 1;
  2088. euler.set(
  2089. this.pxlDevice.touchMouseData.netDistance.y.this.pxlDevice.sH*4 * invertLook,
  2090. this.pxlDevice.touchMouseData.netDistance.x.this.pxlDevice.sW*7 * invertLook + xGrav,
  2091. 0,
  2092. 'YXZ'
  2093. ); ./ Device returns YXZ for deviceOrientation
  2094. camPoseQuat=new Quaternion();
  2095. camPoseQuat.setFromEuler(euler);
  2096. camPoseQuat=this.pxlDevice.touchMouseData.initialQuat.clone().multiply(camPoseQuat);
  2097. ./camPoseQuat.multiply(poseQuat.setFromAxisAngle(viewNormal,-this.cameraPose.orientation));
  2098. }else{
  2099. let invertLookBool = this.pxlOptions.userSettings.look.pc.invert || false;
  2100. let invertLook = invertLookBool ? -1 : 1;
  2101. euler.set(
  2102. this.pxlDevice.touchMouseData.velocity.y*.005 * invertLook,
  2103. this.pxlDevice.touchMouseData.velocity.x*.008 * invertLook + xGrav,
  2104. 0,
  2105. 'YXZ'./ Device returns YXZ for deviceOrientation
  2106. );
  2107. camPoseQuat=new Quaternion();
  2108. camPoseQuat.setFromEuler(euler);
  2109. ./camPoseQuat=this.pxlDevice.touchMouseData.initialQuat.clone().multiply(camPoseQuat);
  2110. camPoseQuat=this.camera.quaternion.clone().multiply(camPoseQuat);
  2111. }
  2112. camPoseQuat.normalize();
  2113. let lookAt= new Vector3(0,0,-10).applyQuaternion( camPoseQuat ).add( this.camera.position );
  2114. this.camera.setRotationFromQuaternion(camPoseQuat);
  2115. this.camera.lookAt(lookAt);
  2116. this.camera.up.set( 0,1,0 );
  2117. this.hasRotated=true;
  2118. }
  2119. }
  2120. updateStaticRotation(){
  2121. ./ this.pxlDevice.touchMouseData.startPos;
  2122. ./ this.pxlDevice.touchMouseData.endPos;
  2123. ./ this.pxlDevice.touchMouseData.netDistance;
  2124. let blendOut=1;
  2125. if( this.touchBlender ){
  2126. ./ Camera rotation easing logic-
  2127. blendOut=Math.min(1, Math.max(0, this.pxlTimer.curMS - this.pxlDevice.touchMouseData.releaseTime ));
  2128. blendOut*=blendOut;
  2129. this.pxlDevice.touchMouseData.netDistance.multiplyScalar(1-blendOut);
  2130. this.touchBlender=blendOut<1;
  2131. }else{
  2132. this.pxlDevice.touchMouseData.netDistance.multiplyScalar(.5);
  2133. }
  2134. let euler=new Euler();
  2135. euler.set(
  2136. (this.pxlDevice.touchMouseData.netDistance.y.this.pxlDevice.sH*2),
  2137. (this.pxlDevice.touchMouseData.netDistance.x.this.pxlDevice.sW*2),
  2138. 0,
  2139. 'YXZ'
  2140. ); ./ Device returns YXZ for deviceOrientation
  2141. ./ Limit Up/Down looking
  2142. let camPoseQuat=new Quaternion().clone( this.camera.quaternion );
  2143. camPoseQuat.setFromEuler(euler);
  2144. camPoseQuat=this.camera.quaternion.clone().multiply(camPoseQuat);
  2145. ./camPoseQuat.multiply(poseQuat.setFromAxisAngle(viewNormal,-this.cameraPose.orientation));
  2146. camPoseQuat.normalize();
  2147. ./ let smoothedQuat=new Quaternion();
  2148. if( this.touchBlender ){
  2149. camPoseQuat.slerp(this.camera.quaternion.clone(),blendOut).normalize();
  2150. }
  2151. let lookAt= new Vector3(0,0,-10).applyQuaternion( camPoseQuat ).add( this.camera.position );
  2152. this.camera.setRotationFromQuaternion(camPoseQuat);./smoothedQuat);
  2153. this.camera.lookAt(lookAt);
  2154. this.camera.up.set( 0,1,0 );
  2155. this.hasRotated=true;
  2156. }
  2157. .**
  2158. * Locks the camera to look at a target.
  2159. * @example
  2160. * ./ Lock the camera to look at a target.
  2161. * let targetObj = new Object3D();
  2162. * targetObj.position.set( 10, 15, 5 );
  2163. *
  2164. * this.lookAtLockPerc=1;
  2165. * this.pxlCamera.lookAtTarget( targetObj );
  2166. *.
  2167. lookAtTargetLock(){
  2168. if(!this.lookAtTargetActive){ return; }
  2169. if(this.lookAtTargetActive){
  2170. if(this.lookAtLockFader!=0){
  2171. this.lookAtLockPerc+=(this.lookAtLockFader+Math.min(1,this.pxlDevice.touchMouseData.velocity.length()*.001))*this.lookAtLockFadeRate;
  2172. if(this.lookAtLockPerc<0 || this.lookAtLockPerc>1){
  2173. this.lookAtLockPerc=this.lookAtLockPerc<0?0:1;
  2174. this.lookAtLockFader=0;
  2175. }
  2176. this.lookAtPerc.x = this.lookAtLockPerc;
  2177. }
  2178. ./ If Look At is locked
  2179. ./ set the offset in rotation
  2180. ./ slerpin some quats!
  2181. if(this.lookAtLockPerc>0){
  2182. let origCamQuat=this.camera.quaternion.clone();
  2183. this.camera.lookAt(this.cameraAimTarget.position);
  2184. let targetCamQuat=this.camera.quaternion.clone();
  2185. if(this.lookAtLockPerc==1){
  2186. this.camera.setRotationFromQuaternion( targetCamQuat );
  2187. }else{
  2188. this.camera.setRotationFromQuaternion( targetCamQuat.slerp(origCamQuat,Math.cos(this.lookAtLockPerc*pi)*.5+.5) );
  2189. }
  2190. this.hasRotated=true;
  2191. }
  2192. }
  2193. }
  2194. .//////////////////////////////////////////
  2195. ./ Render Effects / Quality Functions //
  2196. .////////////////////////////////////////
  2197. .**
  2198. * Triggers a Room Warp . Portal Screen Effect event.
  2199. * This will initiate the visual effect and set the end warp object and target.
  2200. * The actual warp will be run in the `warpCamRun()` function on following frames.
  2201. * @param {number} [visualType=0] - The type of visual effect.
  2202. * @param {Object} [warpObj=null] - The warp object.
  2203. * @param {string} [target='init'] - The target of the warp.
  2204. * @example
  2205. * ./ Trigger a Room Warp / Portal Screen Effect event.
  2206. * this.pxlCamera.warpEventTriggered( 1, warpObj, 'init' );
  2207. *.
  2208. warpEventTriggered( visualType=0, warpObj=null, target='init' ){
  2209. if( !this.warpActive ){
  2210. this.pxlEnv.mapComposerWarpPass.needsSwap=true;
  2211. this.warpType=visualType;
  2212. this.warpObj=warpObj;
  2213. this.warpTarget=target;
  2214. this.warpActive=true;
  2215. this.pxlEnv.initWarpVisual( visualType );
  2216. }
  2217. }
  2218. .**
  2219. * Runs the room warping camera effects.
  2220. *.
  2221. warpCamRun(){
  2222. if(this.warpType==0){
  2223. this.setToObj( this.warpObj );
  2224. }else if(this.warpType==1){
  2225. let init=this.warpTarget=='init';
  2226. this.warpToRoom( this.warpObj, init, this.warpTarget );
  2227. }
  2228. this.pxlEnv.setExposure( this.uniformScalars.exposureUniformBase );
  2229. this.warpObj=null;
  2230. this.warpTarget=null;
  2231. this.warpActive=false;
  2232. }
  2233. .**
  2234. * Updates low-quality render events.
  2235. * @private
  2236. *.
  2237. lowQualityUpdates(){
  2238. if(this.HDRView){
  2239. let uPitch=new Vector3(0,0,-1).applyQuaternion( this.camera.quaternion );
  2240. let uRot=uPitch.clone().multiply(new Vector3(1,0,1)).normalize();
  2241. let ptr=0.1591549430918955;
  2242. ./ Update shader uniforms -
  2243. this.camRotPitch.set(
  2244. -Math.atan2(uRot.x,uRot.z)*ptr,
  2245. uPitch.y*.5 );
  2246. }
  2247. }
  2248. .**
  2249. * Updates mid-quality render events.
  2250. * @private
  2251. *.
  2252. midQualityUpdates(){
  2253. ./ Trailing Effects; Fake Motion Blur
  2254. if( this.pxlQuality.settings.motion ){ ./ Don't run blur pass if the quality setting is under 50%
  2255. let shaderCamRot=new Vector3(0,0,0);
  2256. shaderCamRot.applyQuaternion(this.camera.quaternion);./.add(camOrigQuat).multiplyScalar(.5);
  2257. this.camRotXYZ.multiplyScalar(.8).add( shaderCamRot.multiplyScalar(.2) );
  2258. let viewDirection;
  2259. if(this.pxlDevice.mobile){
  2260. let sWHalf = sW*.5
  2261. let sHHalf= sH*.5;
  2262. let fromWorldPos=new Vector3(0,0,10);
  2263. let toWorldPos=new Vector3(0,0,10);
  2264. ./fromWorldPos.applyMatrix4( this.camera.matrixWorld.clone() ).project(this.camera);
  2265. ./toWorldPos.applyMatrix4( this.prevWorldMatrix ).project(this.camera);
  2266. fromWorldPos.applyQuaternion( this.camera.quaternion.clone() ).project(this.camera);
  2267. toWorldPos.applyQuaternion( this.prevQuaternion ).project(this.camera);
  2268. fromWorldPos.x=(fromWorldPos.x+1)*sWHalf;
  2269. fromWorldPos.y=-(fromWorldPos.y-1)*sHHalf;
  2270. toWorldPos.x=(toWorldPos.x+1)*sWHalf;
  2271. toWorldPos.y=-(toWorldPos.y-1)*sHHalf;
  2272. viewDirection=toWorldPos.clone().sub(fromWorldPos.clone()).multiplyScalar(.6).multiply(new Vector3(this.pxlDevice.screenRes.x,this.pxlDevice.screenRes.y,0));
  2273. let motionBlurMaxDist=.1;
  2274. if(viewDirection.length>motionBlurMaxDist){
  2275. viewDirection.normalize().multiplyScalar(motionBlurMaxDist);
  2276. }
  2277. ./viewDirection=this.pxlDevice.touchMouseData.velocityEase.clone().multiplyScalar( Math.max(this.screenRes[1],this.screenRes[0]) );
  2278. }else{
  2279. ./viewDirection=this.pxlDevice.touchMouseData.velocityEase.clone().multiplyScalar( Math.max(this.pxlDevice.screenRes.x,this.pxlDevice.screenRes.y) );
  2280. viewDirection=this.pxlDevice.touchMouseData.velocity.clone().multiplyScalar( Math.max(this.pxlDevice.screenRes.x,this.pxlDevice.screenRes.y) );
  2281. }
  2282. let toDir=new Vector2( viewDirection.x, -viewDirection.y);
  2283. let blurDir=new Vector2(0,0).lerpVectors( this.pxlEnv.blurDirPrev, toDir, .85 );
  2284. ./ Update motionBlur direction uniforms -
  2285. this.pxlEnv.blurDirPrev.set( this.pxlEnv.blurDirCur );
  2286. this.pxlEnv.blurDirCur.set( blurDir );
  2287. }
  2288. }
  2289. .//////////////////////////////////////////////////////
  2290. ./ WebSocket Emit for Position and Rotation Changes //
  2291. .////////////////////////////////////////////////////
  2292. ./ Notify Server of Position / Rotation Changes
  2293. .**
  2294. * Emits camera transforms to the server.
  2295. * NETWORKING HAS BEEN REMOVED, you'll need to implement your own server-side logic.
  2296. * @param {Vector3} cameraPos - The camera position.
  2297. * @param {number} standingHeight - The standing height.
  2298. * @param {boolean} [force=false] - Whether to force the emission.
  2299. * @private
  2300. *.
  2301. emitCameraTransforms( cameraPos, standingHeight, force=false ){
  2302. this.emit("cameraTransforms",{
  2303. cameraPos:cameraPos,
  2304. standingHeight:standingHeight,
  2305. force:force
  2306. });
  2307. ./ Networking scripting removed
  2308. }
  2309. .**
  2310. * Jog the server memory with the current camera position.
  2311. * Originally the server would use this event to update remote client's positions of the local user.
  2312. * Also as a server-side sanity check that the user is in the correct "chat" room and other data that may have gone stale.
  2313. * You'll need to implement your own usage of this function with server-side logic.
  2314. * CURRENTLY UNUSED
  2315. * @private
  2316. *.
  2317. jogServerMemory(){
  2318. let curCamPos=this.cameraPos.clone();
  2319. let standingHeight=this.getUserHeight();
  2320. this.emitCameraTransforms( curCamPos, standingHeight, true );
  2321. }
  2322. .//////////////////////////////////
  2323. ./ Main Camera Update Function //
  2324. .////////////////////////////////
  2325. .**
  2326. * Main update function for the camera.
  2327. *.
  2328. updateCamera(){
  2329. ./this.updateStaticRotation();
  2330. ./let velEaseMag=this.pxlDevice.touchMouseData.velocityEase.length();
  2331. let velEaseMag = this.pxlDevice.touchMouseData.velocity.length();
  2332. this.hasRotated = this.hasRotated || velEaseMag > 0;
  2333. this.camUpdated = this.camUpdated || this.hasRotated;
  2334. ./ Fade out touchMouseData, likely to be removed in later versions
  2335. this.pxlDevice.touchMouseData.curFadeOut.multiplyScalar( .7 );
  2336. ./ Check if the camera has been updated; step camera values, apply gravity, check for colliders, and interact with objects
  2337. if( this.camUpdated ){ ./ || this.pxlDevice.touchMouseData.active){// || this.lookAtLockPerc>0 ){ // ## Not using any cam locking yet
  2338. ./ Camera checks are initiating
  2339. this.camUpdated=false;
  2340. let didUpdate=false;
  2341. this.updateDeviceValues( velEaseMag );
  2342. ./ TODO : Enable when User class is updated
  2343. ./this.pxlUser.localUserTurned=this.pxlDevice.touchMouseData.velocity.length() == 0;
  2344. this.prevQuaternion.copy( this.camera.quaternion );
  2345. ./this.prevWorldMatrix.set( this.camera.matrixWorld ); // Only used if running higher quality motion blur, not needed
  2346. ./ For Gyro enabled devices; Mobile / Tablet / Surface devices
  2347. ./ ## Work in progress, waste of calculations acurrently
  2348. ./this.camApplyMobileRotation();
  2349. let cameraPos=this.initFrameCamPosition();
  2350. ./ Appy Gravity Height Offset
  2351. let standingHeight=this.getUserHeight();
  2352. if( this.pxlOptions.staticCamera ){./ Static Camera Mode
  2353. this.hasGravity = false;
  2354. this.hasMoved = false;
  2355. if( this.pxlOptions.allowStaticRotation && this.hasRotated ){ ./ Static Camera Mode
  2356. this.updateStaticRotation();
  2357. this.camera.updateMatrixWorld();
  2358. }
  2359. this.hasRotated = false;
  2360. return;
  2361. }
  2362. ./ Movement checks
  2363. if( this.hasMoved || this.hasGravity ){./&& this.canMove ){
  2364. ./ Check if the camera is in a valid position
  2365. cameraPos=this.colliderCheck( cameraPos );
  2366. ./ If in air, gravity grows
  2367. ./ This only updates gravity prior to jump calculations
  2368. ./ User vertical based calculations are ran in `applyGravity()`
  2369. this.updateGravity( this.pxlTimer.deltaTime );
  2370. ./ When Jumping / Falling, Collion Hit Position and Object are marked as Invalid
  2371. if( !this.colliderValid && !this.colliderValidityChecked ){
  2372. ./ Check if within maxStepHeight+gravityRate distance of collider hit position
  2373. this.jump=this.checkColliderValid( cameraPos ); ./ Sets colliderValid
  2374. }else{
  2375. this.jump=0;
  2376. }
  2377. this.eventCheckStatus=true;
  2378. ./ Apply gravity rate to current camera position
  2379. cameraPos=this.applyGravity(cameraPos);
  2380. ./ Check length of Camera Movement, `**.5` is the square root of the sum of the squares
  2381. ./ TODO : Implement when User class is updated
  2382. ./this.pxlUser.localUserMoved= this.hasGravity || ((this.cameraMovement[0]**2 + this.cameraMovement[1]**2) ** .5) > 0;
  2383. this.cameraPrevPos=this.cameraPos.clone();
  2384. this.cameraPos=cameraPos.clone();
  2385. didUpdate = this.cameraPos.distanceTo(this.cameraPrevPos) > 0;
  2386. cameraPos.y+=standingHeight+this.cameraJumpHeight;
  2387. this.camera.position.copy(cameraPos);
  2388. }
  2389. ./ Performing different rotation logic based on if the camera is in Roam or Static mode
  2390. ./ Roaming orients the camera up in Y
  2391. ./ Static keeps the camera oriented with Camera Position -to- LookAt cross product
  2392. ./ `cross( cross( normalized(LookAt-Pos), Up), Up )`
  2393. if( this.hasRotated && this.canMove ){ ./ Roam Camera Mode
  2394. this.updateRoamRotation();
  2395. didUpdate = didUpdate || this.hasRotated;
  2396. }else if( this.hasRotated ){ ./ Static Camera Mode
  2397. this.updateStaticRotation();
  2398. didUpdate = didUpdate || this.hasRotated;
  2399. }
  2400. ./this.lookAtTargetLock(); // Camera lookAt target locking
  2401. if( didUpdate ){
  2402. this.camera.updateMatrixWorld(); ./ ## Only needed for lobby geo... Fix
  2403. this.emitCameraTransforms( cameraPos, standingHeight );
  2404. }
  2405. ./ Calculations completed, reset flags
  2406. this.hasMoved = false;
  2407. this.hasRotated = false;
  2408. this.cameraBooted=true;
  2409. }.*else{
  2410. ./ TODO : User class still not updated to utilize these
  2411. this.pxlUser.localUserMoved=false;
  2412. this.pxlUser.localUserTurned=false;
  2413. }*.
  2414. }
  2415. ./ -- -- --
  2416. ./ pxlNav Callbacks
  2417. ./ `event` should be of `pxlEnum.CAMERA_EVENT` type
  2418. .**
  2419. * Subscribes to a camera event.
  2420. * @param {string} event - The camera event.
  2421. * @param {function} callback - The callback function.
  2422. * @example
  2423. * ./ Subscribe to a camera event.
  2424. * this.pxlCamera.subscribe( "cameraTransforms", (data) => {
  2425. * console.log( data );
  2426. * }
  2427. *.
  2428. subscribe( event, callback ){
  2429. if( !this.callbacks.hasOwnProperty(event) ){
  2430. this.callbacks[event] = [];
  2431. }
  2432. this.callbacks[event].push( callback );
  2433. }
  2434. .**
  2435. * Emits a camera event.
  2436. * @private
  2437. *.
  2438. emit( event, data ){
  2439. if( this.callbacks.hasOwnProperty(event) ){
  2440. this.callbacks[event].forEach( (callback) => {
  2441. callback( data );
  2442. });
  2443. }
  2444. }
  2445. ./ -- -- --
  2446. }