diff --git a/firebird-ng/src/app/app.component.html b/firebird-ng/src/app/app.component.html
index aa624de..a386dbf 100644
--- a/firebird-ng/src/app/app.component.html
+++ b/firebird-ng/src/app/app.component.html
@@ -34,18 +34,18 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/firebird-ng/src/app/app.routes.ts b/firebird-ng/src/app/app.routes.ts
index 91201eb..ea4aab0 100644
--- a/firebird-ng/src/app/app.routes.ts
+++ b/firebird-ng/src/app/app.routes.ts
@@ -4,9 +4,8 @@ import {FileBrowserComponent} from "./file-browser/file-browser.component";
import {InputConfigComponent} from "./input-config/input-config.component";
export const routes: Routes = [
- { path: '', redirectTo: '/config', pathMatch: 'full' },
+ { path: '', redirectTo: '/display', pathMatch: 'full' },
{ path: 'config', component: InputConfigComponent },
-
{
path: 'files',
loadComponent: () => import('./file-browser/file-browser.component').then(m => m.FileBrowserComponent)
diff --git a/firebird-ng/src/app/game-controller.service.ts b/firebird-ng/src/app/game-controller.service.ts
index 961e0f4..71c921d 100644
--- a/firebird-ng/src/app/game-controller.service.ts
+++ b/firebird-ng/src/app/game-controller.service.ts
@@ -1,9 +1,121 @@
import { Injectable } from '@angular/core';
+import * as THREE from "three";
+import {BehaviorSubject, Observable, Subject} from "rxjs";
+
+
+export enum ControllerButtonIndexes {
+ ButtonA = 0,
+ ButtonB = 1,
+ ButtonX = 2,
+ ButtonY = 3,
+ ButtonLB = 4,
+ ButtonRB = 5,
+ ButtonLT = 6,
+ ButtonRT = 7,
+ Select = 8,
+ Start = 9,
+}
+
@Injectable({
providedIn: 'root'
})
export class GameControllerService {
- constructor() { }
+ private xAxisSubject = new BehaviorSubject(0);
+ public xAxisChanged = this.xAxisSubject.asObservable();
+ public xAxis = 0;
+
+ private yAxisSubject = new BehaviorSubject(0);
+ private yAxisChanged = this.yAxisSubject.asObservable();
+ public yAxis: number = 0;
+ public buttons: GamepadButton[] = [];
+ public prevButtons: GamepadButton[] = [];
+
+ private buttonASubject = new Subject();
+ public buttonAPressed = this.buttonASubject.asObservable();
+ public buttonA: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonBSubject = new Subject();
+ public buttonBPressed = this.buttonBSubject.asObservable();
+ public buttonB: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonXSubject = new Subject();
+ public buttonXPressed = this.buttonXSubject.asObservable();
+ public buttonX: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonYSubject = new Subject();
+ public buttonYPressed = this.buttonYSubject.asObservable();
+ public buttonY: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonLBSubject = new Subject();
+ public buttonLBPressed = this.buttonLBSubject.asObservable();
+ public buttonLB: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonRBSubject = new Subject();
+ public buttonRBPressed = this.buttonRBSubject.asObservable();
+ public buttonRB: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonLTSubject = new Subject();
+ public buttonLTPressed = this.buttonLTSubject.asObservable();
+ public buttonLT: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonRTSubject = new Subject();
+ public buttonRTPressed = this.buttonRTSubject.asObservable();
+ public buttonRT: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonSelectSubject = new Subject();
+ public buttonSelectPressed = this.buttonSelectSubject.asObservable();
+ public buttonSelect: GamepadButton = {pressed: false, touched: false, value: 0};
+ private buttonStartSubject = new Subject();
+ public buttonStartPressed = this.buttonStartSubject.asObservable();
+ public buttonStart: GamepadButton = {pressed: false, touched: false, value: 0};
+
+ public activeGamepad: Gamepad|null = null;
+
+ animationLoopHandler () {
+
+ const epsilon = 0.01;
+ const gamepads = navigator.getGamepads();
+ for (const gamepad of gamepads) {
+ if (gamepad) {
+
+ this.activeGamepad = gamepad;
+ // Example: Using left joystick to control OrbitControls
+ // Axis 0: Left joystick horizontal (left/right)
+ // Axis 1: Left joystick vertical (up/down)
+ this.xAxis = gamepad.axes[0];
+ this.yAxis = gamepad.axes[1];
+
+ if(Math.abs(this.xAxis - this.xAxisSubject.value) > epsilon) {
+ this.xAxisSubject.next(this.xAxis);
+ }
+
+ if(Math.abs(this.yAxis - this.yAxisSubject.value) > epsilon) {
+ this.yAxisSubject.next(this.yAxis);
+ }
+
+ this.buttonA = gamepad.buttons[ControllerButtonIndexes.ButtonA];
+ this.buttonB = gamepad.buttons[ControllerButtonIndexes.ButtonB];
+ this.buttonX = gamepad.buttons[ControllerButtonIndexes.ButtonX];
+ this.buttonY = gamepad.buttons[ControllerButtonIndexes.ButtonY];
+ this.buttonLB = gamepad.buttons[ControllerButtonIndexes.ButtonLB];
+ this.buttonRB = gamepad.buttons[ControllerButtonIndexes.ButtonRB];
+ this.buttonLT = gamepad.buttons[ControllerButtonIndexes.ButtonLT];
+ this.buttonRT = gamepad.buttons[ControllerButtonIndexes.ButtonRT];
+ this.buttonSelect = gamepad.buttons[ControllerButtonIndexes.Select];
+ this.buttonStart = gamepad.buttons[ControllerButtonIndexes.Start];
+
+ if (this.buttonA.pressed !== this.buttonASubject.observed) this.buttonASubject.next(this.buttonA.pressed);
+ if (this.buttonB.pressed !== this.buttonBSubject.observed) this.buttonASubject.next(this.buttonB.pressed);
+ if (this.buttonX.pressed !== this.buttonXSubject.observed) this.buttonASubject.next(this.buttonX.pressed);
+ if (this.buttonY.pressed !== this.buttonYSubject.observed) this.buttonASubject.next(this.buttonY.pressed);
+ if (this.buttonLB.pressed !== this.buttonLBSubject.observed) this.buttonASubject.next(this.buttonLB.pressed);
+ if (this.buttonRB.pressed !== this.buttonRBSubject.observed) this.buttonASubject.next(this.buttonRB.pressed);
+ if (this.buttonLT.pressed !== this.buttonLTSubject.observed) this.buttonASubject.next(this.buttonLT.pressed);
+ if (this.buttonRT.pressed !== this.buttonRTSubject.observed) this.buttonASubject.next(this.buttonRT.pressed);
+ if (this.buttonSelect.pressed !== this.buttonSelectSubject.observed) this.buttonASubject.next(this.buttonSelect.pressed);
+ if (this.buttonStart.pressed !== this.buttonStartSubject.observed) this.buttonASubject.next(this.buttonStart.pressed);
+
+ break; // Only use the first connected gamepad
+ }
+ }
+ };
+
+ constructor() {
+ // Run it on contruction so if we have an active controller we set up values
+ this.animationLoopHandler();
+ }
}
diff --git a/firebird-ng/src/app/main-display/main-display.component.ts b/firebird-ng/src/app/main-display/main-display.component.ts
index 83a040f..1342b8d 100644
--- a/firebird-ng/src/app/main-display/main-display.component.ts
+++ b/firebird-ng/src/app/main-display/main-display.component.ts
@@ -2,7 +2,7 @@ import {Component, OnInit} from '@angular/core';
import {EventDisplayService, PhoenixUIModule} from 'phoenix-ui-components';
import {ClippingSetting, Configuration, PhoenixLoader, PhoenixMenuNode, PresetView} from 'phoenix-event-display';
import * as THREE from 'three';
-import {Color, DoubleSide, MeshPhongMaterial,} from "three";
+import {Color, DoubleSide, Line, MeshPhongMaterial,} from "three";
import {GeometryService} from '../geometry.service';
import {ActivatedRoute} from '@angular/router';
import {ThreeGeometryProcessor} from "../three-geometry.processor";
@@ -19,6 +19,11 @@ import {
import {mergeMeshList, MergeResult} from "../utils/three-geometry-merge";
import {PhoenixThreeFacade} from "../utils/phoenix-three-facade";
import {BehaviorSubject, Subject} from "rxjs";
+import {GameControllerService} from "../game-controller.service";
+import {LineMaterial} from "three/examples/jsm/lines/LineMaterial";
+import {Line2} from "three/examples/jsm/lines/Line2";
+import {LineGeometry} from "three/examples/jsm/lines/LineGeometry";
+// import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
@Component({
@@ -55,6 +60,7 @@ export class MainDisplayComponent implements OnInit {
constructor(
private geomService: GeometryService,
private eventDisplay: EventDisplayService,
+ private controller: GameControllerService,
private route: ActivatedRoute) {
this.threeFacade = new PhoenixThreeFacade(this.eventDisplay);
}
@@ -195,7 +201,53 @@ export class MainDisplayComponent implements OnInit {
}
};
- handleGamepadInput () {
+ rotateCamera(xAxisChange: number, yAxisChange: number) {
+ let orbitControls = this.threeFacade.activeOrbitControls;
+ let camera = this.threeFacade.mainCamera;
+
+ const offset = new THREE.Vector3(); // Offset of the camera from the target
+ const quat = new THREE.Quaternion().setFromUnitVectors(camera.up, new THREE.Vector3(0, 1, 0));
+ const quatInverse = quat.clone().invert();
+
+ const currentPosition = camera.position.clone().sub(orbitControls.target);
+ currentPosition.applyQuaternion(quat); // Apply the quaternion
+
+ // Spherical coordinates
+ const spherical = new THREE.Spherical().setFromVector3(currentPosition);
+
+ // Adjusting spherical coordinates
+ spherical.theta -= xAxisChange * 0.01; // Azimuth angle change
+ spherical.phi += yAxisChange * 0.01; // Polar angle change, for rotating up/down
+
+ // Ensure phi is within bounds to avoid flipping
+ spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi));
+
+ // Convert back to Cartesian coordinates
+ const newPostion = new THREE.Vector3().setFromSpherical(spherical);
+ newPostion.applyQuaternion(quatInverse);
+
+ camera.position.copy(newPostion.add(orbitControls.target));
+ camera.lookAt(orbitControls.target);
+ orbitControls.update();
+ }
+
+ zoom(factor: number) {
+ let orbitControls = this.threeFacade.activeOrbitControls;
+ let camera = this.threeFacade.mainCamera;
+ orbitControls.object.position.subVectors(camera.position, orbitControls.target).multiplyScalar(factor).add(orbitControls.target);
+ orbitControls.update();
+ }
+
+ handleGamepadInputV2 () {
+ this.controller.animationLoopHandler();
+ }
+
+ logCamera() {
+ console.log(this.threeFacade.mainCamera);
+ }
+
+
+ handleGamepadInputV1 () {
// Update stats display that showing FPS, etc.
if (this.stats) {
@@ -215,84 +267,20 @@ export class MainDisplayComponent implements OnInit {
let camera = this.threeFacade.mainCamera;
if (Math.abs(xAxis) > 0.1 || Math.abs(yAxis) > 0.1) {
- const offset = new THREE.Vector3(); // Offset of the camera from the target
- const quat = new THREE.Quaternion().setFromUnitVectors(camera.up, new THREE.Vector3(0, 1, 0));
- const quatInverse = quat.clone().invert();
-
- const currentPosition = camera.position.clone().sub(controls.target);
- currentPosition.applyQuaternion(quat); // Apply the quaternion
-
- // Spherical coordinates
- const spherical = new THREE.Spherical().setFromVector3(currentPosition);
-
- // Adjusting spherical coordinates
- spherical.theta -= xAxis * 0.01; // Azimuth angle change
- spherical.phi += yAxis * 0.01; // Polar angle change, for rotating up/down
-
- // Ensure phi is within bounds to avoid flipping
- spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi));
+ this.rotateCamera(xAxis, yAxis);
- // Convert back to Cartesian coordinates
- const newPostion = new THREE.Vector3().setFromSpherical(spherical);
- newPostion.applyQuaternion(quatInverse);
-
- camera.position.copy(newPostion.add(controls.target));
- camera.lookAt(controls.target);
- controls.update();
}
- // // Assume button indices 4 and 5 are the shoulder buttons
- // const zoomInButton = gamepad.buttons[0];
- // const zoomOutButton = gamepad.buttons[2];
- //
- // if (zoomInButton.pressed) {
- // camera.zoom *= 1.05;
- // console.log(camera);
- // // Updating camera clipping planes dynamically
- // //camera.near = 0.1; // Be cautious with making this too small, which can cause z-fighting
- // //camera.far = 100000; // Large value to ensure distant objects are rendered
- //
- // camera.updateProjectionMatrix();
- // }
- //
- // if (zoomOutButton.pressed) {
- // camera.zoom /= 1.05;
- // // Updating camera clipping planes dynamically
- // //camera.near = 0.1; // Be cautious with making this too small, which can cause z-fighting
- // //camera.far = 100000; // Large value to ensure distant objects are rendered
- // camera.updateProjectionMatrix();
- // console.log(camera);
- // }
- //
- // // Optionally: Map other axes/buttons to other camera controls like zoom or pan
- // if (gamepad.axes.length > 2) {
- // // Additional axes for more control, e.g., zoom with third axis
- // const zoomAxis = gamepad.axes[2]; // Typically the right stick vertical
- // camera.position.z += zoomAxis * 0.1; // Adjust zoom sensitivity
- // }
-
-
-
// Zooming using buttons
const zoomInButton = gamepad.buttons[2];
const zoomOutButton = gamepad.buttons[0];
if (zoomInButton.pressed) {
- controls.object.position.subVectors(camera.position, controls.target).multiplyScalar(0.99).add(controls.target);
- controls.update();
+ this.zoom(0.99);
}
if (zoomOutButton.pressed) {
- controls.object.position.subVectors(camera.position, controls.target).multiplyScalar(1.01).add(controls.target);
- controls.update();
- }
-
- // Zooming using the third axis of the gamepad
- const zoomAxis = gamepad.axes[2]; // Typically the right stick vertical
- if (Math.abs(zoomAxis) > 0.1) {
- let zoomFactor = zoomAxis < 0 ? 0.95 : 1.05;
- controls.object.position.subVectors(camera.position, controls.target).multiplyScalar(zoomFactor).add(controls.target);
- controls.update();
+ this.zoom(1.01);
}
break; // Only use the first connected gamepad
@@ -300,6 +288,11 @@ export class MainDisplayComponent implements OnInit {
}
};
+ updateProjectionMatrix() {
+ let camera = this.threeFacade.mainCamera;
+ camera.updateProjectionMatrix();
+ }
+
ngOnInit() {
@@ -322,7 +315,7 @@ export class MainDisplayComponent implements OnInit {
defaultEventFile: {
// (Assuming the file exists in the `src/assets` directory of the app)
//eventFile: 'assets/herwig_18x275_5evt.json',
- eventFile: 'assets/events/pythia8.json',
+ eventFile: 'assets/events/py8_all_dis-cc_beam-18x275_minq2-1000_nevt-20.evt.json',
eventType: 'json' // or zip
},
}
@@ -346,9 +339,13 @@ export class MainDisplayComponent implements OnInit {
// container: document.getElementById("lil-gui-place") ?? undefined,
});
+
gui.title("Debug");
gui.add(this, "produceRenderOrder");
gui.add(this, "logGamepadStates").name( 'Log controls' );
+ gui.add(this, "logCamera").name( 'Log camera' );
+ gui.add(this, "updateProjectionMatrix").name( 'Update Projection Matrix' );
+ gui.close();
// Set default clipping
this.eventDisplay.getUIManager().setClipping(true);
@@ -359,51 +356,88 @@ export class MainDisplayComponent implements OnInit {
this.eventDisplay.getLoadingManager().addLoadListenerWithCheck(() => {
console.log('Loading default configuration.');
this.loaded = true;
- });
- this.eventDisplay
- .getLoadingManager()
- .addProgressListener((progress) => (this.loadingProgress = progress));
- this.stats = (this.eventDisplay.getUIManager() as any).stats;
+ console.log(threeManager.getSceneManager().getEventData());
+ let mcTracksGroup = threeManager.getSceneManager().getObjectByName("mc_tracks");
+ if(mcTracksGroup) {
+ for(let trackGroup of mcTracksGroup.children) {
- threeManager.setAnimationLoop(()=>{this.handleGamepadInput()});
+ for(let obj of trackGroup.children) {
+ if(obj.type == "Line") {
- let beSubject = new BehaviorSubject('a');
- beSubject.next('b');
- beSubject.subscribe(value => {
- console.log('BehaviorSubject: Subscription received the value ', value);
+ let material = new LineMaterial( {
- // Subscription received B. It would not happen
- // for an Observable or Subject by default.
- });
+ color: 0xffff00,
+ linewidth: 50, // in world units with size attenuation, pixels otherwise
+ vertexColors: true,
+
+ //resolution: // to be set by renderer, eventually
+ dashed: true,
+ alphaToCoverage: true,
- beSubject.next('c');
-// Subscription received C.
+ } );
+ let positions = (obj.userData as any).pos;
+ let flat = [];
+ for(let position of positions) {
- beSubject.next('d');
-// Subscription received D.
+ flat.push(position[0], position[1], position[2]);
+ }
+ const geometry = new LineGeometry();
+ geometry.setPositions( flat );
+ // geometry.setColors( colors );
- // Subject.
+ let matLine = new LineMaterial( {
- let subject = new Subject();
+ color: 0xffff00,
+ linewidth: 10, // in world units with size attenuation, pixels otherwise
+ //vertexColors: true,
+ worldUnits: true,
+ //needsUpdate=: true;
+ //resolution: // to be set by renderer, eventually
+ dashed: true,
+ //dashScale: 100,
+ dashSize: 100,
+ gapSize: 100,
+ alphaToCoverage: true,
- subject.next('b');
+ } );
- subject.subscribe(value => {
- console.log('Subject: Subscription received the value ', value);
+ let line = new Line2( geometry, matLine );
+
+ line.scale.set( 1, 1, 1 );
+ line.computeLineDistances();
+ line.visible = true;
+ trackGroup.add( line );
+ obj.visible = false;
+ }
+
+ if(obj.type == "Mesh") {
+ obj.visible = false;
+ }
+ }
+ console.log(trackGroup);
+
+ }
+
+ }
- // Subscription won't receive anything at this point.
});
- subject.next('c');
-// Subscription received C.
- subject.next('d');
+ this.eventDisplay
+ .getLoadingManager()
+ .addProgressListener((progress) => (this.loadingProgress = progress));
+
+ this.stats = (this.eventDisplay.getUIManager() as any).stats;
+
+
+ threeManager.setAnimationLoop(()=>{this.handleGamepadInputV1()});
+
//const events_url = "https://eic.github.io/epic/artifacts/sim_dis_10x100_minQ2=1000_epic_craterlake.edm4hep.root/sim_dis_10x100_minQ2=1000_epic_craterlake.edm4hep.root"
@@ -419,9 +453,9 @@ export class MainDisplayComponent implements OnInit {
console.log(`geometry query: ${geometryAddress}`);
let jsonGeometry;
- this.loadGeometry().then(jsonGeom => {
- jsonGeometry = jsonGeom;
- });
+ // this.loadGeometry().then(jsonGeom => {
+ // jsonGeometry = jsonGeom;
+ // });