import {Injectable} from '@angular/core'; import * as THREE from 'three'; import {RendererService} from './renderer.service'; /** * Service responsible for managing the intersection plane */ @Injectable({ providedIn: 'root' }) export class IntersectionPlaneService { private intersectionPlane!: THREE.Mesh; private planePosition: number = 8; // Position in 1/16th of a block constructor(private rendererService: RendererService) { } /** * Creates the intersection plane and adds it to the scene */ createIntersectionPlane(): THREE.Mesh { const planeGeometry = new THREE.PlaneGeometry(3, 3); const planeMaterial = new THREE.MeshBasicMaterial({ color: 0x00AA00, transparent: true, opacity: 0.05, side: THREE.DoubleSide }); this.intersectionPlane = new THREE.Mesh(planeGeometry, planeMaterial); this.intersectionPlane.position.z = 0; // Center the plane vertically with the player (player is about 2 blocks tall) this.intersectionPlane.position.y = 1; this.rendererService.scene.add(this.intersectionPlane); return this.intersectionPlane; } /** * Updates the plane position based on slider value */ updatePlanePosition(value: number): void { this.planePosition = value; // Convert from 1/16th block to Three.js units const position = (this.planePosition / 16) - 0.5; // Center at 0 // Check if the plane is rotated vertically (looking from above/below) if (Math.abs(this.intersectionPlane.rotation.x) > 0.1) { // For vertical orientation, adjust Y position this.intersectionPlane.position.y = position * (this.intersectionPlane.rotation.x > 0 ? -1 : 1); // Reset x and z positions this.intersectionPlane.position.x = 0; this.intersectionPlane.position.z = 0; } else { const rotation = this.intersectionPlane.rotation.y; if (Math.abs(rotation) < 0.1 || Math.abs(rotation - Math.PI) < 0.1) { // Camera in front (0) or behind (PI) const direction = Math.abs(rotation) < 0.1 ? 1 : -1; this.intersectionPlane.position.z = position * direction; // Reset x position to avoid cumulative changes this.intersectionPlane.position.x = 0; } else { // Camera on right (PI/2) or left (-PI/2) const direction = rotation > 0 ? 1 : -1; this.intersectionPlane.position.x = position * direction; // Reset z position to avoid cumulative changes this.intersectionPlane.position.z = 0; } } } /** * Updates the plane orientation based on camera position */ updatePlaneOrientation(camera: THREE.Camera): void { if (!this.intersectionPlane) return; // Check if camera is looking from above or below first const verticalAngle = Math.atan2( camera.position.y, Math.sqrt(camera.position.x * camera.position.x + camera.position.z * camera.position.z) ); // Threshold angle for considering the camera to be above/below (about 45 degrees) const verticalThreshold = Math.PI / 4; if (verticalAngle > verticalThreshold) { // Camera is above this.intersectionPlane.rotation.x = -Math.PI / 2; this.intersectionPlane.rotation.y = 0; this.updatePlaneMaterial(0xAA0000); } else if (verticalAngle < -verticalThreshold) { // Camera is below this.intersectionPlane.rotation.x = Math.PI / 2; this.intersectionPlane.rotation.y = 0; this.updatePlaneMaterial(0xAA0000); } else { // Reset rotation.x as we're now in the horizontal plane this.intersectionPlane.rotation.x = 0; // Calculate the angle between camera and player (in the XZ plane) const cameraAngle = Math.atan2( camera.position.x, camera.position.z ); // Determine which quadrant the camera is in with a 45-degree offset const quadrant = Math.floor((cameraAngle + Math.PI + Math.PI / 4) / (Math.PI / 2)) % 4; // Rotate the plane to face the camera if (quadrant === 0) { this.intersectionPlane.rotation.y = 0; // Camera in front this.updatePlaneMaterial(0x00AA00); } else if (quadrant === 1) { this.intersectionPlane.rotation.y = Math.PI / 2; // Camera on right this.updatePlaneMaterial(0x0000AA); } else if (quadrant === 2) { this.intersectionPlane.rotation.y = Math.PI; // Camera behind this.updatePlaneMaterial(0x00AA00); } else { this.intersectionPlane.rotation.y = -Math.PI / 2; // Camera on left this.updatePlaneMaterial(0x0000AA); } } // Update position after rotation change this.updatePlanePosition(this.planePosition); } /** * Updates the plane material color */ private updatePlaneMaterial(color: number): void { this.intersectionPlane.material = new THREE.MeshBasicMaterial({ color: color, transparent: true, opacity: 0.05, side: THREE.DoubleSide }); } /** * Gets the intersection plane */ getIntersectionPlane(): THREE.Mesh { return this.intersectionPlane; } /** * Gets the current plane position */ getPlanePosition(): number { return this.planePosition; } }