|
|
|
|
@ -0,0 +1,778 @@
@@ -0,0 +1,778 @@
|
|
|
|
|
import { useRef, useMemo } from 'react'; |
|
|
|
|
import { useFrame } from '@react-three/fiber'; |
|
|
|
|
import { SpotLight, Text } from '@react-three/drei'; |
|
|
|
|
import * as THREE from 'three'; |
|
|
|
|
|
|
|
|
|
function DanceFloor() { |
|
|
|
|
return ( |
|
|
|
|
<mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow> |
|
|
|
|
<planeGeometry args={[30, 30]} /> |
|
|
|
|
<meshStandardMaterial |
|
|
|
|
color="#8b4513" |
|
|
|
|
roughness={0.6} |
|
|
|
|
metalness={0.0} |
|
|
|
|
/> |
|
|
|
|
</mesh> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function Balloons() { |
|
|
|
|
const balloons = useMemo(() => { |
|
|
|
|
const positions = []; |
|
|
|
|
for (let i = 0; i < 15; i++) { |
|
|
|
|
positions.push([ |
|
|
|
|
(Math.random() - 0.5) * 25, |
|
|
|
|
2 + Math.random() * 8, |
|
|
|
|
(Math.random() - 0.5) * 25 |
|
|
|
|
]); |
|
|
|
|
} |
|
|
|
|
return positions; |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
{balloons.map((pos, i) => ( |
|
|
|
|
<group key={i} position={pos}> |
|
|
|
|
<mesh> |
|
|
|
|
<sphereGeometry args={[0.3, 16, 16]} /> |
|
|
|
|
<meshStandardMaterial |
|
|
|
|
color={['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'][i % 6]} |
|
|
|
|
roughness={0.3} |
|
|
|
|
metalness={0.1} |
|
|
|
|
/> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0, -0.3, 0]}> |
|
|
|
|
<cylinderGeometry args={[0.01, 0.01, 1, 8]} /> |
|
|
|
|
<meshStandardMaterial color="#333" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
))} |
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function Confetti() { |
|
|
|
|
const confettiRef = useRef(); |
|
|
|
|
const count = 250; |
|
|
|
|
|
|
|
|
|
const positions = useMemo(() => { |
|
|
|
|
const pos = new Float32Array(count * 3); |
|
|
|
|
for (let i = 0; i < count * 3; i += 3) { |
|
|
|
|
pos[i] = (Math.random() - 0.5) * 30; |
|
|
|
|
pos[i + 1] = Math.random() * 15; |
|
|
|
|
pos[i + 2] = (Math.random() - 0.5) * 30; |
|
|
|
|
} |
|
|
|
|
return pos; |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
const colors = useMemo(() => { |
|
|
|
|
const cols = new Float32Array(count * 3); |
|
|
|
|
const colorArray = [ |
|
|
|
|
[1, 0, 0], [0, 1, 0], [0, 0, 1], |
|
|
|
|
[1, 1, 0], [1, 0, 1], [0, 1, 1] |
|
|
|
|
]; |
|
|
|
|
for (let i = 0; i < count; i++) { |
|
|
|
|
const color = colorArray[Math.floor(Math.random() * colorArray.length)]; |
|
|
|
|
cols[i * 3] = color[0]; |
|
|
|
|
cols[i * 3 + 1] = color[1]; |
|
|
|
|
cols[i * 3 + 2] = color[2]; |
|
|
|
|
} |
|
|
|
|
return cols; |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
useFrame((state) => { |
|
|
|
|
if (confettiRef.current) { |
|
|
|
|
confettiRef.current.rotation.y += 0.001; |
|
|
|
|
const positions = confettiRef.current.geometry.attributes.position.array; |
|
|
|
|
for (let i = 1; i < positions.length; i += 3) { |
|
|
|
|
positions[i] -= 0.01; |
|
|
|
|
if (positions[i] < 0) { |
|
|
|
|
positions[i] = 15; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
confettiRef.current.geometry.attributes.position.needsUpdate = true; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<points ref={confettiRef}> |
|
|
|
|
<bufferGeometry> |
|
|
|
|
<bufferAttribute |
|
|
|
|
attach="attributes-position" |
|
|
|
|
count={count} |
|
|
|
|
array={positions} |
|
|
|
|
itemSize={3} |
|
|
|
|
/> |
|
|
|
|
<bufferAttribute |
|
|
|
|
attach="attributes-color" |
|
|
|
|
count={count} |
|
|
|
|
array={colors} |
|
|
|
|
itemSize={3} |
|
|
|
|
/> |
|
|
|
|
</bufferGeometry> |
|
|
|
|
<pointsMaterial |
|
|
|
|
size={0.1} |
|
|
|
|
vertexColors |
|
|
|
|
transparent |
|
|
|
|
opacity={0.8} |
|
|
|
|
blending={THREE.AdditiveBlending} |
|
|
|
|
/> |
|
|
|
|
</points> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function Person({ position, index, lookAt }) { |
|
|
|
|
const groupRef = useRef(); |
|
|
|
|
const leftArmRef = useRef(); |
|
|
|
|
const rightArmRef = useRef(); |
|
|
|
|
|
|
|
|
|
// Randomize skin tones |
|
|
|
|
const skinTones = ['#ffdbac', '#f1c27d', '#e0ac69', '#c68642', '#8d5524']; |
|
|
|
|
const skinColor = skinTones[index % skinTones.length]; |
|
|
|
|
|
|
|
|
|
// More color variety for shirts |
|
|
|
|
const shirtColors = [ |
|
|
|
|
'#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7', |
|
|
|
|
'#a29bfe', '#fd79a8', '#00b894', '#e17055', '#74b9ff', |
|
|
|
|
'#a29bfe', '#fd79a8', '#fdcb6e', '#e84393', '#00cec9' |
|
|
|
|
]; |
|
|
|
|
const shirtColor = shirtColors[index % shirtColors.length]; |
|
|
|
|
|
|
|
|
|
useFrame(() => { |
|
|
|
|
if (groupRef.current && lookAt) { |
|
|
|
|
// Face the person they're talking to |
|
|
|
|
const dx = lookAt[0] - position[0]; |
|
|
|
|
const dz = lookAt[2] - position[2]; |
|
|
|
|
groupRef.current.rotation.y = Math.atan2(dx, dz); |
|
|
|
|
|
|
|
|
|
// Subtle idle animation - slight head/body movement |
|
|
|
|
const t = Date.now() * 0.005; |
|
|
|
|
const offset = index * 0.3; |
|
|
|
|
groupRef.current.rotation.y += Math.sin(t * 0.05 + offset) * 0.05; |
|
|
|
|
|
|
|
|
|
// Subtle arm gestures while talking |
|
|
|
|
if (leftArmRef.current) { |
|
|
|
|
leftArmRef.current.rotation.x = Math.sin(t * 1.7 + offset) * 0.1; |
|
|
|
|
leftArmRef.current.rotation.z = Math.sin(t * 1.5 + offset) * 0.05; |
|
|
|
|
} |
|
|
|
|
if (rightArmRef.current) { |
|
|
|
|
rightArmRef.current.rotation.x = Math.sin(t * 1.5 + offset + 1) * 0.1; |
|
|
|
|
rightArmRef.current.rotation.z = Math.sin(t * 1.5 + offset + 1) * 0.05; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<group ref={groupRef} position={position}> |
|
|
|
|
{/* Head */} |
|
|
|
|
<mesh position={[0, 1.45, 0]}> |
|
|
|
|
<sphereGeometry args={[0.15, 16, 16]} /> |
|
|
|
|
<meshStandardMaterial color={skinColor} /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Torso */} |
|
|
|
|
<mesh position={[0, 1.1, 0]}> |
|
|
|
|
<boxGeometry args={[0.35, 0.5, 0.25]} /> |
|
|
|
|
<meshStandardMaterial color={shirtColor} /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Left Arm */} |
|
|
|
|
<group ref={leftArmRef} position={[-0.18, 1.1, 0]}> |
|
|
|
|
<mesh position={[-0.15, 0, 0]}> |
|
|
|
|
<boxGeometry args={[0.3, 0.12, 0.12]} /> |
|
|
|
|
<meshStandardMaterial color={skinColor} /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
|
|
|
|
|
{/* Right Arm */} |
|
|
|
|
<group ref={rightArmRef} position={[0.18, 1.1, 0]}> |
|
|
|
|
<mesh position={[0.15, 0, 0]}> |
|
|
|
|
<boxGeometry args={[0.3, 0.12, 0.12]} /> |
|
|
|
|
<meshStandardMaterial color={skinColor} /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
|
|
|
|
|
{/* Left Leg - standing still */} |
|
|
|
|
<mesh position={[-0.1, 0.7, 0]}> |
|
|
|
|
<boxGeometry args={[0.12, 0.7, 0.12]} /> |
|
|
|
|
<meshStandardMaterial color="#333" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[-0.1, 0.25, 0.05]}> |
|
|
|
|
<boxGeometry args={[0.15, 0.1, 0.2]} /> |
|
|
|
|
<meshStandardMaterial color="#222" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Right Leg - standing still */} |
|
|
|
|
<mesh position={[0.1, 0.7, 0]}> |
|
|
|
|
<boxGeometry args={[0.12, 0.7, 0.12]} /> |
|
|
|
|
<meshStandardMaterial color="#333" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0.1, 0.25, 0.05]}> |
|
|
|
|
<boxGeometry args={[0.15, 0.1, 0.2]} /> |
|
|
|
|
<meshStandardMaterial color="#222" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function People() { |
|
|
|
|
// Create groups of people talking to each other - 80 total people |
|
|
|
|
const groups = useMemo(() => { |
|
|
|
|
const peopleGroups = []; |
|
|
|
|
const groupCount = 180; // 30 groups |
|
|
|
|
|
|
|
|
|
for (let g = 0; g < groupCount; g++) { |
|
|
|
|
const groupAngle = (g / groupCount) * Math.PI * 3; |
|
|
|
|
const groupRadius = 2 + Math.random() * 10; // Spread around the room |
|
|
|
|
const groupCenter = [ |
|
|
|
|
Math.cos(groupAngle) * groupRadius, |
|
|
|
|
0, |
|
|
|
|
Math.sin(groupAngle) * groupRadius |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
// Vary group sizes to get close to 80 total (mostly groups of 3, some 2) |
|
|
|
|
const peopleInGroup = g < 25 ? 3 : 2; |
|
|
|
|
const group = []; |
|
|
|
|
|
|
|
|
|
for (let p = 0; p < peopleInGroup; p++) { |
|
|
|
|
const personAngle = (p / peopleInGroup) * Math.PI * 2; |
|
|
|
|
const personRadius = 0.8; |
|
|
|
|
const personPos = [ |
|
|
|
|
groupCenter[0] + Math.cos(personAngle) * personRadius, |
|
|
|
|
0, |
|
|
|
|
groupCenter[2] + Math.sin(personAngle) * personRadius |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
// Person they're looking at (next person in circle, or center) |
|
|
|
|
const lookAtIndex = (p + 1) % peopleInGroup; |
|
|
|
|
const lookAtPos = [ |
|
|
|
|
groupCenter[0] + Math.cos((lookAtIndex / peopleInGroup) * Math.PI * 2) * personRadius, |
|
|
|
|
0, |
|
|
|
|
groupCenter[2] + Math.sin((lookAtIndex / peopleInGroup) * Math.PI * 2) * personRadius |
|
|
|
|
]; |
|
|
|
|
|
|
|
|
|
group.push({ position: personPos, lookAt: lookAtPos }); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
peopleGroups.push(...group); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return peopleGroups; |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
{groups.map((person, i) => ( |
|
|
|
|
<Person key={i} position={person.position} index={i} lookAt={person.lookAt} /> |
|
|
|
|
))} |
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function YouTubeBanner() { |
|
|
|
|
// Create triangle geometry for play button |
|
|
|
|
const triangleShape = useMemo(() => { |
|
|
|
|
const shape = new THREE.Shape(); |
|
|
|
|
shape.moveTo(0, 0.4); |
|
|
|
|
shape.lineTo(0.7, 0); |
|
|
|
|
shape.lineTo(0, -0.4); |
|
|
|
|
shape.lineTo(0, 0.4); |
|
|
|
|
return shape; |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<group position={[0, -1, 0.1]}> |
|
|
|
|
{/* Red background */} |
|
|
|
|
<mesh> |
|
|
|
|
<planeGeometry args={[5, 2]} /> |
|
|
|
|
<meshStandardMaterial color="#ff0000" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* White play button triangle */} |
|
|
|
|
<mesh position={[0, 0, 0.01]}> |
|
|
|
|
<shapeGeometry args={[triangleShape]} /> |
|
|
|
|
<meshStandardMaterial color="#ffffff" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* YouTube text */} |
|
|
|
|
<Text |
|
|
|
|
position={[0.3, 0, 0.01]} |
|
|
|
|
fontSize={0.35} |
|
|
|
|
color="#ffffff" |
|
|
|
|
anchorX="left" |
|
|
|
|
anchorY="middle" |
|
|
|
|
fontWeight="bold" |
|
|
|
|
> |
|
|
|
|
YouTube |
|
|
|
|
</Text> |
|
|
|
|
</group> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function DJ() { |
|
|
|
|
const groupRef = useRef(); |
|
|
|
|
const leftArmRef = useRef(); |
|
|
|
|
const rightArmRef = useRef(); |
|
|
|
|
|
|
|
|
|
useFrame((state) => { |
|
|
|
|
if (groupRef.current) { |
|
|
|
|
const t = state.clock.elapsedTime; |
|
|
|
|
// Subtle head bobbing to the beat |
|
|
|
|
groupRef.current.position.y = Math.sin(t * 2) * 0.05; |
|
|
|
|
|
|
|
|
|
// Arm movements - mixing/scratching |
|
|
|
|
if (leftArmRef.current) { |
|
|
|
|
leftArmRef.current.rotation.x = Math.sin(t * 3) * 0.2; |
|
|
|
|
leftArmRef.current.rotation.z = Math.sin(t * 3) * 0.1; |
|
|
|
|
} |
|
|
|
|
if (rightArmRef.current) { |
|
|
|
|
rightArmRef.current.rotation.x = Math.sin(t * 3 + 1) * 0.2; |
|
|
|
|
rightArmRef.current.rotation.z = Math.sin(t * 3 + 1) * 0.1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<group ref={groupRef} position={[0, 2, -13.3]}> |
|
|
|
|
{/* Head */} |
|
|
|
|
<mesh position={[0, 1.6, 0]}> |
|
|
|
|
<sphereGeometry args={[0.18, 16, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#d4a574" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Headphones */} |
|
|
|
|
<mesh position={[0, 1.6, 0]} rotation={[0, 0, 0]}> |
|
|
|
|
<torusGeometry args={[0.25, 0.05, 8, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[-0.25, 1.6, 0]}> |
|
|
|
|
<cylinderGeometry args={[0.12, 0.12, 0.15, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0.25, 1.6, 0]}> |
|
|
|
|
<cylinderGeometry args={[0.12, 0.12, 0.15, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Torso */} |
|
|
|
|
<mesh position={[0, 1.1, 0]}> |
|
|
|
|
<boxGeometry args={[0.35, 0.5, 0.25]} /> |
|
|
|
|
<meshStandardMaterial color="#000000" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Left Arm */} |
|
|
|
|
<group ref={leftArmRef} position={[-0.25, 1.1, 0]}> |
|
|
|
|
<mesh position={[-0.15, 0, 0]}> |
|
|
|
|
<boxGeometry args={[0.3, 0.12, 0.12]} /> |
|
|
|
|
<meshStandardMaterial color="#d4a574" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
|
|
|
|
|
{/* Right Arm */} |
|
|
|
|
<group ref={rightArmRef} position={[0.25, 1.1, 0]}> |
|
|
|
|
<mesh position={[0.15, 0, 0]}> |
|
|
|
|
<boxGeometry args={[0.3, 0.12, 0.12]} /> |
|
|
|
|
<meshStandardMaterial color="#d4a574" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
|
|
|
|
|
{/* Legs */} |
|
|
|
|
<mesh position={[-0.1, 0.7, 0]}> |
|
|
|
|
<boxGeometry args={[0.12, 0.7, 0.12]} /> |
|
|
|
|
<meshStandardMaterial color="#333" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0.1, 0.7, 0]}> |
|
|
|
|
<boxGeometry args={[0.12, 0.7, 0.12]} /> |
|
|
|
|
<meshStandardMaterial color="#333" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Shoes */} |
|
|
|
|
<mesh position={[-0.1, 0.25, 0.05]}> |
|
|
|
|
<boxGeometry args={[0.18, 0.1, 0.25]} /> |
|
|
|
|
<meshStandardMaterial color="#0a0a0a" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0.1, 0.25, 0.05]}> |
|
|
|
|
<boxGeometry args={[0.18, 0.1, 0.25]} /> |
|
|
|
|
<meshStandardMaterial color="#0a0a0a" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function DJBooth() { |
|
|
|
|
return ( |
|
|
|
|
<group position={[0, 0, -14]}> |
|
|
|
|
{/* Bar base - larger and more prominent */} |
|
|
|
|
<mesh position={[0, 0.5, 0]}> |
|
|
|
|
<boxGeometry args={[6, 1, 2]} /> |
|
|
|
|
<meshStandardMaterial color="#2a1a0a" roughness={0.3} metalness={0.2} /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Bar top */} |
|
|
|
|
<mesh position={[0, 1.2, 0]}> |
|
|
|
|
<boxGeometry args={[6.2, 0.2, 2.2]} /> |
|
|
|
|
<meshStandardMaterial color="#1a0a0a" roughness={0.1} metalness={0.5} /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* DJ equipment section - raised */} |
|
|
|
|
<mesh position={[0, 2, 0.6]}> |
|
|
|
|
<boxGeometry args={[4, 1.5, 0.8]} /> |
|
|
|
|
<meshStandardMaterial color="#0a0a0a" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Equipment panel */} |
|
|
|
|
<mesh position={[0, 2.5, 0.61]}> |
|
|
|
|
<boxGeometry args={[3.5, 0.4, 0.1]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Control buttons - more prominent */} |
|
|
|
|
<mesh position={[-1.2, 2.5, 0.62]}> |
|
|
|
|
<cylinderGeometry args={[0.2, 0.2, 0.08, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#ff0000" emissive="#ff0000" emissiveIntensity={0.5} /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0, 2.5, 0.62]}> |
|
|
|
|
<cylinderGeometry args={[0.2, 0.2, 0.08, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#00ff00" emissive="#00ff00" emissiveIntensity={0.5} /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[1.2, 2.5, 0.62]}> |
|
|
|
|
<cylinderGeometry args={[0.2, 0.2, 0.08, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#0000ff" emissive="#0000ff" emissiveIntensity={0.5} /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Bar stools */} |
|
|
|
|
<mesh position={[-2.5, 0.6, -1.2]} rotation={[0, 0, 0]}> |
|
|
|
|
<cylinderGeometry args={[0.15, 0.15, 1.2, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#654321" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[-2.5, 1.2, -1.2]}> |
|
|
|
|
<cylinderGeometry args={[0.4, 0.4, 0.1, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#8b4513" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
<mesh position={[2.5, 0.6, -1.2]} rotation={[0, 0, 0]}> |
|
|
|
|
<cylinderGeometry args={[0.15, 0.15, 1.2, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#654321" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[2.5, 1.2, -1.2]}> |
|
|
|
|
<cylinderGeometry args={[0.4, 0.4, 0.1, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#8b4513" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Banner hanging from above - bigger and more prominent */} |
|
|
|
|
<group position={[0, 5.5, 0]}> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* YouTube logo banner background */} |
|
|
|
|
<YouTubeBanner /> |
|
|
|
|
|
|
|
|
|
{/* Banner text - bigger */} |
|
|
|
|
<Text |
|
|
|
|
position={[0, -1, 0.12]} |
|
|
|
|
rotation={[0, 0, 0]} |
|
|
|
|
fontSize={0.4} |
|
|
|
|
color="#ffffff" |
|
|
|
|
anchorX="center" |
|
|
|
|
anchorY="middle" |
|
|
|
|
fontWeight="bold" |
|
|
|
|
outlineWidth={0.02} |
|
|
|
|
outlineColor="#000000" |
|
|
|
|
> |
|
|
|
|
1 MILLION SUBSCRIBERS |
|
|
|
|
</Text> |
|
|
|
|
|
|
|
|
|
{/* Rope/string to hang it */} |
|
|
|
|
|
|
|
|
|
</group> |
|
|
|
|
|
|
|
|
|
{/* Spotlight on the bar */} |
|
|
|
|
<pointLight position={[0, 5, -14]} intensity={50} distance={15} color="#ffffff" /> |
|
|
|
|
</group> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function DJBoothSpotlight() { |
|
|
|
|
const spotlightRef = useRef(); |
|
|
|
|
const targetRef = useRef(); |
|
|
|
|
|
|
|
|
|
useFrame(() => { |
|
|
|
|
if (spotlightRef.current && targetRef.current) { |
|
|
|
|
spotlightRef.current.target = targetRef.current; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<group> |
|
|
|
|
<spotLight |
|
|
|
|
ref={spotlightRef} |
|
|
|
|
position={[0, 10, -14]} |
|
|
|
|
angle={Math.PI / 6} |
|
|
|
|
penumbra={0.3} |
|
|
|
|
intensity={300} |
|
|
|
|
distance={20} |
|
|
|
|
decay={1} |
|
|
|
|
color="#ffffff" |
|
|
|
|
castShadow |
|
|
|
|
/> |
|
|
|
|
<object3D ref={targetRef} position={[0, 2, -14]} /> |
|
|
|
|
</group> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function Speakers() { |
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
<group position={[-14, 0, 0]}> |
|
|
|
|
<mesh position={[0, 1.5, 0]}> |
|
|
|
|
<boxGeometry args={[1, 3, 1]} /> |
|
|
|
|
<meshStandardMaterial color="#222" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0, 1.5, 0.51]}> |
|
|
|
|
<cylinderGeometry args={[0.4, 0.4, 0.1, 32]} /> |
|
|
|
|
<meshStandardMaterial color="#111" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
<group position={[14, 0, 0]}> |
|
|
|
|
<mesh position={[0, 1.5, 0]}> |
|
|
|
|
<boxGeometry args={[1, 3, 1]} /> |
|
|
|
|
<meshStandardMaterial color="#222" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0, 1.5, 0.51]}> |
|
|
|
|
<cylinderGeometry args={[0.4, 0.4, 0.1, 32]} /> |
|
|
|
|
<meshStandardMaterial color="#111" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function ThePrimeagen() { |
|
|
|
|
const groupRef = useRef(); |
|
|
|
|
const leftArmRef = useRef(); |
|
|
|
|
const rightArmRef = useRef(); |
|
|
|
|
|
|
|
|
|
useFrame((state) => { |
|
|
|
|
if (groupRef.current) { |
|
|
|
|
const t = state.clock.elapsedTime; |
|
|
|
|
// Subtle animation |
|
|
|
|
groupRef.current.rotation.y = Math.sin(t * 0.5) * 0.1; |
|
|
|
|
|
|
|
|
|
if (leftArmRef.current) { |
|
|
|
|
leftArmRef.current.rotation.x = Math.sin(t * 2) * 0.2; |
|
|
|
|
} |
|
|
|
|
if (rightArmRef.current) { |
|
|
|
|
rightArmRef.current.rotation.x = -Math.sin(t * 2) * 0.2; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<group ref={groupRef} position={[10, 0, -8]}> |
|
|
|
|
{/* Head */} |
|
|
|
|
<mesh position={[0, 1.6, 0]}> |
|
|
|
|
<sphereGeometry args={[0.18, 16, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#d4a574" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Beard */} |
|
|
|
|
<mesh position={[0, 1.45, 0.12]}> |
|
|
|
|
<boxGeometry args={[0.25, 0.2, 0.08]} /> |
|
|
|
|
<meshStandardMaterial color="#2a2a2a" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[-0.08, 1.4, 0.12]}> |
|
|
|
|
<boxGeometry args={[0.1, 0.15, 0.08]} /> |
|
|
|
|
<meshStandardMaterial color="#2a2a2a" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0.08, 1.4, 0.12]}> |
|
|
|
|
<boxGeometry args={[0.1, 0.15, 0.08]} /> |
|
|
|
|
<meshStandardMaterial color="#2a2a2a" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Glasses */} |
|
|
|
|
<mesh position={[-0.08, 1.58, 0.1]}> |
|
|
|
|
<torusGeometry args={[0.06, 0.01, 8, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" metalness={0.8} roughness={0.2} /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0.08, 1.58, 0.1]}> |
|
|
|
|
<torusGeometry args={[0.06, 0.01, 8, 16]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" metalness={0.8} roughness={0.2} /> |
|
|
|
|
</mesh> |
|
|
|
|
{/* Bridge */} |
|
|
|
|
<mesh position={[0, 1.58, 0.1]} rotation={[0, 0, Math.PI / 2]}> |
|
|
|
|
<boxGeometry args={[0.16, 0.01, 0.01]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" metalness={0.8} roughness={0.2} /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Hoodie */} |
|
|
|
|
<mesh position={[0, 1.1, 0]}> |
|
|
|
|
<boxGeometry args={[0.5, 0.6, 0.3]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Hood */} |
|
|
|
|
<mesh position={[0, 1.5, -0.05]}> |
|
|
|
|
<coneGeometry args={[0.3, 0.3, 8]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Left Arm */} |
|
|
|
|
<group ref={leftArmRef} position={[-0.3, 1.1, 0]}> |
|
|
|
|
<mesh position={[-0.15, 0, 0]}> |
|
|
|
|
<boxGeometry args={[0.3, 0.15, 0.15]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
|
|
|
|
|
{/* Right Arm */} |
|
|
|
|
<group ref={rightArmRef} position={[0.3, 1.1, 0]}> |
|
|
|
|
<mesh position={[0.15, 0, 0]}> |
|
|
|
|
<boxGeometry args={[0.3, 0.15, 0.15]} /> |
|
|
|
|
<meshStandardMaterial color="#1a1a1a" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
|
|
|
|
|
{/* Legs */} |
|
|
|
|
<mesh position={[-0.1, 0.7, 0]}> |
|
|
|
|
<boxGeometry args={[0.15, 0.7, 0.15]} /> |
|
|
|
|
<meshStandardMaterial color="#2a2a2a" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0.1, 0.7, 0]}> |
|
|
|
|
<boxGeometry args={[0.15, 0.7, 0.15]} /> |
|
|
|
|
<meshStandardMaterial color="#2a2a2a" /> |
|
|
|
|
</mesh> |
|
|
|
|
|
|
|
|
|
{/* Shoes */} |
|
|
|
|
<mesh position={[-0.1, 0.25, 0.05]}> |
|
|
|
|
<boxGeometry args={[0.18, 0.1, 0.25]} /> |
|
|
|
|
<meshStandardMaterial color="#0a0a0a" /> |
|
|
|
|
</mesh> |
|
|
|
|
<mesh position={[0.1, 0.25, 0.05]}> |
|
|
|
|
<boxGeometry args={[0.18, 0.1, 0.25]} /> |
|
|
|
|
<meshStandardMaterial color="#0a0a0a" /> |
|
|
|
|
</mesh> |
|
|
|
|
</group> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function Scene() { |
|
|
|
|
const spot1Ref = useRef(); |
|
|
|
|
const spot2Ref = useRef(); |
|
|
|
|
const spot3Ref = useRef(); |
|
|
|
|
const spot4Ref = useRef(); |
|
|
|
|
const spot5Ref = useRef(); |
|
|
|
|
|
|
|
|
|
useFrame((state) => { |
|
|
|
|
const t = state.clock.elapsedTime; |
|
|
|
|
|
|
|
|
|
if (spot1Ref.current) { |
|
|
|
|
const phase = t * 0.6; |
|
|
|
|
spot1Ref.current.target.position.x = Math.cos(phase) * 3; |
|
|
|
|
spot1Ref.current.target.position.z = Math.sin(phase) * 3; |
|
|
|
|
spot1Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (spot2Ref.current) { |
|
|
|
|
const phase = t * 0.6 + (Math.PI * 2 / 5); |
|
|
|
|
spot2Ref.current.target.position.x = Math.cos(phase) * 3; |
|
|
|
|
spot2Ref.current.target.position.z = Math.sin(phase) * 3; |
|
|
|
|
spot2Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (spot3Ref.current) { |
|
|
|
|
const phase = t * 0.6 + (Math.PI * 4 / 5); |
|
|
|
|
spot3Ref.current.target.position.x = Math.cos(phase) * 3; |
|
|
|
|
spot3Ref.current.target.position.z = Math.sin(phase) * 3; |
|
|
|
|
spot3Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (spot4Ref.current) { |
|
|
|
|
const phase = t * 0.6 + (Math.PI * 6 / 5); |
|
|
|
|
spot4Ref.current.target.position.x = Math.cos(phase) * 3; |
|
|
|
|
spot4Ref.current.target.position.z = Math.sin(phase) * 3; |
|
|
|
|
spot4Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (spot5Ref.current) { |
|
|
|
|
const phase = t * 0.6 + (Math.PI * 8 / 5); |
|
|
|
|
spot5Ref.current.target.position.x = Math.cos(phase) * 3; |
|
|
|
|
spot5Ref.current.target.position.z = Math.sin(phase) * 3; |
|
|
|
|
spot5Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
<DanceFloor /> |
|
|
|
|
<Balloons /> |
|
|
|
|
<Confetti /> |
|
|
|
|
<People /> |
|
|
|
|
<ThePrimeagen /> |
|
|
|
|
<DJBooth /> |
|
|
|
|
<DJ /> |
|
|
|
|
<DJBoothSpotlight /> |
|
|
|
|
<Speakers /> |
|
|
|
|
|
|
|
|
|
<SpotLight |
|
|
|
|
ref={spot1Ref} |
|
|
|
|
position={[10, 12, 10]} |
|
|
|
|
angle={Math.PI / 7} |
|
|
|
|
penumbra={0.5} |
|
|
|
|
intensity={250} |
|
|
|
|
distance={60} |
|
|
|
|
decay={1} |
|
|
|
|
color="#ff3366" |
|
|
|
|
castShadow |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<SpotLight |
|
|
|
|
ref={spot2Ref} |
|
|
|
|
position={[-10, 12, 10]} |
|
|
|
|
angle={Math.PI / 7} |
|
|
|
|
penumbra={0.5} |
|
|
|
|
intensity={250} |
|
|
|
|
distance={60} |
|
|
|
|
decay={1} |
|
|
|
|
color="#33ffcc" |
|
|
|
|
castShadow |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<SpotLight |
|
|
|
|
ref={spot3Ref} |
|
|
|
|
position={[0, 12, -12]} |
|
|
|
|
angle={Math.PI / 7} |
|
|
|
|
penumbra={0.5} |
|
|
|
|
intensity={250} |
|
|
|
|
distance={60} |
|
|
|
|
decay={1} |
|
|
|
|
color="#3366ff" |
|
|
|
|
castShadow |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<SpotLight |
|
|
|
|
ref={spot4Ref} |
|
|
|
|
position={[-10, 12, -10]} |
|
|
|
|
angle={Math.PI / 7} |
|
|
|
|
penumbra={0.5} |
|
|
|
|
intensity={250} |
|
|
|
|
distance={60} |
|
|
|
|
decay={1} |
|
|
|
|
color="#ffff00" |
|
|
|
|
castShadow |
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
<SpotLight |
|
|
|
|
ref={spot5Ref} |
|
|
|
|
position={[10, 12, -10]} |
|
|
|
|
angle={Math.PI / 7} |
|
|
|
|
penumbra={0.5} |
|
|
|
|
intensity={250} |
|
|
|
|
distance={60} |
|
|
|
|
decay={1} |
|
|
|
|
color="#ff00ff" |
|
|
|
|
castShadow |
|
|
|
|
/> |
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export default Scene; |