diff --git a/package.json b/package.json
index 4f43362..8da4a06 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.7",
"@tanstack/react-table": "^8.21.3",
+ "@types/three": "^0.178.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"drizzle-orm": "^0.44.2",
@@ -34,6 +35,7 @@
"react-dom": "^19.1.0",
"react-icons": "^5.5.0",
"tailwind-merge": "^3.3.1",
+ "three": "^0.178.0",
"tw-animate-css": "^1.3.4"
},
"devDependencies": {
diff --git a/src/app/messiala/page.tsx b/src/app/messiala/page.tsx
index 23ecc61..d322c5d 100644
--- a/src/app/messiala/page.tsx
+++ b/src/app/messiala/page.tsx
@@ -1,10 +1,366 @@
+"use client";
+
import { vipnagorgialla } from "@/components/Vipnagorgialla";
+import * as THREE from "three";
+import { useEffect, useRef, useState } from "react";
export default function Expo() {
- return (
-
-
Messiala
-
Koostööpartneritega arutamisel. Rohkem infot teel!
-
+ const mountRef = useRef(null);
+ const [hoveredRoom, setHoveredRoom] = useState(null);
+ const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
+
+ useEffect(() => {
+ if (!mountRef.current) return;
+
+ // Scene setup
+ const scene = new THREE.Scene();
+ scene.background = new THREE.Color(0x0e0f19);
+
+ // Isometric camera setup
+ const aspect = 800 / 600;
+ const frustumSize = 14;
+ const camera = new THREE.OrthographicCamera(
+ (frustumSize * aspect) / -2,
+ (frustumSize * aspect) / 2,
+ frustumSize / 2,
+ frustumSize / -2,
+ 1,
+ 1000,
);
+
+ // Position camera for isometric view
+ camera.position.set(10, 10, 14);
+ camera.lookAt(-1.4, 0, 0);
+
+ // Renderer
+ const renderer = new THREE.WebGLRenderer({ antialias: true });
+ renderer.setSize(800, 600);
+ renderer.shadowMap.enabled = true;
+ renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+ mountRef.current.appendChild(renderer.domElement);
+
+ // Raycaster for mouse interactions
+ const raycaster = new THREE.Raycaster();
+ const mouse = new THREE.Vector2();
+
+ // Lighting
+ const ambientLight = new THREE.AmbientLight(0x404040, 1.2);
+ scene.add(ambientLight);
+
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
+ directionalLight.position.set(10, 10, 5);
+ directionalLight.castShadow = false;
+ directionalLight.shadow.mapSize.width = 2048;
+ directionalLight.shadow.mapSize.height = 2048;
+ scene.add(directionalLight);
+
+ // Room colors and names
+ const roomColors = [
+ 0xff6b35, // Orange - Mänguklubi
+ 0x4ecdc4, // Turquoise - Baariala
+ 0xffe66d, // Yellow - EVAL
+ 0xe74c3c, // Red - Redbull
+ 0x9b59b6, // Purple - Võitlusmängu ala
+ 0x3498db, // Blue - Sony
+ 0x2ecc71, // Green - Chillimisala
+ ];
+
+ const roomNames = [
+ "Mänguklubi",
+ "Baariala",
+ "EVAL",
+ "Redbull",
+ "Võitlusmängu ala",
+ "Sony",
+ "Chillimisala",
+ ];
+
+ // Create individual rooms as rectangles with custom positions
+ const rooms: THREE.Mesh[] = [];
+ const roomData: Array<{
+ mesh: THREE.Mesh;
+ name: string;
+ originalColor: number;
+ originalScale: THREE.Vector3;
+ }> = [];
+
+ // Define rooms with custom positions, sizes and colors
+ const roomDefinitions = [
+ {
+ width: 7,
+ height: 0.7,
+ depth: 3,
+ x: 2.5,
+ z: 4,
+ color: roomColors[0],
+ name: roomNames[0],
+ }, // Mänguklubi
+ {
+ width: 2.5,
+ height: 0.7,
+ depth: 0.7,
+ x: 1,
+ z: 0,
+ color: roomColors[1],
+ name: roomNames[1],
+ }, // Baariala
+ {
+ width: 1.8,
+ height: 0.7,
+ depth: 1.5,
+ x: 2.5,
+ z: -3.5,
+ color: roomColors[2],
+ name: roomNames[2],
+ }, // EVAL
+ {
+ width: 2,
+ height: 0.7,
+ depth: 4,
+ x: 5,
+ z: -1.7,
+ color: roomColors[3],
+ name: roomNames[3],
+ }, // Redbull
+ {
+ width: 3,
+ height: 0.7,
+ depth: 1.3,
+ x: 0,
+ z: -3.5,
+ color: roomColors[4],
+ name: roomNames[4],
+ }, // Võitlusmängu ala
+ {
+ width: 1.8,
+ height: 0.7,
+ depth: 1.5,
+ x: -2.55,
+ z: -3.5,
+ color: roomColors[5],
+ name: roomNames[5],
+ }, // Sony
+ {
+ width: 4,
+ height: 0.7,
+ depth: 4,
+ x: -5.5,
+ z: -2.3,
+ color: roomColors[6],
+ name: roomNames[6],
+ }, // Chillimisala
+ ];
+
+ roomDefinitions.forEach((roomDef) => {
+ const geometry = new THREE.BoxGeometry(
+ roomDef.width,
+ roomDef.height,
+ roomDef.depth,
+ );
+ const material = new THREE.MeshLambertMaterial({
+ color: roomDef.color,
+ });
+
+ const room = new THREE.Mesh(geometry, material);
+ room.position.set(roomDef.x, roomDef.height / 2, roomDef.z);
+ room.castShadow = true;
+ room.receiveShadow = true;
+ room.userData = { name: roomDef.name, originalColor: roomDef.color };
+
+ scene.add(room);
+ rooms.push(room);
+ roomData.push({
+ mesh: room,
+ name: roomDef.name,
+ originalColor: roomDef.color,
+ originalScale: room.scale.clone(),
+ });
+ });
+
+ // Ground plane
+ const groundGeometry = new THREE.PlaneGeometry(14, 10.5);
+ const groundMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
+ const ground = new THREE.Mesh(groundGeometry, groundMaterial);
+ ground.rotation.x = -Math.PI / 2;
+ ground.position.x = -1.1;
+ ground.position.y = -0.5;
+ ground.receiveShadow = true;
+ scene.add(ground);
+
+ // Second ground plane
+ const groundGeometry2 = new THREE.PlaneGeometry(2, 7);
+ const groundMaterial2 = new THREE.MeshLambertMaterial({ color: 0xcccccc });
+ const ground2 = new THREE.Mesh(groundGeometry2, groundMaterial2);
+ ground2.rotation.x = -Math.PI / 2;
+ ground2.position.x = -12.2;
+ ground2.position.y = -5;
+ ground2.receiveShadow = true;
+ scene.add(ground2);
+
+ // Mouse event handlers
+ const onMouseMove = (event: MouseEvent) => {
+ const rect = renderer.domElement.getBoundingClientRect();
+ mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
+ mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
+
+ // Update mouse position for tooltip
+ setMousePosition({ x: event.clientX, y: event.clientY });
+
+ // Update raycaster
+ raycaster.setFromCamera(mouse, camera);
+ const intersects = raycaster.intersectObjects(rooms);
+
+ // Reset all rooms to original state
+ roomData.forEach(({ mesh, originalColor, originalScale }) => {
+ (mesh.material as THREE.MeshLambertMaterial).color.setHex(
+ originalColor,
+ );
+ mesh.scale.copy(originalScale);
+ });
+
+ if (intersects.length > 0) {
+ const hoveredMesh = intersects[0].object as THREE.Mesh;
+ const roomInfo = roomData.find((r) => r.mesh === hoveredMesh);
+
+ if (roomInfo) {
+ // Apply hover effects
+ (hoveredMesh.material as THREE.MeshLambertMaterial).color.setHex(
+ 0xffffff,
+ );
+ hoveredMesh.scale.multiplyScalar(1.02);
+ setHoveredRoom(roomInfo.name);
+ }
+ } else {
+ setHoveredRoom(null);
+ }
+ };
+
+ // Add mouse event listener
+ renderer.domElement.addEventListener("mousemove", onMouseMove);
+
+ // Animation loop
+ const animate = () => {
+ requestAnimationFrame(animate);
+
+ // Gentle floating animation for rooms
+ rooms.forEach((room, index) => {
+ const originalY = 0.25; // height / 2 for the room height of 0.5
+ const baseY = originalY + Math.sin(Date.now() * 0.001 + index) * 0.05;
+
+ // Maintain current scale while updating Y position
+ room.position.y = baseY;
+ });
+
+ renderer.render(scene, camera);
+ };
+
+ animate();
+
+ // Cleanup
+ return () => {
+ renderer.domElement.removeEventListener("mousemove", onMouseMove);
+ if (mountRef.current && renderer.domElement) {
+ mountRef.current.removeChild(renderer.domElement);
+ }
+ renderer.dispose();
+ };
+ }, []);
+
+ return (
+
+
+ Messiala
+
+
+
+ Tudengimaja
+
+
+
+
+
+
+
+
+
+ Võitlusmängu ala
+
+
+
+
+
+
+
+
+
+ {/* Tooltip */}
+ {hoveredRoom && (
+
+ {hoveredRoom}
+
+ )}
+
+
+ );
}