diff --git a/src/app/gym-3d/page.tsx b/src/app/gym-3d/page.tsx index a73f830..04ee4cd 100644 --- a/src/app/gym-3d/page.tsx +++ b/src/app/gym-3d/page.tsx @@ -1,181 +1,254 @@ "use client"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from 'react'; +import * as THREE from 'three'; import NavbarLayoutFloatingOverlay from '@/components/navbar/NavbarLayoutFloatingOverlay/NavbarLayoutFloatingOverlay'; import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider"; -import { Mail, Send } from 'lucide-react'; -import * as THREE from "three"; -import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; +import { ChevronDown, Mail, Phone, MapPin } from 'lucide-react'; -export default function Gym3D() { - const canvasRef = useRef(null); - const [formData, setFormData] = useState({ name: "", email: "", message: "" }); +interface FormData { + name: string; + email: string; + phone: string; + message: string; +} + +export default function Gym3DPage() { + const mountRef = useRef(null); + const sceneRef = useRef(null); + const rendererRef = useRef(null); + const [formData, setFormData] = useState({ + name: '', + email: '', + phone: '', + message: '' + }); const [submitted, setSubmitted] = useState(false); useEffect(() => { - const loadThree = async () => { - if (!canvasRef.current) return; + if (!mountRef.current) return; - const scene = new THREE.Scene(); - scene.background = new THREE.Color(0x0a0a0a); - scene.fog = new THREE.Fog(0x0a0a0a, 50, 200); + // Scene setup + const scene = new THREE.Scene(); + scene.background = new THREE.Color(0x0a0a0a); + sceneRef.current = scene; - const camera = new THREE.PerspectiveCamera( - 75, - canvasRef.current.clientWidth / canvasRef.current.clientHeight, - 0.1, - 1000 + // Camera + const camera = new THREE.PerspectiveCamera( + 75, + mountRef.current.clientWidth / mountRef.current.clientHeight, + 0.1, + 1000 + ); + camera.position.set(0, 5, 15); + camera.lookAt(0, 5, 0); + + // Renderer + const renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(mountRef.current.clientWidth, mountRef.current.clientHeight); + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFShadowShadowMap; + mountRef.current.appendChild(renderer.domElement); + rendererRef.current = renderer; + + // Lighting + const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); + scene.add(ambientLight); + + const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); + directionalLight.position.set(10, 20, 10); + directionalLight.castShadow = true; + directionalLight.shadow.mapSize.width = 2048; + directionalLight.shadow.mapSize.height = 2048; + scene.add(directionalLight); + + const pointLight = new THREE.PointLight(0xffff00, 0.5); + pointLight.position.set(0, 10, 0); + scene.add(pointLight); + + // Floor + const floorGeometry = new THREE.PlaneGeometry(50, 50); + const floorMaterial = new THREE.MeshStandardMaterial({ + color: 0x1a1a1a, + roughness: 0.8, + metalness: 0.1 + }); + const floor = new THREE.Mesh(floorGeometry, floorMaterial); + floor.rotation.x = -Math.PI / 2; + floor.castShadow = true; + floor.receiveShadow = true; + scene.add(floor); + + // Walls + const wallMaterial = new THREE.MeshStandardMaterial({ + color: 0x2a2a2a, + roughness: 0.7, + metalness: 0.05 + }); + + // Back wall + const backWallGeometry = new THREE.BoxGeometry(50, 15, 1); + const backWall = new THREE.Mesh(backWallGeometry, wallMaterial); + backWall.position.set(0, 7.5, -25); + backWall.castShadow = true; + backWall.receiveShadow = true; + scene.add(backWall); + + // Side walls + const sideWallGeometry = new THREE.BoxGeometry(1, 15, 50); + const leftWall = new THREE.Mesh(sideWallGeometry, wallMaterial); + leftWall.position.set(-25, 7.5, 0); + leftWall.castShadow = true; + leftWall.receiveShadow = true; + scene.add(leftWall); + + const rightWall = new THREE.Mesh(sideWallGeometry, wallMaterial); + rightWall.position.set(25, 7.5, 0); + rightWall.castShadow = true; + rightWall.receiveShadow = true; + scene.add(rightWall); + + // Equipment - Barbells/Rack + const rackGeometry = new THREE.BoxGeometry(8, 2, 0.5); + const rackMaterial = new THREE.MeshStandardMaterial({ + color: 0x333333, + roughness: 0.3, + metalness: 0.8 + }); + const rack = new THREE.Mesh(rackGeometry, rackMaterial); + rack.position.set(-10, 1, 0); + rack.castShadow = true; + rack.receiveShadow = true; + scene.add(rack); + + // Dumbbells + for (let i = 0; i < 5; i++) { + const dumbbell = new THREE.Mesh( + new THREE.CylinderGeometry(0.3, 0.3, 0.8, 16), + new THREE.MeshStandardMaterial({ + color: 0xff6600, + roughness: 0.2, + metalness: 0.9 + }) ); - camera.position.set(20, 15, 20); + dumbbell.position.set(-15 + i * 2, 0.5, -5); + dumbbell.castShadow = true; + dumbbell.receiveShadow = true; + scene.add(dumbbell); + } - const renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setSize(canvasRef.current.clientWidth, canvasRef.current.clientHeight); - renderer.shadowMap.enabled = true; - canvasRef.current.appendChild(renderer.domElement); + // Rings (suspension trainer) + const ringGeometry = new THREE.TorusGeometry(0.3, 0.05, 16, 16); + const ringMaterial = new THREE.MeshStandardMaterial({ + color: 0xffff00, + roughness: 0.3, + metalness: 0.8 + }); + for (let i = 0; i < 2; i++) { + const ring = new THREE.Mesh(ringGeometry, ringMaterial); + ring.position.set(-5 + i * 10, 8, -10); + ring.castShadow = true; + ring.receiveShadow = true; + scene.add(ring); + } - // Lighting - const ambientLight = new THREE.AmbientLight(0xffff00, 0.6); - scene.add(ambientLight); + // Climbing wall + const climbingWallGeometry = new THREE.BoxGeometry(8, 10, 0.3); + const climbingWallMaterial = new THREE.MeshStandardMaterial({ + color: 0x444444, + roughness: 0.8, + metalness: 0.1 + }); + const climbingWall = new THREE.Mesh(climbingWallGeometry, climbingWallMaterial); + climbingWall.position.set(10, 5, -22); + climbingWall.castShadow = true; + climbingWall.receiveShadow = true; + scene.add(climbingWall); - const directionalLight = new THREE.DirectionalLight(0xffff00, 0.8); - directionalLight.position.set(30, 30, 20); - directionalLight.castShadow = true; - directionalLight.shadow.mapSize.width = 2048; - directionalLight.shadow.mapSize.height = 2048; - scene.add(directionalLight); - - // Floor - const floorGeometry = new THREE.PlaneGeometry(50, 50); - const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x1a1a1a }); - const floor = new THREE.Mesh(floorGeometry, floorMaterial); - floor.rotation.x = -Math.PI / 2; - floor.receiveShadow = true; - scene.add(floor); - - // Gym Equipment - Barbells - const barlbellGeometry = new THREE.CylinderGeometry(0.2, 0.2, 2, 16); - const barMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00, metalness: 0.8, roughness: 0.2 }); - const barbell1 = new THREE.Mesh(barlbellGeometry, barMaterial); - barbell1.position.set(-10, 1, 0); - barbell1.rotation.z = Math.PI / 2; - barbell1.castShadow = true; - scene.add(barbell1); - - const barbell2 = new THREE.Mesh(barlbellGeometry, barMaterial); - barbell2.position.set(-5, 1, 0); - barbell2.rotation.z = Math.PI / 2; - barbell2.castShadow = true; - scene.add(barbell2); - - const barbell3 = new THREE.Mesh(barlbellGeometry, barMaterial); - barbell3.position.set(0, 1, 0); - barbell3.rotation.z = Math.PI / 2; - barbell3.castShadow = true; - scene.add(barbell3); - - // Dumbbells - const dumbellBallGeometry = new THREE.SphereGeometry(0.3, 16, 16); - const dumbellMaterial = new THREE.MeshStandardMaterial({ color: 0x1a1a1a, metalness: 0.6 }); - for (let i = 0; i < 6; i++) { - const dumbbell1 = new THREE.Mesh(dumbellBallGeometry, dumbellMaterial); - dumbbell1.position.set(-15 + i * 3, 0.5, -5); - dumbbell1.castShadow = true; - scene.add(dumbbell1); - - const dumbbell2 = new THREE.Mesh(dumbellBallGeometry, dumbellMaterial); - dumbbell2.position.set(-15 + i * 3, 0.5, 5); - dumbbell2.castShadow = true; - scene.add(dumbbell2); - } - - // Pull-up Bar / Rig - const rigFrameGeometry = new THREE.BoxGeometry(15, 0.3, 0.3); - const rigMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00, metalness: 0.7 }); - const rigBar = new THREE.Mesh(rigFrameGeometry, rigMaterial); - rigBar.position.set(0, 10, -8); - rigBar.castShadow = true; - scene.add(rigBar); - - // Supports for rig - const supportGeometry = new THREE.CylinderGeometry(0.2, 0.2, 10, 16); - const support1 = new THREE.Mesh(supportGeometry, rigMaterial); - support1.position.set(-7, 5, -8); - support1.castShadow = true; - scene.add(support1); - - const support2 = new THREE.Mesh(supportGeometry, rigMaterial); - support2.position.set(7, 5, -8); - support2.castShadow = true; - scene.add(support2); - - // Rowing Machine - const rowingBaseGeometry = new THREE.BoxGeometry(2, 0.5, 1); - const rowingMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 }); - const rowingBase = new THREE.Mesh(rowingBaseGeometry, rowingMaterial); - rowingBase.position.set(10, 0.25, 0); - rowingBase.castShadow = true; - scene.add(rowingBase); - - const rowingSeatGeometry = new THREE.BoxGeometry(0.5, 1, 0.8); - const rowingSeat = new THREE.Mesh(rowingSeatGeometry, rowingMaterial); - rowingSeat.position.set(10, 1.3, 0); - rowingSeat.castShadow = true; - scene.add(rowingSeat); - - // Gym Walls - const wallGeometry = new THREE.BoxGeometry(50, 15, 0.5); - const wallMaterial = new THREE.MeshStandardMaterial({ color: 0x2a2a2a }); - - const backWall = new THREE.Mesh(wallGeometry, wallMaterial); - backWall.position.set(0, 7.5, -25); - scene.add(backWall); - - const sideWall = new THREE.Mesh( - new THREE.BoxGeometry(0.5, 15, 50), - wallMaterial + // Hold indicators on climbing wall + for (let i = 0; i < 6; i++) { + const hold = new THREE.Mesh( + new THREE.SphereGeometry(0.3, 16, 16), + new THREE.MeshStandardMaterial({ + color: 0xff3333, + roughness: 0.4, + metalness: 0.6 + }) ); - sideWall.position.set(-25, 7.5, 0); - scene.add(sideWall); + hold.position.set( + 10 - 2 + (i % 3) * 2, + 5 + Math.floor(i / 3) * 3, + -21.5 + ); + hold.castShadow = true; + hold.receiveShadow = true; + scene.add(hold); + } - // Controls - const controls = new OrbitControls(camera, renderer.domElement); - controls.enableDamping = true; - controls.dampingFactor = 0.05; - controls.autoRotate = true; - controls.autoRotateSpeed = 2; + // Treadmill/Cardio station + const treadmillGeometry = new THREE.BoxGeometry(1.5, 1, 2); + const treadmillMaterial = new THREE.MeshStandardMaterial({ + color: 0x1a1a1a, + roughness: 0.6, + metalness: 0.4 + }); + const treadmill = new THREE.Mesh(treadmillGeometry, treadmillMaterial); + treadmill.position.set(15, 0.5, 0); + treadmill.castShadow = true; + treadmill.receiveShadow = true; + scene.add(treadmill); - const animate = () => { - requestAnimationFrame(animate); - controls.update(); - renderer.render(scene, camera); - }; - animate(); + // Animation + let animationId: number; + const animate = () => { + animationId = requestAnimationFrame(animate); - // Handle resize - const handleResize = () => { - if (!canvasRef.current) return; - const width = canvasRef.current.clientWidth; - const height = canvasRef.current.clientHeight; - camera.aspect = width / height; - camera.updateProjectionMatrix(); - renderer.setSize(width, height); - }; - window.addEventListener("resize", handleResize); + // Rotate the camera around the scene + const angle = Date.now() * 0.0001; + camera.position.x = Math.sin(angle) * 20; + camera.position.z = Math.cos(angle) * 20; + camera.lookAt(0, 5, 0); - return () => { - window.removeEventListener("resize", handleResize); - canvasRef.current?.removeChild(renderer.domElement); - }; + renderer.render(scene, camera); + }; + animate(); + + // Handle resize + const handleResize = () => { + if (!mountRef.current) return; + const width = mountRef.current.clientWidth; + const height = mountRef.current.clientHeight; + camera.aspect = width / height; + camera.updateProjectionMatrix(); + renderer.setSize(width, height); }; - loadThree(); + window.addEventListener('resize', handleResize); + + return () => { + window.removeEventListener('resize', handleResize); + cancelAnimationFrame(animationId); + mountRef.current?.removeChild(renderer.domElement); + }; }, []); + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); + // Handle form submission + console.log('Form submitted:', formData); setSubmitted(true); - setFormData({ name: "", email: "", message: "" }); - setTimeout(() => setSubmitted(false), 3000); + setTimeout(() => { + setSubmitted(false); + setFormData({ name: '', email: '', phone: '', message: '' }); + }, 3000); }; return ( @@ -195,112 +268,117 @@ export default function Gym3D() { -
-
-
-

Tour Our 3D Gym

-

Explore Iron Pulse's state-of-the-art facility from every angle

-
+
+ {/* 3D Gym Visualization */} +
-
- -
+ {/* Contact Form */} +
+
+

Contact Us

+

Interested in our 3D gym experience or want to learn more? Get in touch!

-
-
-

Gym Features

-
    -
  • - - Olympic Barbells & Competition Platforms -
  • -
  • - - Complete Gymnastics Rig with Rings & Pull-up Bars -
  • -
  • - - Full Set of Competition Dumbbells (5 lbs to 150 lbs) -
  • -
  • - - Cardio Equipment: Rowing Machines & Bikes -
  • -
  • - - Strength Training: Power Racks & Benches -
  • -
  • - - Climate Controlled & Fully Equipped Locker Rooms -
  • -
-
+ {submitted ? ( +
+

✓ Thank you for reaching out!

+

We'll get back to you soon.

+
+ ) : ( +
+
+
+ + +
+
+ + +
+
-
-

- - Contact Us -

-
- + setFormData({ ...formData, name: e.target.value })} - className="w-full bg-black/50 border border-yellow-400/30 rounded px-4 py-2 text-white placeholder-gray-500 focus:outline-none focus:border-yellow-400 transition" - placeholder="Your name" - required - /> -
-
- - setFormData({ ...formData, email: e.target.value })} - className="w-full bg-black/50 border border-yellow-400/30 rounded px-4 py-2 text-white placeholder-gray-500 focus:outline-none focus:border-yellow-400 transition" - placeholder="your@email.com" - required + type="tel" + name="phone" + value={formData.phone} + onChange={handleInputChange} + className="w-full bg-gray-800 border border-yellow-400/20 rounded-lg px-4 py-3 text-white focus:outline-none focus:border-yellow-400 transition" + placeholder="+1 (555) 000-0000" />
+