130 lines
4.8 KiB
TypeScript
130 lines
4.8 KiB
TypeScript
import * as THREE from "three";
|
|
import { useEffect, useMemo, useRef, useCallback } from "react";
|
|
import { useThree } from "@react-three/fiber";
|
|
import { useHeatMapStore } from "../../../store/simulation/useHeatMapStore";
|
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
|
import { exportHeatmapAsPNG } from "../functions/exportHeatmapAsPNG";
|
|
|
|
// Constants
|
|
const RADIUS = 0.0025;
|
|
const OPACITY = 0.8;
|
|
const GROWTH_RATE = 20.0;
|
|
|
|
// 🔹 React Component
|
|
const BakedHeatMap = () => {
|
|
const { bakedPoints } = useHeatMapStore();
|
|
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
|
const meshRef = useRef<THREE.Mesh>(null);
|
|
const { gl } = useThree();
|
|
|
|
const height = CONSTANTS.gridConfig.size;
|
|
const width = CONSTANTS.gridConfig.size;
|
|
|
|
const pointTexture = useMemo(() => {
|
|
if (bakedPoints.length === 0) return null;
|
|
const data = new Float32Array(bakedPoints.length * 4);
|
|
bakedPoints.forEach((p, i) => {
|
|
const index = i * 4;
|
|
data[index] = (p.points.x + width / 2) / width;
|
|
data[index + 1] = (p.points.y + height / 2) / height;
|
|
data[index + 2] = 0.3;
|
|
data[index + 3] = 0.0;
|
|
});
|
|
const texture = new THREE.DataTexture(data, bakedPoints.length, 1, THREE.RGBAFormat, THREE.FloatType);
|
|
texture.needsUpdate = true;
|
|
return texture;
|
|
}, [bakedPoints, width, height]);
|
|
|
|
const uniformsRef = useRef({
|
|
u_points: { value: pointTexture },
|
|
u_count: { value: bakedPoints.length },
|
|
u_radius: { value: RADIUS },
|
|
u_opacity: { value: OPACITY },
|
|
u_growthRate: { value: GROWTH_RATE },
|
|
});
|
|
|
|
useEffect(() => {
|
|
uniformsRef.current.u_points.value = pointTexture;
|
|
uniformsRef.current.u_count.value = bakedPoints.length;
|
|
}, [pointTexture, bakedPoints.length]);
|
|
|
|
useEffect(() => {
|
|
if (meshRef.current) {
|
|
exportHeatmapAsPNG({
|
|
bakedPoints,
|
|
gl,
|
|
width: width,
|
|
height: height,
|
|
mesh: meshRef.current,
|
|
});
|
|
}
|
|
}, []);
|
|
|
|
return (
|
|
<></>
|
|
// <mesh ref={meshRef} rotation={[Math.PI / 2, 0, 0]} position={[0, 0.025, 0]}>
|
|
// <planeGeometry args={[width, height]} />
|
|
// <shaderMaterial
|
|
// ref={materialRef}
|
|
// transparent
|
|
// depthWrite={false}
|
|
// blending={THREE.AdditiveBlending}
|
|
// uniforms={uniformsRef.current}
|
|
// side={THREE.DoubleSide}
|
|
// vertexShader={`
|
|
// varying vec2 vUv;
|
|
// void main() {
|
|
// vUv = uv;
|
|
// gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
// }
|
|
// `}
|
|
// fragmentShader={`
|
|
// uniform sampler2D u_points;
|
|
// precision highp float;
|
|
// uniform int u_count;
|
|
// uniform float u_radius;
|
|
// uniform float u_opacity;
|
|
// uniform float u_growthRate;
|
|
// varying vec2 vUv;
|
|
|
|
// float gauss(float dist, float radius) {
|
|
// return exp(-pow(dist / radius, 2.0));
|
|
// }
|
|
|
|
// void main() {
|
|
// float intensity = 0.0;
|
|
|
|
// for (int i = 0; i < 10000; i++) {
|
|
// if (i >= u_count) break;
|
|
// float fi = float(i) + 0.5;
|
|
// float u = fi / float(u_count);
|
|
|
|
// vec4 point = texture2D(u_points, vec2(u, 0.5));
|
|
// vec2 pos = point.rg;
|
|
// float strength = point.b;
|
|
|
|
// float d = distance(vUv, pos);
|
|
// intensity += strength * gauss(d, u_radius);
|
|
// }
|
|
|
|
// float normalized = clamp(intensity / max(u_growthRate, 0.0001), 0.0, 1.0);
|
|
|
|
// vec3 color = vec3(0.0);
|
|
// if (normalized < 0.33) {
|
|
// color = mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), normalized / 0.33);
|
|
// } else if (normalized < 0.66) {
|
|
// color = mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 1.0, 0.0), (normalized - 0.33) / 0.33);
|
|
// } else {
|
|
// color = mix(vec3(1.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (normalized - 0.66) / 0.34);
|
|
// }
|
|
|
|
// gl_FragColor = vec4(color, normalized * u_opacity);
|
|
// }
|
|
// `}
|
|
// />
|
|
// </mesh>
|
|
);
|
|
};
|
|
|
|
export default BakedHeatMap;
|