import React, { useRef, useEffect } from 'react' import * as THREE from 'three' interface CityscapeBackgroundProps { className?: string } export function CityscapeBackground({ className }: CityscapeBackgroundProps) { const canvasRef = useRef(null) const sceneRef = useRef() const rendererRef = useRef() const cameraRef = useRef() const animationIdRef = useRef() const speedLinesRef = useRef() const mouseRef = useRef(new THREE.Vector2()) const startTimeRef = useRef(Date.now()) useEffect(() => { if (!canvasRef.current) { console.log('CityscapeBackground: Canvas ref not available') return } console.log('FuturistBackground: Initializing 3D futurist scene') // Scene setup with futurist art movement atmosphere const scene = new THREE.Scene() scene.background = new THREE.Color(0x000000) // Black background scene.fog = new THREE.Fog(0x000000, 30, 120) // Black fog for depth // Camera setup for isometric view const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 200) camera.position.set(30, 25, 30) camera.lookAt(0, 0, 0) // Renderer setup const renderer = new THREE.WebGLRenderer({ canvas: canvasRef.current, antialias: false, // Disable for better performance alpha: false // Disable alpha for opaque background }) renderer.setSize(window.innerWidth, window.innerHeight) renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5)) // Limit pixel ratio for performance // Store references sceneRef.current = scene rendererRef.current = renderer cameraRef.current = camera // Futurist art movement lighting - dramatic contrasts inspired by paintings const ambientLight = new THREE.AmbientLight(0x1a1a1a, 0.4) // Warm grey ambient scene.add(ambientLight) // Bold primary light - warm like industrial sunlight const primaryLight = new THREE.DirectionalLight(0xffaa33, 1.8) // Golden orange primaryLight.position.set(-25, 35, 20) scene.add(primaryLight) // Dynamic red light for energy and motion const dynamicLight = new THREE.DirectionalLight(0xdd2222, 1.2) // Bold red dynamicLight.position.set(20, 10, -15) scene.add(dynamicLight) // Contrasting blue for depth and drama const contrastLight = new THREE.PointLight(0x2244bb, 0.8, 50) // Deep blue contrastLight.position.set(-30, 5, 0) scene.add(contrastLight) // Yellow accent for vibrant highlights const accentLight = new THREE.PointLight(0xdddd22, 0.7, 40) // Yellow accentLight.position.set(25, 20, -5) scene.add(accentLight) // Green industrial light const industrialLight = new THREE.PointLight(0x22aa22, 0.6, 45) // Industrial green industrialLight.position.set(0, -5, 30) scene.add(industrialLight) // Create speed lines createSpeedLines() console.log('FuturistBackground: Dynamic futurist art created, starting animation') // Reset start time startTimeRef.current = Date.now() // Animation loop const animate = () => { animationIdRef.current = requestAnimationFrame(animate) const time = Date.now() * 0.001 // Convert to seconds for smoother animation // Animate speed lines animateSpeedLines(time) // Apply interactive effects applyFuturistInteractions() // Dramatic camera movement inspired by futurist dynamism const cameraSpeed = time * 0.2 camera.position.x = 20 + Math.sin(cameraSpeed * 1.3) * 12 + Math.cos(cameraSpeed * 0.7) * 6 camera.position.y = 15 + Math.cos(cameraSpeed * 0.9) * 8 + Math.sin(cameraSpeed * 1.1) * 4 camera.position.z = 25 + Math.sin(cameraSpeed * 0.6) * 15 + Math.cos(cameraSpeed * 1.4) * 8 // Dynamic look-at point for more dramatic perspective shifts const lookAtTarget = new THREE.Vector3( Math.sin(cameraSpeed * 0.8) * 5, Math.cos(cameraSpeed * 0.5) * 3, Math.sin(cameraSpeed * 1.2) * 8 ) camera.lookAt(lookAtTarget) renderer.render(scene, camera) } animate() // Mouse tracking for interactive effects const handleMouseMove = (event: MouseEvent) => { // Convert screen coordinates to normalized device coordinates (-1 to +1) mouseRef.current.set( (event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1 ) } // Handle window resize const handleResize = () => { if (camera && renderer) { camera.aspect = window.innerWidth / window.innerHeight camera.updateProjectionMatrix() renderer.setSize(window.innerWidth, window.innerHeight) } } window.addEventListener('mousemove', handleMouseMove) window.addEventListener('resize', handleResize) // Cleanup return () => { if (animationIdRef.current) { cancelAnimationFrame(animationIdRef.current) } window.removeEventListener('mousemove', handleMouseMove) window.removeEventListener('resize', handleResize) // Dispose of Three.js objects scene.clear() renderer.dispose() } }, []) const applyFuturistInteractions = () => { const camera = cameraRef.current! const raycaster = new THREE.Raycaster() // Convert mouse coordinates to world position raycaster.setFromCamera(mouseRef.current, camera) // Interactive speed line distortion if (speedLinesRef.current) { const positions = speedLinesRef.current.geometry.attributes.position.array as Float32Array for (let i = 0; i < positions.length; i += 6) { // Step by 6 for line pairs const linePos = new THREE.Vector3(positions[i], positions[i + 1], positions[i + 2]) const mouseWorldPos = new THREE.Vector3() raycaster.ray.at(linePos.z, mouseWorldPos) const distance = linePos.distanceTo(mouseWorldPos) const distortRadius = 15 if (distance < distortRadius) { const distortStrength = (distortRadius - distance) / distortRadius // Create dramatic wave distortion around mouse const waveX = Math.sin(Date.now() * 0.02 + i) * distortStrength * 2.0 const waveY = Math.cos(Date.now() * 0.025 + i) * distortStrength * 1.5 positions[i] += waveX positions[i + 1] += waveY positions[i + 3] += waveX * 0.8 // End point follows with slight lag positions[i + 4] += waveY * 0.8 } } speedLinesRef.current.geometry.attributes.position.needsUpdate = true } } const createSpeedLines = () => { const scene = sceneRef.current! // Create sharp, dramatic speed lines const motionVertices = [] const motionColors = [] // High-contrast speed colors const speedColors = [ { r: 1.0, g: 1.0, b: 1.0 }, // Brilliant white { r: 1.0, g: 0.1, b: 0.1 }, // Sharp red { r: 1.0, g: 0.8, b: 0.0 }, // Electric yellow { r: 0.0, g: 1.0, b: 1.0 }, // Cyan { r: 1.0, g: 0.0, b: 1.0 }, // Magenta ] // Create 300 sharp speed lines for intense motion for (let i = 0; i < 300; i++) { // Start point - spread across space const startX = (Math.random() - 0.5) * 100 const startY = (Math.random() - 0.5) * 40 const startZ = Math.random() * -200 // End point - long streaks suggesting extreme speed const endX = startX + (Math.random() - 0.5) * 20 const endY = startY + (Math.random() - 0.5) * 8 const endZ = startZ + 20 + Math.random() * 40 // Longer streaks motionVertices.push(startX, startY, startZ) motionVertices.push(endX, endY, endZ) // High-contrast colors for sharp appearance const colorIndex = Math.floor(Math.random() * speedColors.length) const color = speedColors[colorIndex] motionColors.push(color.r, color.g, color.b) motionColors.push(color.r, color.g, color.b) } const motionGeometry = new THREE.BufferGeometry() motionGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(motionVertices), 3)) motionGeometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(motionColors), 3)) const motionMaterial = new THREE.LineBasicMaterial({ vertexColors: true, transparent: true, opacity: 0.9, linewidth: 2 }) const speedLines = new THREE.LineSegments(motionGeometry, motionMaterial) speedLinesRef.current = speedLines scene.add(speedLines) } const animateSpeedLines = (time: number) => { if (!speedLinesRef.current) return const positions = speedLinesRef.current.geometry.attributes.position.array as Float32Array for (let i = 0; i < positions.length; i += 6) { // Step by 6 since we have line pairs // Extreme speed effect - lines blazing toward camera positions[i + 2] += 6.0 + Math.sin(time * 2 + i) * 3.0 // High variable speed positions[i + 5] += 6.0 + Math.sin(time * 2 + i) * 3.0 // End point matches // Intense lateral movement for speed blur const lateralMotion = Math.sin(time * 1.5 + i * 0.15) * 0.8 positions[i] += lateralMotion positions[i + 3] += lateralMotion // Sharp vertical oscillation for dynamic energy const verticalMotion = Math.cos(time * 2.2 + i * 0.12) * 0.6 positions[i + 1] += verticalMotion positions[i + 4] += verticalMotion // Reset lines that have blazed past camera if (positions[i + 2] > 100) { positions[i + 2] = -250 - Math.random() * 100 positions[i + 5] = positions[i + 2] + 20 + Math.random() * 40 positions[i] = (Math.random() - 0.5) * 100 positions[i + 1] = (Math.random() - 0.5) * 40 positions[i + 3] = positions[i] + (Math.random() - 0.5) * 20 positions[i + 4] = positions[i + 1] + (Math.random() - 0.5) * 8 } } speedLinesRef.current.geometry.attributes.position.needsUpdate = true // Rapid rotation for intense motion blur speedLinesRef.current.rotation.x += 0.008 speedLinesRef.current.rotation.y += 0.012 speedLinesRef.current.rotation.z += 0.025 } return ( ) }