-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathBlocksScene.tsx
More file actions
111 lines (100 loc) · 3.34 KB
/
BlocksScene.tsx
File metadata and controls
111 lines (100 loc) · 3.34 KB
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
import * as THREE from 'three';
import pack from 'pack-spheres';
import { Float, ContactShadows } from '@react-three/drei';
import * as Random from 'canvas-sketch-util/random';
import { EffectComposer, DepthOfField } from '@react-three/postprocessing';
import { useThree } from '@react-three/fiber';
import { ErrorBoundary } from 'react-error-boundary';
import { Block, blockTypes } from './Block';
import { VersionText } from './VersionText';
import { Stage } from './Stage';
import { Suspense, useEffect, useState } from 'react';
import { colors } from './store';
import { withPrefix } from '@lib/with-prefix';
interface Sphere {
position: number[];
radius: number;
}
const size = 5.5;
const scale = [size * 6, size, size];
// Generate a blocks using sphere packing algorithm
const blocks = pack({
maxCount: 40,
minRadius: 0.125,
maxRadius: 0.25
}).map((sphere: Sphere, index: number) => {
const inFront = sphere.position[2] >= 0;
return {
...sphere,
id: index,
position: [
sphere.position[0],
sphere.position[1],
// shift the blocks to avoid overlapping with 7.0
inFront ? sphere.position[2] + 0.6 : sphere.position[2] - 0.6
].map((v: number, idx) => v * scale[idx]), // scale position to world space
size: sphere.radius * size * 1.5, // scale radius to world space
color: Random.pick(colors),
type: Random.pick(blockTypes),
rotation: new THREE.Quaternion(...Random.quaternion())
};
});
function LoopControl({ setFrameLoop }: { setFrameLoop: (value: 'demand' | 'always') => void }) {
const { invalidate } = useThree();
useEffect(() => {
const viewportWidth = Math.max(
document.documentElement.clientWidth || 0,
window.innerWidth || 0
);
if (viewportWidth < 400) {
setFrameLoop('demand');
invalidate();
}
}, [invalidate, setFrameLoop]);
return null;
}
function ErrorFallback() {
return <img src={withPrefix('/block-scene-fallback.png')} style={{ width: '100%' }} />;
}
export const BlocksScene = () => {
const [frameLoop, setFrameLoop] = useState<'demand' | 'always'>('always');
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Stage frameLoop={frameLoop}>
<LoopControl setFrameLoop={setFrameLoop} />
<Suspense fallback={null}>
<group position={[0, 0.5, 0]}>
<VersionText />
{blocks.map((block: any) => (
<Float
key={block.id}
position={block.position}
quaternion={block.rotation}
scale={block.size}
speed={1}
rotationIntensity={frameLoop === 'demand' ? 0 : 2}
floatIntensity={frameLoop === 'demand' ? 0 : 2}
floatingRange={[-0.25, 0.25]}
>
<Block type={block.type} color={block.color} />
</Float>
))}
<ContactShadows
position={[0, -8, 0]}
opacity={0.75}
width={20}
height={10}
blur={1}
far={9}
color="#333"
resolution={256}
/>
<EffectComposer multisampling={8}>
<DepthOfField focusDistance={0.5} bokehScale={7} focalLength={0.2} />
</EffectComposer>
</group>
</Suspense>
</Stage>
</ErrorBoundary>
);
};