File size: 3,083 Bytes
d2226ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import * as THREE from "three";
import { useRef, useMemo } from "react";
import { useFrame } from "@react-three/fiber";

export function Meteors({ dateStr }) {
  const meshRef = useRef();
  const date = new Date(dateStr);
  const activeShower = useMemo(() => {}, [dateStr]);
  const count = activeShower ? 12 : 3;

  const { positions, velocities, lifetimes } = useMemo(() => {
    const pos = new Float32Array(count * 3 * 2);
    const vel = [];
    const life = new Float32Array(count);

    for (let i = 0; i < count; i++) {
      resetMeteor(i, pos, vel, life, activeShower);
    }
    return { positions: pos, velocities: vel, lifetimes: life };
  }, [activeShower]);

  function resetMeteor(i, pos, vel, life, shower) {
    const theta = Math.random() * Math.PI * 2;
    const phi = Math.acos(2 * Math.random() - 1);
    const start = new THREE.Vector3().setFromSphericalCoords(90, phi, theta);

    let velocity = new THREE.Vector3();

    if (shower) {
      const radiant = new THREE.Vector3(...shower.radiant).normalize();
      velocity
        .subVectors(start, radiant)
        .normalize()
        .multiplyScalar(1.5 + Math.random());
      const angle = start.angleTo(radiant);
      if (angle > Math.PI / 1.5 || angle < 0.2) {
        resetMeteor(i, pos, vel, life, shower);
        return;
      }
    } else {
      velocity
        .set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)
        .normalize()
        .multiplyScalar(2.0);
    }

    pos[i * 6] = start.x;
    pos[i * 6 + 1] = start.y;
    pos[i * 6 + 2] = start.z;
    pos[i * 6 + 3] = start.x;
    pos[i * 6 + 4] = start.y;
    pos[i * 6 + 5] = start.z;

    vel[i] = velocity;
    life[i] = Math.random() * 200;
  }

  useFrame(() => {
    if (!meshRef.current) return;
    const posAttr = meshRef.current.geometry.attributes.position;

    for (let i = 0; i < count; i++) {
      lifetimes[i] -= 1.0;

      if (lifetimes[i] < 0) {
        if (lifetimes[i] > -15) {
          const headIdx = i * 6;
          const tailIdx = i * 6 + 3;

          posAttr.array[tailIdx] = posAttr.array[headIdx] - velocities[i].x * 3;
          posAttr.array[tailIdx + 1] =
            posAttr.array[headIdx + 1] - velocities[i].y * 3;
          posAttr.array[tailIdx + 2] =
            posAttr.array[headIdx + 2] - velocities[i].z * 3;

          posAttr.array[headIdx] += velocities[i].x;
          posAttr.array[headIdx + 1] += velocities[i].y;
          posAttr.array[headIdx + 2] += velocities[i].z;
        } else {
          resetMeteor(i, posAttr.array, velocities, lifetimes, activeShower);
        }
      }
    }
    posAttr.needsUpdate = true;
  });

  return (
    <lineSegments ref={meshRef}>
      <bufferGeometry>
        <bufferAttribute
          attach="attributes-position"
          count={positions.length / 3}
          array={positions}
          itemSize={3}
        />
      </bufferGeometry>
      <lineBasicMaterial
        color={activeShower ? "#ccddff" : "#ffffff"}
        transparent
        opacity={0.5}
        linewidth={1}
      />
    </lineSegments>
  );
}