summaryrefslogtreecommitdiff
path: root/frontend/src/components/CityscapeBackground.tsx
blob: 82a679d201d8bfbed218397ec3b8fe9789548b4e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
import React, { useRef, useEffect } from 'react'
import * as THREE from 'three'

interface CityscapeBackgroundProps {
  className?: string
}

export function CityscapeBackground({ className }: CityscapeBackgroundProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const sceneRef = useRef<THREE.Scene>()
  const rendererRef = useRef<THREE.WebGLRenderer>()
  const cameraRef = useRef<THREE.PerspectiveCamera>()
  const animationIdRef = useRef<number>()
  const speedLinesRef = useRef<THREE.LineSegments>()
  const mouseRef = useRef(new THREE.Vector2())
  const startTimeRef = useRef<number>(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 (
    <canvas
      ref={canvasRef}
      className={className}
      style={{
        position: 'fixed',
        top: 0,
        left: 0,
        width: '100%',
        height: '100%',
        zIndex: 0,
        pointerEvents: 'auto',
        opacity: 1,
        visibility: 'visible',
      }}
    />
  )
}