Back to home page

EIC code displayed by LXR

 
 

    


Warning, /firebird/firebird-ng/src/app/painters/box-tracker-hit.painter.ts is written in an unsupported language. File is not indexed.

0001 import {
0002   Object3D,
0003   InstancedMesh,
0004   BoxGeometry,
0005   ShaderMaterial,
0006   Object3D as THREEObject3D,
0007   InstancedBufferAttribute,
0008   Color,
0009   Matrix4,
0010 } from 'three';
0011 import { ComponentPainter } from './component-painter';
0012 import { BoxTrackerHitComponent } from '../model/box-tracker-hit.component';
0013 import {EntryComponent} from "../model/entry-component";
0014 
0015 /**
0016  * Painter class for rendering BoxTrackerHitComponent using InstancedMesh.
0017  */
0018 export class BoxTrackerHitPainter extends ComponentPainter {
0019   /** The InstancedMesh object to store multiple hit boxes. */
0020   private instancedMesh: InstancedMesh;
0021 
0022   /** Number of hits */
0023   private count: number;
0024 
0025   /** Shader material for the instanced mesh */
0026   private material: ShaderMaterial;
0027 
0028   /** Geometry for the instanced boxes */
0029   private geometry: BoxGeometry;
0030 
0031 
0032   private boxComponent: BoxTrackerHitComponent;
0033 
0034   /**
0035    * Constructs a new BoxHitPainter.
0036    *
0037    * @param node - The Object3D node where the instanced mesh will be added.
0038    * @param component - The BoxTrackerHitComponent containing the hit data.
0039    */
0040   constructor(node: Object3D, component: EntryComponent) {
0041     super(node, component);
0042 
0043     // Runtime type check
0044     if (component.type !== BoxTrackerHitComponent.type) {
0045       throw new Error('Invalid component type for BoxTrackerHitPainter');
0046     }
0047 
0048     this.boxComponent = component as BoxTrackerHitComponent;
0049 
0050     this.count = this.boxComponent.hits.length;
0051 
0052     // Define geometry for a unit box
0053     this.geometry = new BoxGeometry(1, 1, 1);
0054 
0055     // Define material for the boxes using shaders for time-based animation
0056     this.material = new ShaderMaterial({
0057       uniforms: {
0058         uTime: { value: 0 },
0059       },
0060       vertexShader: this.vertexShader(),
0061       fragmentShader: this.fragmentShader(),
0062       // Enable per-instance coloring
0063       vertexColors: true,
0064     });
0065 
0066     // Create the instanced mesh
0067     this.instancedMesh = new InstancedMesh(this.geometry, this.material, this.count);
0068 
0069     // Set up each instance (position, scale, color, appearance time)
0070     this.setupInstances(this.boxComponent);
0071 
0072     // Add the instanced mesh to the node
0073     node.add(this.instancedMesh);
0074   }
0075 
0076   /**
0077    * Sets up the instances of the hits, including position, scale, color, and time.
0078    *
0079    * @param component - The BoxTrackerHitComponent with the hit data.
0080    */
0081   private setupInstances(component: BoxTrackerHitComponent): void {
0082     const instanceColors: number[] = [];
0083     const instanceTimes: number[] = [];
0084     const dummy = new Object3D();
0085 
0086     for (let i = 0; i < component.hits.length; i++) {
0087       const hit = component.hits[i];
0088 
0089       // Set position from hit data
0090       dummy.position.set(hit.position[0], hit.position[1], hit.position[2]);
0091 
0092       // Set non-uniform scale from hit dimensions
0093       dummy.scale.set(hit.dimensions[0], hit.dimensions[1], hit.dimensions[2]);
0094 
0095       // Update the transformation matrix for this instance
0096       dummy.updateMatrix();
0097       this.instancedMesh.setMatrixAt(i, dummy.matrix);
0098 
0099       // Set color (e.g., based on energy deposit)
0100       // For example, mapping energy deposit to color intensity
0101       const edep = hit.energyDeposit[0];
0102       const color = new Color().setHSL(0.0, 1.0, Math.min(1.0, edep));
0103 
0104       instanceColors.push(color.r, color.g, color.b);
0105 
0106       // Set appearance time for this hit
0107       instanceTimes.push(hit.time[0]);
0108     }
0109 
0110     // Add per-instance color attribute
0111     const instanceColorAttr = new InstancedBufferAttribute(new Float32Array(instanceColors), 3);
0112     this.instancedMesh.instanceColor = instanceColorAttr;
0113 
0114     // Add per-instance appearance time attribute
0115     const instanceTimeAttr = new InstancedBufferAttribute(new Float32Array(instanceTimes), 1);
0116     this.instancedMesh.geometry.setAttribute('instanceAppearanceTime', instanceTimeAttr);
0117   }
0118 
0119   /**
0120    * Custom vertex shader to control the visibility based on time and transform the instance.
0121    */
0122   private vertexShader(): string {
0123     return `
0124       uniform float uTime;
0125       attribute float instanceAppearanceTime;
0126       attribute vec3 instanceColor;
0127       varying float vVisible;
0128       varying vec3 vColor;
0129 
0130       void main() {
0131         // Check if this instance should be visible at the current time
0132         vVisible = step(instanceAppearanceTime, uTime);
0133 
0134         // Pass the instance color to the fragment shader
0135         vColor = instanceColor;
0136 
0137         // Apply the usual transformations
0138         gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0);
0139       }
0140     `;
0141   }
0142 
0143   /**
0144    * Custom fragment shader to render each box with its color and control its visibility.
0145    */
0146   private fragmentShader(): string {
0147     return `
0148       varying float vVisible;
0149       varying vec3 vColor;
0150 
0151       void main() {
0152         if (vVisible < 1.0) discard; // Skip rendering if not visible
0153 
0154         gl_FragColor = vec4(vColor, 1.0); // Apply the instance color
0155       }
0156     `;
0157   }
0158 
0159   /**
0160    * Paint method to update the time-based animation of the hits.
0161    *
0162    * @param time - The current time in nanoseconds or null for static rendering.
0163    */
0164   public paint(time: number | null): void {
0165     if (time !== null) {
0166       this.material.uniforms["uTime"].value = time; // Assuming time is in nanoseconds
0167     }
0168 
0169     // If instances move or change over time, update instanceMatrix
0170     // this.instancedMesh.instanceMatrix.needsUpdate = true;
0171   }
0172 
0173   /**
0174    * Dispose of resources used by the BoxHitPainter.
0175    */
0176   override dispose(): void {
0177     if (this.instancedMesh) {
0178       // Dispose of geometry
0179       this.instancedMesh.geometry.dispose();
0180 
0181       // Dispose of material(s)
0182       const materials = Array.isArray(this.instancedMesh.material)
0183         ? this.instancedMesh.material
0184         : [this.instancedMesh.material];
0185       materials.forEach((material) => material.dispose());
0186 
0187       // Remove the instanced mesh from the scene
0188       this.instancedMesh.parent?.remove(this.instancedMesh);
0189     }
0190     super.dispose();
0191   }
0192 }