🚀 Auto-deploy: BrainWind atualizado em 28/07/2026 11:08:28
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { useRef, useMemo } from 'react';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import * as THREE from 'three';
|
||||
|
||||
interface AirflowSystemProps {
|
||||
windAngle: number;
|
||||
width: number;
|
||||
length: number;
|
||||
height: number;
|
||||
permeabilityCase: string;
|
||||
}
|
||||
|
||||
export function AirflowSystem({ windAngle, width, length, height, permeabilityCase }: AirflowSystemProps) {
|
||||
const count = 300;
|
||||
const meshRef = useRef<THREE.InstancedMesh>(null);
|
||||
const particles = useMemo(() => {
|
||||
const temp = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
temp.push({
|
||||
position: new THREE.Vector3(
|
||||
(Math.random() - 0.5) * (width * 3),
|
||||
Math.random() * height * 2,
|
||||
(Math.random() - 0.5) * (length * 3)
|
||||
),
|
||||
speed: 0.1 + Math.random() * 0.2,
|
||||
wobbleSpeed: Math.random() * 0.05,
|
||||
wobbleOffset: Math.random() * Math.PI * 2,
|
||||
});
|
||||
}
|
||||
return temp;
|
||||
}, [count, width, length, height]);
|
||||
|
||||
const dummy = useMemo(() => new THREE.Object3D(), []);
|
||||
|
||||
useFrame((state) => {
|
||||
if (!meshRef.current) return;
|
||||
const time = state.clock.elapsedTime;
|
||||
const isParallel = windAngle === 90;
|
||||
|
||||
// Wind direction vector
|
||||
const dir = isParallel ? new THREE.Vector3(0, 0, 1) : new THREE.Vector3(1, 0, 0);
|
||||
|
||||
// Bounds
|
||||
const halfW = width / 2;
|
||||
const halfL = length / 2;
|
||||
|
||||
particles.forEach((p, i) => {
|
||||
// Move particle
|
||||
p.position.addScaledVector(dir, p.speed);
|
||||
|
||||
// Add slight turbulence
|
||||
p.position.y += Math.sin(time * p.wobbleSpeed * 10 + p.wobbleOffset) * 0.02;
|
||||
|
||||
const isInsideX = p.position.x > -halfW && p.position.x < halfW;
|
||||
const isInsideZ = p.position.z > -halfL && p.position.z < halfL;
|
||||
const isInsideY = p.position.y > 0 && p.position.y < height;
|
||||
const isInsideBuilding = isInsideX && isInsideZ && isInsideY;
|
||||
|
||||
if (isInsideBuilding && permeabilityCase === 'airtight') {
|
||||
// Push particle up to simulate wind going over the roof
|
||||
p.position.y += 0.2;
|
||||
}
|
||||
|
||||
// Reset if it goes too far
|
||||
if (isParallel) {
|
||||
if (p.position.z > halfL + 20) {
|
||||
p.position.z = -halfL - 20;
|
||||
p.position.y = Math.random() * height * 1.5;
|
||||
p.position.x = (Math.random() - 0.5) * (width * 2);
|
||||
}
|
||||
} else {
|
||||
if (p.position.x > halfW + 20) {
|
||||
p.position.x = -halfW - 20;
|
||||
p.position.y = Math.random() * height * 1.5;
|
||||
p.position.z = (Math.random() - 0.5) * (length * 2);
|
||||
}
|
||||
}
|
||||
|
||||
dummy.position.copy(p.position);
|
||||
|
||||
// Orient the particle along the wind direction
|
||||
dummy.lookAt(p.position.clone().add(dir));
|
||||
dummy.rotateX(Math.PI / 2); // Cylinder is aligned along Y by default, rotate to face direction
|
||||
|
||||
dummy.updateMatrix();
|
||||
meshRef.current!.setMatrixAt(i, dummy.matrix);
|
||||
});
|
||||
|
||||
meshRef.current.instanceMatrix.needsUpdate = true;
|
||||
});
|
||||
|
||||
return (
|
||||
<instancedMesh ref={meshRef} args={[undefined, undefined, count]}>
|
||||
{/* A simple arrow-like or dashed line geometry */}
|
||||
<cylinderGeometry args={[0.02, 0.02, 1.5, 4]} />
|
||||
<meshBasicMaterial
|
||||
color="#a5f3fc"
|
||||
transparent
|
||||
opacity={0.6}
|
||||
blending={THREE.AdditiveBlending}
|
||||
depthWrite={false}
|
||||
/>
|
||||
</instancedMesh>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user