Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/packages/firebird-ng/display/src/lib/services/game-controller.service.ts is written in an unsupported language. File is not indexed.

0001 import { Injectable } from '@angular/core';
0002 import * as THREE from "three";
0003 import {BehaviorSubject, Observable, Subject} from "rxjs";
0004 import {ThreeService} from "./three.service";
0005 import { ConfigService } from '@dexvis/app-features';
0006 import { USE_CONTROLLER_CONFIG } from '@dexvis/firebird-ng/api';
0007 
0008 
0009 export enum GamepadButtonIndexes {
0010   ButtonA = 0,
0011   ButtonB = 1,
0012   ButtonX = 2,
0013   ButtonY = 3,
0014   ButtonLB = 4,
0015   ButtonRB = 5,
0016   ButtonLT = 6,
0017   ButtonRT = 7,
0018   Select = 8,
0019   Start = 9,
0020 }
0021 
0022 export class GamepadObservableButton {
0023   private onPressSubject = new Subject<boolean>();
0024   public onPress = this.onPressSubject.asObservable();
0025   public state: GamepadButton = {pressed: false, touched: false, value: 0};
0026 
0027   constructor(public index: GamepadButtonIndexes) {
0028   }
0029 
0030   updateState(newState: GamepadButton) {
0031     if(this.onPressSubject.observed) {
0032       if(newState.pressed != this.state.pressed) {
0033         this.onPressSubject.next(newState.pressed);
0034       }
0035     }
0036     this.state = newState;
0037   }
0038 
0039   updateFromGamepadState(gamepad: Gamepad) {
0040     this.updateState(gamepad.buttons[this.index]);
0041   }
0042 }
0043 
0044 @Injectable({
0045   providedIn: 'root'
0046 })
0047 export class GameControllerService {
0048 
0049   private xAxisSubject = new BehaviorSubject<number>(0);
0050   public xAxisChanged = this.xAxisSubject.asObservable();
0051   public xAxis = 0;
0052 
0053   private yAxisSubject = new BehaviorSubject<number>(0);
0054   private yAxisChanged = this.yAxisSubject.asObservable();
0055   public yAxis: number = 0;
0056   public buttons: GamepadButton[] = [];
0057   public prevButtons: GamepadButton[] = [];
0058 
0059 
0060   public buttonA: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.ButtonA);
0061   public buttonB: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.ButtonB);
0062   public buttonX: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.ButtonX);
0063   public buttonY: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.ButtonY);
0064   public buttonLB: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.ButtonLB);
0065   public buttonRB: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.ButtonRB);
0066   public buttonLT: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.ButtonLT);
0067   public buttonRT: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.ButtonRT);
0068   public buttonStart: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.Start);
0069   public buttonSelect: GamepadObservableButton = new GamepadObservableButton(GamepadButtonIndexes.Select);
0070 
0071   public activeGamepad: Gamepad|null = null;
0072   private frameCallbackRef: (() => void) | null = null;
0073   private isControllerEnabled: boolean = false;
0074 
0075   animationLoopHandler () {
0076     // Only process if controller is enabled
0077     if (!this.isControllerEnabled) {
0078       return;
0079     }
0080 
0081     const epsilon = 0.01;
0082     const gamepads = navigator.getGamepads();
0083     for (const gamepad of gamepads) {
0084       if (gamepad) {
0085 
0086         this.activeGamepad = gamepad;
0087         // Example: Using left joystick to control OrbitControls
0088         // Axis 0: Left joystick horizontal (left/right)
0089         // Axis 1: Left joystick vertical (up/down)
0090         this.xAxis = gamepad.axes[0];
0091         this.yAxis = gamepad.axes[1];
0092 
0093         if(Math.abs(this.xAxis - this.xAxisSubject.value) > epsilon) {
0094           this.xAxisSubject.next(this.xAxis);
0095         }
0096 
0097         if(Math.abs(this.yAxis - this.yAxisSubject.value) > epsilon) {
0098           this.yAxisSubject.next(this.yAxis);
0099         }
0100 
0101         const xAxis = gamepad.axes[0];
0102         const yAxis = gamepad.axes[1];
0103 
0104         if (Math.abs(xAxis) > 0.1 || Math.abs(yAxis) > 0.1) {
0105           this.rotateCamera(xAxis, yAxis);
0106         }
0107 
0108         // Zooming using buttons (X and A)
0109         const zoomInButton = gamepad.buttons[2]; // X button
0110         const zoomOutButton = gamepad.buttons[0]; // A button
0111 
0112         if (zoomInButton.pressed) {
0113           this.zoom(0.99);
0114         }
0115 
0116         if (zoomOutButton.pressed) {
0117           this.zoom(1.01);
0118         }
0119 
0120         // Strafe functionality
0121         // B button for strafe left
0122         if (this.buttonB.state.pressed) {
0123           this.strafe(-0.5);
0124         }
0125 
0126         // RT button for strafe right
0127         if (this.buttonRT.state.pressed) {
0128           this.strafe(0.5);
0129         }
0130 
0131         // Default view on LT button press
0132         if (this.buttonLT.state.pressed) {
0133           this.resetToDefaultView();
0134         }
0135 
0136         this.buttonSelect.updateFromGamepadState(gamepad);
0137         this.buttonStart.updateFromGamepadState(gamepad);
0138 
0139         this.buttonA.updateFromGamepadState(gamepad);
0140         this.buttonB.updateFromGamepadState(gamepad);
0141         this.buttonY.updateFromGamepadState(gamepad);
0142         this.buttonX.updateFromGamepadState(gamepad);
0143 
0144         this.buttonLB.updateFromGamepadState(gamepad);
0145         this.buttonRB.updateFromGamepadState(gamepad);
0146         this.buttonLT.updateFromGamepadState(gamepad);
0147         this.buttonRT.updateFromGamepadState(gamepad);
0148 
0149         break; // Only use the first connected gamepad
0150       }
0151     }
0152   };
0153 
0154 
0155   rotateCamera(xAxisChange: number, yAxisChange: number) {
0156     let orbitControls = this.three.controls;
0157     let camera = this.three.camera;
0158 
0159     const offset = new THREE.Vector3(); // Offset of the camera from the target
0160     const quat = new THREE.Quaternion().setFromUnitVectors(camera.up, new THREE.Vector3(0, 1, 0));
0161     const quatInverse = quat.clone().invert();
0162 
0163     const currentPosition = camera.position.clone().sub(orbitControls.target);
0164     currentPosition.applyQuaternion(quat); // Apply the quaternion
0165 
0166     // Spherical coordinates
0167     const spherical = new THREE.Spherical().setFromVector3(currentPosition);
0168 
0169     // Adjusting spherical coordinates
0170     spherical.theta -= xAxisChange * 0.023; // Azimuth angle change
0171     spherical.phi += yAxisChange * 0.023; // Polar angle change, for rotating up/down
0172 
0173     // Ensure phi is within bounds to avoid flipping
0174     spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi));
0175 
0176     // Convert back to Cartesian coordinates
0177     const newPostion = new THREE.Vector3().setFromSpherical(spherical);
0178     newPostion.applyQuaternion(quatInverse);
0179 
0180     camera.position.copy(newPostion.add(orbitControls.target));
0181     camera.lookAt(orbitControls.target);
0182     orbitControls.update();
0183   }
0184 
0185   zoom(factor: number) {
0186     let orbitControls = this.three.controls;
0187     let camera = this.three.camera;
0188     orbitControls.object.position.subVectors(camera.position, orbitControls.target).multiplyScalar(factor).add(orbitControls.target);
0189     orbitControls.update();
0190   }
0191 
0192   strafe(amount: number) {
0193     const camera = this.three.camera;
0194     const controls = this.three.controls;
0195 
0196     // Move along Z axis (right = positive Z, left = negative Z)
0197     const offset = new THREE.Vector3(0, 0, amount * 10); // Scale for reasonable movement
0198 
0199     // Move camera and target together
0200     camera.position.add(offset);
0201     controls.target.add(offset);
0202     controls.update();
0203   }
0204 
0205   resetToDefaultView() {
0206     const camera = this.three.camera;
0207     const controls = this.three.controls;
0208 
0209     // Reset camera to default position
0210     camera.position.set(-7000, 0, 0);
0211     controls.target.set(0, 0, 0);
0212 
0213     // Update controls
0214     controls.update();
0215 
0216     console.log('[GameController] Camera reset to default view');
0217   }
0218 
0219   constructor(
0220     private three: ThreeService,
0221     private localStorageService: ConfigService
0222   ) {
0223     // The config page's "Use controller" toggle writes this key.
0224     const useController = this.localStorageService.declare(USE_CONTROLLER_CONFIG);
0225     this.isControllerEnabled = useController.value;
0226 
0227     // Create the callback reference
0228     this.frameCallbackRef = () => { this.animationLoopHandler(); };
0229 
0230     // Only attach handler if controller is enabled
0231     if (this.isControllerEnabled) {
0232       this.three.addFrameCallback(this.frameCallbackRef);
0233       console.log('[GameController] Controller enabled on initialization');
0234     }
0235 
0236     // Subscribe to changes in the use controller setting (root singleton: lives with the app)
0237     useController.changes$.subscribe((enabled) => {
0238       this.setControllerEnabled(enabled);
0239     });
0240 
0241     this.xAxisChanged.subscribe((data)=>{
0242       if (this.isControllerEnabled) {
0243         console.log(`[joystick] x: ${data}`);
0244       }
0245     });
0246     this.yAxisChanged.subscribe((data)=>{
0247       if (this.isControllerEnabled) {
0248         console.log(`[joystick] y: ${data}`);
0249       }
0250     });
0251   }
0252 
0253   private setControllerEnabled(enabled: boolean) {
0254     if (this.isControllerEnabled === enabled) {
0255       return;
0256     }
0257 
0258     this.isControllerEnabled = enabled;
0259 
0260     if (enabled) {
0261       // Attach the frame callback
0262       if (this.frameCallbackRef) {
0263         this.three.addFrameCallback(this.frameCallbackRef);
0264         console.log('[GameController] Controller enabled');
0265       }
0266     } else {
0267       // Remove the frame callback
0268       if (this.frameCallbackRef) {
0269         this.three.removeFrameCallback(this.frameCallbackRef);
0270         console.log('[GameController] Controller disabled');
0271       }
0272     }
0273   }
0274 }