Skip to content

Commit

Permalink
optimize instances in scene and make video hoverable
Browse files Browse the repository at this point in the history
  • Loading branch information
mbodge committed Oct 3, 2024
1 parent e393099 commit 059836b
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 49 deletions.
115 changes: 112 additions & 3 deletions apps/web/src/components/ThreeHero/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,17 @@ import {
Physics,
RigidBody,
BallCollider,
CuboidCollider,
Vector3Tuple,
CylinderCollider,
InstancedRigidBodies,
} from '@react-three/rapier';

import { EffectComposer, Bloom, SMAA } from '@react-three/postprocessing';

import {
Box,
//Box,
BlackMaterial,
BaseLogoModel,
Lightning,
blue,
Expand Down Expand Up @@ -203,6 +206,59 @@ export function Everything(props) {
);
}

function Boxes({ count = 10 }) {
const instancedApi = useRef(null);
const { viewport } = useThree();
const vec = useMemo(() => new THREE.Vector3(), []);
const gravityEffect = 0.003;

const instances = useMemo(() => {
const temp = [];
for (let i = 0; i < count; i++) {
temp.push({
key: 'instance_' + i,
position: [
THREE.MathUtils.randFloatSpread(viewport.width * 2),
THREE.MathUtils.randFloatSpread(viewport.height * 2),
THREE.MathUtils.randFloatSpread(viewport.width * 2),
],
rotation: [Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI],
scale: 0.5,
});
}
return temp;
}, [count, viewport]);

useFrame(() => {
if (instancedApi.current) {
for (let i = 0; i < count; i++) {
if (!instancedApi.current.at(i)) continue;
const instance = instancedApi.current.at(i);
const translation = instance.translation();
vec.set(translation.x, translation.y, translation.z).negate().multiplyScalar(gravityEffect);
instance.applyImpulse(vec);
}
}
});

return (
<InstancedRigidBodies
instances={instances}
ref={instancedApi}
linearDamping={4}
angularDamping={1}
friction={0.1}
>
<instancedMesh args={[null, null, count]} castShadow receiveShadow>
<boxGeometry args={[0.75, 0.75, 0.75]} />
<BlackMaterial />
</instancedMesh>
<CuboidCollider args={[0.5, 0.5, 0.5]} />
</InstancedRigidBodies>
);
}

/*
function Boxes({ count = 10 }: { count?: number }) {
const boxes = useMemo(
() =>
Expand All @@ -219,6 +275,7 @@ function Boxes({ count = 10 }: { count?: number }) {
return <group>{boxes}</group>;
}
function Balls({ count = 10 }: { count?: number }) {
const boxes = useMemo(
() =>
Expand All @@ -236,6 +293,58 @@ function Balls({ count = 10 }: { count?: number }) {
);
return <group>{boxes}</group>;
}*/

function Balls({ count = 20 }) {
const instancedApi = useRef(null);
const { viewport } = useThree();
const vec = useMemo(() => new THREE.Vector3(), []);
const gravityEffect = 0.00005;

const instances = useMemo(() => {
const temp = [];
for (let i = 0; i < count; i++) {
temp.push({
key: 'instance_' + i,
position: [
THREE.MathUtils.randFloatSpread(viewport.width * 2),
THREE.MathUtils.randFloatSpread(viewport.height * 2),
THREE.MathUtils.randFloatSpread(viewport.width * 2),
],
rotation: [Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI],
scale: 0.25,
});
}
return temp;
}, [count, viewport]);

useFrame(() => {
if (instancedApi.current) {
for (let i = 0; i < count; i++) {
if (!instancedApi.current.at(i)) continue;
const instance = instancedApi.current.at(i);
const translation = instance.translation();
vec.set(translation.x, translation.y, translation.z).negate().multiplyScalar(gravityEffect);
instance.applyImpulse(vec);
}
}
});

return (
<InstancedRigidBodies
instances={instances}
ref={instancedApi}
linearDamping={4}
angularDamping={1}
friction={0.1}
>
<instancedMesh args={[null, null, count]} castShadow receiveShadow>
<sphereGeometry args={[0.25, 64, 64]} />
<meshPhysicalMaterial color={blue} />
</instancedMesh>
<BallCollider args={[0.25]} />
</InstancedRigidBodies>
);
}

function BaseLogo() {
Expand Down Expand Up @@ -265,9 +374,9 @@ function BaseLogo() {

return (
<RigidBody type="kinematicPosition" colliders={false}>
<CylinderCollider rotation={[Math.PI / 2, 0, 0]} args={[10, mobile ? 0.75 : 2]} />
<CylinderCollider rotation={[Math.PI / 2, 0, 0]} args={[10, mobile ? 1.1 : 2]} />
<group ref={logoRef} position={[0, 0, -10]} rotation={[0, -Math.PI, 0]}>
<Center scale={mobile ? 0.05 : 0.13}>
<Center scale={mobile ? 0.075 : 0.13}>
<BaseLogoModel />
</Center>
</group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,19 @@ import cubes from './assets/cubes.webm';
export default function BuildAndRewardSection() {
const videoRef = useRef<HTMLVideoElement>(null);
const { logError } = useErrors();
const playVideo = useCallback(() => {
if (!videoRef.current) return;

const [isVisible, setIsVisible] = useState(true);
const [isWindowFocused, setIsWindowFocused] = useState(true);
const containerRef = useRef<HTMLDivElement>(null);

const handleVisibilityChange = useCallback(() => {
setIsWindowFocused(!document.hidden);
}, []);

useEffect(() => {
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, [handleVisibilityChange]);

useEffect(() => {
if (!containerRef.current) return;

const observer = new IntersectionObserver(
([entry]) => {
setIsVisible(entry.isIntersecting);
},
{ threshold: 0.1 }, // Adjust this value as needed
);

observer.observe(containerRef.current);

return () => {
observer.disconnect();
};
}, []);

useEffect(() => {
if (isVisible && isWindowFocused) {
videoRef.current.play().catch((error) => {
logError(error, 'failed to play video');
});
} else {
videoRef.current?.pause();
}
}, [isVisible, isWindowFocused]);
videoRef.current.play().catch((error) => {
logError(error, 'failed to play video');
});
}, [logError]);

return (
<section>
<div
ref={containerRef}
className="mb-12 mt-8 flex w-full flex-col items-center gap-4 md:flex-row"
>
<div className="mb-12 mt-8 flex w-full flex-col items-center gap-4 md:flex-row">
<div className="relative flex w-full flex-row gap-4">
<div className="relative flex w-full flex-row gap-4">
<div className="relative flex w-full flex-row gap-4" onMouseEnter={playVideo}>
<Card>
<video
src={cubes}
Expand Down

0 comments on commit 059836b

Please sign in to comment.